/*
 *	$Source: /mit/kerberos/src/appl/bsd/RCS/rsh.c,v $
 *	$Header: rsh.c,v 4.11 89/01/17 15:43:09 rfrench Exp $
 */

#ifndef lint
static char *rcsid_rsh_c = 
"$Header: rsh.c,v 4.11 89/01/17 15:43:09 rfrench Exp $";
#endif lint

/*
 * Copyright (c) 1983 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 The Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)rsh.c	5.7 (Berkeley) 9/20/88";
#endif /* not lint */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>

#include <netinet/in.h>

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <pwd.h>
#include <netdb.h>

#ifdef KERBEROS
#include <krb.h>
#endif KERBEROS

/*
 * rsh - remote shell
 */
/* VARARGS */
int	error();
char	*index(), *rindex(), *malloc(), *getpass(), *strcpy();

struct	passwd *getpwuid();

int	errno;
int	options;
int	rfd2;
int	nflag;
int	sendsig();
#ifdef KERBEROS
char	krb_realm[REALM_SZ];
#define	UCB_RSH	"/usr/ucb/rsh.ucb"
void	try_normal();
#endif KERBEROS

#define	mask(s)	(1 << ((s) - 1))

main(argc, argv0)
	int argc;
	char **argv0;
{
	int rem, pid;
	char *host, *cp, **ap, buf[BUFSIZ], *args, **argv = argv0, *user = 0;
	register int cc;
	int asrsh = 0;
	struct passwd *pwd;
	int readfrom, ready;
	int one = 1;
	struct servent *sp;
	int omask;
#ifdef KERBEROS
	KTEXT_ST ticket;
	int sock;
	long authopts;
#endif KERBEROS

	host = rindex(argv[0], '/');
	if (host)
		host++;
	else
		host = argv[0];
	argv++, --argc;
	if (!strcmp(host, "rsh")) {
		host = *argv++, --argc;
		asrsh = 1;
	}
another:
	if (argc > 0 && !strcmp(*argv, "-l")) {
		argv++, argc--;
		if (argc > 0)
			user = *argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-n")) {
		argv++, argc--;
		nflag++;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-d")) {
		argv++, argc--;
		options |= SO_DEBUG;
		goto another;
	}
#ifdef KERBEROS
	if (argc > 0 && !strcmp(*argv, "-k")) {
	  	argv++, argc--;
		if (argc == 0) {
		  fprintf(stderr, "rsh(kerberos): -k flag must have a realm after it.\n");
		  exit (1);
		}
		strncpy(krb_realm,*argv,REALM_SZ);
		argv++, argc--;
		goto another;
	}
	/*
	 * Ignore -x from kerberos rlogin
	 */
	if (argc > 0 && !strncmp(*argv, "-x", 2)) {
		argv++, argc--;
		goto another;
	}

#endif KERBEROS
	/*
	 * Ignore the -L, -w, -e and -8 flags to allow aliases with rlogin
	 * to work
	 *
	 * There must be a better way to do this! -jmb
	 */
	if (argc > 0 && !strncmp(*argv, "-L", 2)) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strncmp(*argv, "-w", 2)) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strncmp(*argv, "-8", 2)) {
		argv++, argc--;
		goto another;
	}
#ifdef ATHENA
	/* additional Athena flags to be ignored */
	if (argc > 0 && !strcmp(*argv, "-noflow")) {	/* No local flow control option for rlogin */
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-7")) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-c")) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-a")) {
		argv++, argc--;
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-n")) {
		argv++, argc--;
		goto another;
	}
		/*
		** Also ignore -t ttytype
		*/
	if (argc > 0 && !strcmp(*argv, "-t")) {
		argv++; argv++; argc--; argc--;
		goto another;
	}
#endif ATHENA
	if (host == 0)
		goto usage;
	if (argv[0] == 0) {
		if (asrsh)
			*argv0 = "rlogin";
		execv("/usr/ucb/rlogin", argv0);
		perror("/usr/ucb/rlogin");
		exit(1);
	}
	pwd = getpwuid(getuid());
	if (pwd == 0) {
		fprintf(stderr, "who are you?\n");
		exit(1);
	}
	cc = 0;
	for (ap = argv; *ap; ap++)
		cc += strlen(*ap) + 1;
	cp = args = malloc(cc);
	for (ap = argv; *ap; ap++) {
		(void) strcpy(cp, *ap);
		while (*cp)
			cp++;
		if (ap[1])
			*cp++ = ' ';
	}
#ifdef KERBEROS
	sp = getservbyname("kshell", "tcp");
#else 
	sp = getservbyname("shell", "tcp");
#endif KERBEROS
	if (sp == 0) {
#ifdef KERBEROS
		fprintf(stderr, "rsh: kshell/tcp: unknown service\n");
		try_normal(argv0);
#else 
		fprintf(stderr, "rsh: shell/tcp: unknown service\n");
#endif KERBEROS
		exit(1);
	}
#ifdef KERBEROS
	rem=KSUCCESS;
#ifdef ATHENA_COMPAT
	authopts = KOPT_DO_OLDSTYLE;
#else
	authopts = 0L;
#endif ATHENA_COMPAT
		
	rem = kcmd(&sock, &host, sp->s_port,
		   pwd->pw_name,
		   user ? user : pwd->pw_name,
		   args, &rfd2, &ticket, "rcmd", krb_realm,
		   (CREDENTIALS *)0, (bit_64 *)0,
		   (MSG_DAT *)0,
		   (struct sockaddr_in *) 0,
		   (struct sockaddr_in *) 0,
		   authopts);
	if (rem != KSUCCESS) {
	    if (rem == KDC_PR_UNKNOWN) /* assume the foreign principal
					  isn't registered */
		fprintf(stderr, "%s: Host %s isn't registered for Kerberos rsh service\n",
			argv0[0], host);
	    else
		fprintf(stderr,
			"%s: Kerberos rcmd failed: %s.\n",
			argv0[0],
			((rem == -1) ? "rcmd protocol failure":
			 krb_err_txt[rem]));
	    rem = -1;
	}
	if (rem<0) {
		try_normal(argv0);
	}
	rem = sock;
#else !KERBEROS
        rem = rcmd(&host, sp->s_port, pwd->pw_name,
	    user ? user : pwd->pw_name, args, &rfd2);
        if (rem < 0)
                exit(1);
#endif KERBEROS
	if (rfd2 < 0) {
		fprintf(stderr, "rsh: can't establish stderr\n");
		exit(2);
	}
	if (options & SO_DEBUG) {
		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof (one)) < 0)
			perror("setsockopt (stdin)");
		if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, sizeof (one)) < 0)
			perror("setsockopt (stderr)");
	}
	(void) setuid(getuid());
	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, sendsig);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, sendsig);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, sendsig);
	if (nflag == 0) {
		pid = fork();
		if (pid < 0) {
			perror("fork");
			exit(1);
		}
	}
	ioctl(rfd2, FIONBIO, &one);
	ioctl(rem, FIONBIO, &one);
        if (nflag == 0 && pid == 0) {
		char *bp; int rembits, wc;
		(void) close(rfd2);
	reread:
		errno = 0;
		cc = read(0, buf, sizeof buf);
		if (cc <= 0)
			goto done;
		bp = buf;
	rewrite:
		rembits = 1<<rem;
		if (select(16, 0, &rembits, 0, 0) < 0) {
			if (errno != EINTR) {
				perror("select");
				exit(1);
			}
			goto rewrite;
		}
		if ((rembits & (1<<rem)) == 0)
			goto rewrite;
		wc = write(rem, bp, cc);
		if (wc < 0) {
			if (errno == EWOULDBLOCK)
				goto rewrite;
			goto done;
		}
		cc -= wc; bp += wc;
		if (cc == 0)
			goto reread;
		goto rewrite;
	done:
		(void) shutdown(rem, 1);
		exit(0);
	}
	sigsetmask(omask);
	readfrom = (1<<rfd2) | (1<<rem);
	do {
		ready = readfrom;
		if (select(16, &ready, 0, 0, 0) < 0) {
			if (errno != EINTR) {
				perror("select");
				exit(1);
			}
			continue;
		}
		if (ready & (1<<rfd2)) {
			errno = 0;
			cc = read(rfd2, buf, sizeof buf);
			if (cc <= 0) {
				if (errno != EWOULDBLOCK)
					readfrom &= ~(1<<rfd2);
			} else
				(void) write(2, buf, cc);
		}
		if (ready & (1<<rem)) {
			errno = 0;
			cc = read(rem, buf, sizeof buf);
			if (cc <= 0) {
				if (errno != EWOULDBLOCK)
					readfrom &= ~(1<<rem);
			} else
				(void) write(1, buf, cc);
		}
        } while (readfrom);
	if (nflag == 0)
		(void) kill(pid, SIGKILL);
	exit(0);
usage:
	fprintf(stderr,
	    "usage: rsh host [ -l login ] [ -n ] command\n");
	exit(1);
}

sendsig(signo)
	char signo;
{

	(void) write(rfd2, &signo, 1);
}
#ifdef KERBEROS
void
try_normal(argv)
char **argv;
{
	char *host;

	/*
	 * if we were invoked as 'rsh host mumble', strip off the rsh
	 * from arglist.
	 *
	 * We always want to call the Berkeley rsh as 'host mumble'
	 */

	host = rindex(argv[0], '/');
	if (host)
		host++;
	else
		host = argv[0];

	if (!strcmp(host, "rsh"))
		argv++;

	fprintf(stderr,"trying normal rsh (%s)\n",
		UCB_RSH);
	fflush(stderr);
	execv(UCB_RSH, argv);
	perror("exec");
	exit(1);
}
#endif KERBEROS
