/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:rsh.c 12.0$ */
/* $ACIS:rsh.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/RCS/rsh.c,v $ */

#ifndef lint
static char *rcsid = "$Header:rsh.c 12.0$";
#endif

/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

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

#ifndef lint
static char sccsid[] = "@(#)rsh.c	5.4 (Berkeley) 8/28/85";
#endif not lint

#ifdef	VICE
#include <r/xdr.h>
#else		VICE
#include <sys/types.h>
#endif	VICE
#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	VICE
#include <rauth.h>
#endif	VICE

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

struct	passwd *getpwuid();

int	errno;
int	options;
int	rfd2;
int	sendsig();
int	lostconn();
int	pid;
int	inwrite;	/* used to determine if a "lost connection" msg is printed */

#define	mask(s)	(1 << ((s) - 1))
#ifdef	VICE
int	viceii = 0;
#endif	VICE

main(argc, argv0)
	int argc;
	char **argv0;
{
	int rem;
	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	VICE
	SecretToken sToken;
	ClearToken  cToken;
#endif	VICE
	char *progname = argv[0];

#ifdef	VICE
	{ char *cp, *getenv();
		if ((cp = getenv("AUTH")) != NULL && strcmp(cp,"viceii") == 0)
			viceii++;
	}
#endif	VICE
	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--;
		(void) close(0);
		(void) open("/dev/null", 0);
		goto another;
	}
	if (argc > 0 && !strcmp(*argv, "-d")) {
		argv++, argc--;
		options |= SO_DEBUG;
		goto another;
	}
#ifdef	VICE
	if (argc > 0 && !strcmp(*argv, "-v")) {
		viceii++;
		argv++, argc--;
		goto another;
	}
#endif	VICE
	/*
	 * 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;
	}
	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	VICE
	if (viceii) {
		sp = getservbyname("vshell", "tcp");
		if (sp == 0) {
			fprintf(stderr, "rsh: vshell/tcp: unknown service\n");
			exit(2);
		}
	} else {
		sp = getservbyname("shell", "tcp");
		if (sp == 0) {
			fprintf(stderr, "rsh: shell/tcp: unknown service\n");
			exit(2);
		}
	}
#else	VICE
	sp = getservbyname("shell", "tcp");
	if (sp == 0) {
		fprintf(stderr, "rsh: shell/tcp: unknown service\n");
		exit(1);
	}
#endif	VICE
        rem = rcmd(&host, sp->s_port, pwd->pw_name,
	    user ? user : pwd->pw_name, args, &rfd2);
        if (rem < 0)
                exit(1);
	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());
#ifdef	VICE
	/* this is shakey since ANYONE watching could grab it */
	if (viceii) { int rc;
		U_InitRPC();
		if (rc = U_GetLocalTokens(&cToken, &sToken)) {
			fprintf(stderr, "rsh: %s\n",U_Error(rc));
			exit(2);
		}
		U_HostToNetClearToken(&cToken);
		alarm(120);
		if (write(rem,&cToken,sizeof(cToken)) != sizeof(cToken)) {
			perror("rsh: write cToken");
			exit(3);
		}
		if (write(rem,&sToken,sizeof(sToken)) != sizeof(sToken)) {
			perror("rsh: write sToken");
			exit(3);
		}
		alarm(0);
	}
#endif	VICE
	(void) signal(SIGPIPE, lostconn);
	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);
        pid = fork();
        if (pid < 0) {
		perror("fork");
                exit(1);
        }
	ioctl(rfd2, FIONBIO, &one);
	ioctl(rem, FIONBIO, &one);
	/*
	 * Turn on KEEPALIVE to keep us from hanging forever
	 * if the remote host goes down.
	 *
	 * We turn it on for both sockets, since sometimes the input/output
	 * socket closes rather early (ie: if redirected from /dev/null).
	 */
	if (setsockopt(rem, SOL_SOCKET, SO_KEEPALIVE, (char *)&one,
							sizeof (one)) < 0) {
		perror("rsh setsockopt (SO_KEEPALIVE)");
		exit(1);
	}
	if (setsockopt(rfd2, SOL_SOCKET, SO_KEEPALIVE, (char *)&one,
							sizeof (one)) < 0) {
		perror("rsh stderr setsockopt (SO_KEEPALIVE)");
		exit(1);
	}
        if (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 {
				inwrite = 1;
				(void) write(2, buf, cc);
				inwrite = 0;
			}
		}
		if (ready & (1<<rem)) {
			errno = 0;
			cc = read(rem, buf, sizeof buf);
			if (cc <= 0) {
				if (errno != EWOULDBLOCK)
					readfrom &= ~(1<<rem);
			} else {
				inwrite = 1;
				(void) write(1, buf, cc);
				inwrite = 0;
			}
		}
        } while (readfrom);
        (void) kill(pid, SIGKILL);
	exit(0);
usage:
	fprintf(stderr,
	    "usage: %s host [ -l login ] [ -n ] command\n", progname);
	exit(1);
}

sendsig(signo)
	char signo;
{

	(void) write(rfd2, &signo, 1);
}

lostconn()
{
	if(inwrite == 0)
		fprintf(stderr, "rsh: lost connection\n");
        (void) kill(pid, SIGKILL);
	exit(1);
}
