xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 18358)
16444Swnj #ifndef lint
2*18358Ssam static char sccsid[] = "@(#)rlogin.c	4.17 (Berkeley) 85/03/17";
36444Swnj #endif
46444Swnj 
512990Ssam /*
612990Ssam  * rlogin - remote login
712990Ssam  */
86444Swnj #include <sys/types.h>
96444Swnj #include <sys/socket.h>
1013620Ssam #include <sys/wait.h>
119365Ssam 
129207Ssam #include <netinet/in.h>
139365Ssam 
149365Ssam #include <stdio.h>
159365Ssam #include <sgtty.h>
166444Swnj #include <errno.h>
176444Swnj #include <pwd.h>
189365Ssam #include <signal.h>
199365Ssam #include <netdb.h>
20*18358Ssam #include <setjmp.h>
216444Swnj 
226444Swnj char	*index(), *rindex(), *malloc(), *getenv();
236444Swnj struct	passwd *getpwuid();
249365Ssam char	*name;
256444Swnj int	rem;
266444Swnj char	cmdchar = '~';
276444Swnj int	eight;
286444Swnj char	*speeds[] =
296444Swnj     { "0", "50", "75", "110", "134", "150", "200", "300",
306444Swnj       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
31*18358Ssam char	term[256] = "network";
329365Ssam extern	int errno;
339365Ssam int	lostpeer();
34*18358Ssam int	nosigwin;
35*18358Ssam jmp_buf	winsizechanged;
36*18358Ssam struct	winsize winsize;
37*18358Ssam int	sigwinch();
386444Swnj 
396444Swnj main(argc, argv)
406444Swnj 	int argc;
416444Swnj 	char **argv;
426444Swnj {
439365Ssam 	char *host, *cp;
446444Swnj 	struct sgttyb ttyb;
456444Swnj 	struct passwd *pwd;
469365Ssam 	struct servent *sp;
4710415Ssam 	int uid, options = 0;
4817449Slepreau 	int on = 1;
496444Swnj 
506444Swnj 	host = rindex(argv[0], '/');
516444Swnj 	if (host)
526444Swnj 		host++;
536444Swnj 	else
546444Swnj 		host = argv[0];
556444Swnj 	argv++, --argc;
566444Swnj 	if (!strcmp(host, "rlogin"))
576444Swnj 		host = *argv++, --argc;
586444Swnj another:
5910839Ssam 	if (argc > 0 && !strcmp(*argv, "-d")) {
606444Swnj 		argv++, argc--;
6110415Ssam 		options |= SO_DEBUG;
626444Swnj 		goto another;
636444Swnj 	}
6410839Ssam 	if (argc > 0 && !strcmp(*argv, "-l")) {
656444Swnj 		argv++, argc--;
666444Swnj 		if (argc == 0)
676444Swnj 			goto usage;
686444Swnj 		name = *argv++; argc--;
696444Swnj 		goto another;
706444Swnj 	}
7110839Ssam 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
726444Swnj 		cmdchar = argv[0][2];
736444Swnj 		argv++, argc--;
746444Swnj 		goto another;
756444Swnj 	}
7610839Ssam 	if (argc > 0 && !strcmp(*argv, "-8")) {
776444Swnj 		eight = 1;
786444Swnj 		argv++, argc--;
796444Swnj 		goto another;
806444Swnj 	}
81*18358Ssam 	if (argc > 0 && !strcmp(*argv, "-w")) {
82*18358Ssam 		nosigwin++;
83*18358Ssam 		argv++, argc--;
84*18358Ssam 		goto another;
85*18358Ssam 	}
866444Swnj 	if (host == 0)
876444Swnj 		goto usage;
886444Swnj 	if (argc > 0)
896444Swnj 		goto usage;
906444Swnj 	pwd = getpwuid(getuid());
916444Swnj 	if (pwd == 0) {
926444Swnj 		fprintf(stderr, "Who are you?\n");
936444Swnj 		exit(1);
946444Swnj 	}
959365Ssam 	sp = getservbyname("login", "tcp");
969365Ssam 	if (sp == 0) {
979365Ssam 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
989365Ssam 		exit(2);
999365Ssam 	}
1009241Ssam 	cp = getenv("TERM");
1019241Ssam 	if (cp)
1029241Ssam 		strcpy(term, cp);
103*18358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
1046444Swnj 		strcat(term, "/");
1056444Swnj 		strcat(term, speeds[ttyb.sg_ospeed]);
1066444Swnj 	}
107*18358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &winsize) == 0) {
108*18358Ssam 		cp = index(term, '\0');
109*18358Ssam 		sprintf(cp, "/%u,%u,%u,%u", winsize.ws_row, winsize.ws_col,
110*18358Ssam 		    winsize.ws_xpixel, winsize.ws_ypixel);
111*18358Ssam 	}
11212990Ssam 	signal(SIGPIPE, lostpeer);
1139365Ssam         rem = rcmd(&host, sp->s_port, pwd->pw_name,
1146444Swnj 	    name ? name : pwd->pw_name, term, 0);
1156444Swnj         if (rem < 0)
1166444Swnj                 exit(1);
11710415Ssam 	if (options & SO_DEBUG &&
11817449Slepreau 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
11910415Ssam 		perror("rlogin: setsockopt (SO_DEBUG)");
1209365Ssam 	uid = getuid();
1219365Ssam 	if (setuid(uid) < 0) {
1229365Ssam 		perror("rlogin: setuid");
1239365Ssam 		exit(1);
1249365Ssam 	}
1259365Ssam 	doit();
1269365Ssam 	/*NOTREACHED*/
1276444Swnj usage:
1286444Swnj 	fprintf(stderr,
12912155Ssam 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ]\n");
1306444Swnj 	exit(1);
1316444Swnj }
1326444Swnj 
1336444Swnj #define CRLF "\r\n"
1346444Swnj 
1359365Ssam int	child;
13611803Sedward int	catchild();
1379365Ssam 
13813075Ssam int	defflags, tabflag;
13913075Ssam char	deferase, defkill;
14013075Ssam struct	tchars deftc;
14113075Ssam struct	ltchars defltc;
14213075Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
14313075Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
1446444Swnj 
1459365Ssam doit()
1466444Swnj {
1476444Swnj 	int exit();
14813075Ssam 	struct sgttyb sb;
1496444Swnj 
15013075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
15113075Ssam 	defflags = sb.sg_flags;
15212155Ssam 	tabflag = defflags & TBDELAY;
1539962Ssam 	defflags &= ECHO | CRMOD;
15413075Ssam 	deferase = sb.sg_erase;
15513075Ssam 	defkill = sb.sg_kill;
15613075Ssam 	ioctl(0, TIOCGETC, (char *)&deftc);
15713075Ssam 	notc.t_startc = deftc.t_startc;
15813075Ssam 	notc.t_stopc = deftc.t_stopc;
15913075Ssam 	ioctl(0, TIOCGLTC, (char *)&defltc);
16012990Ssam 	signal(SIGINT, exit);
16112990Ssam 	signal(SIGHUP, exit);
16212990Ssam 	signal(SIGQUIT, exit);
1639365Ssam 	child = fork();
1649365Ssam 	if (child == -1) {
1659365Ssam 		perror("rlogin: fork");
1669365Ssam 		done();
1679365Ssam 	}
16812990Ssam 	signal(SIGINT, SIG_IGN);
16911803Sedward 	mode(1);
1709365Ssam 	if (child == 0) {
1719365Ssam 		reader();
17212155Ssam 		sleep(1);
17312155Ssam 		prf("\007Connection closed.");
1746444Swnj 		exit(3);
1756444Swnj 	}
17612990Ssam 	signal(SIGCHLD, catchild);
177*18358Ssam 	if (!nosigwin)
178*18358Ssam 		signal(SIGWINCH, sigwinch);
1799365Ssam 	writer();
18012155Ssam 	prf("Closed connection.");
1816444Swnj 	done();
1826444Swnj }
1836444Swnj 
1846444Swnj done()
1856444Swnj {
1866444Swnj 
1876444Swnj 	mode(0);
1889365Ssam 	if (child > 0 && kill(child, SIGKILL) >= 0)
1899365Ssam 		wait((int *)0);
1906444Swnj 	exit(0);
1916444Swnj }
1926444Swnj 
19311803Sedward catchild()
19411803Sedward {
19511803Sedward 	union wait status;
19611803Sedward 	int pid;
19711803Sedward 
19811803Sedward again:
19911803Sedward 	pid = wait3(&status, WNOHANG|WUNTRACED, 0);
20011803Sedward 	if (pid == 0)
20111803Sedward 		return;
20211803Sedward 	/*
20311803Sedward 	 * if the child (reader) dies, just quit
20411803Sedward 	 */
20511803Sedward 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
20611803Sedward 		done();
20711803Sedward 	goto again;
20811803Sedward }
20911803Sedward 
2106444Swnj /*
2119365Ssam  * writer: write to remote: 0 -> line.
2129365Ssam  * ~.	terminate
2139365Ssam  * ~^Z	suspend rlogin process.
21410415Ssam  * ~^Y  suspend rlogin process, but leave reader alone.
2156444Swnj  */
2169365Ssam writer()
2176444Swnj {
218*18358Ssam 	char obuf[600], c;
219*18358Ssam 	register char *op;
22011803Sedward 	register n;
2216444Swnj 
222*18358Ssam 	/*
223*18358Ssam 	 * Handle SIGWINCH's with in-band signaling of new
224*18358Ssam 	 * window size.  It seems reasonable that we flush
225*18358Ssam 	 * pending input and not force out of band signal
226*18358Ssam 	 * as this most likely will occur from an input device
227*18358Ssam 	 * other than the keyboard (e.g. a mouse).
228*18358Ssam 	 *
229*18358Ssam 	 * The hack of using 0377 to signal an in-band signal
230*18358Ssam 	 * is pretty bad, but otherwise we'd be forced to
231*18358Ssam 	 * either get complicated (use MSG_OOB) or go to a
232*18358Ssam 	 * serious (telnet-style) protocol.
233*18358Ssam 	 */
234*18358Ssam 	if (setjmp(winsizechanged)) {
235*18358Ssam 		struct winsize *wp = (struct winsize *)(obuf+4);
236*18358Ssam 
237*18358Ssam 		obuf[0] = 0377;			/* XXX */
238*18358Ssam 		obuf[1] = 0377;			/* XXX */
239*18358Ssam 		obuf[2] = 's';			/* XXX */
240*18358Ssam 		obuf[3] = 's';			/* XXX */
241*18358Ssam 		wp->ws_row = htons(winsize.ws_row);
242*18358Ssam 		wp->ws_col = htons(winsize.ws_col);
243*18358Ssam 		wp->ws_xpixel = htons(winsize.ws_xpixel);
244*18358Ssam 		wp->ws_ypixel = htons(winsize.ws_ypixel);
245*18358Ssam 		(void) write(rem, obuf, 4+sizeof (*wp));
246*18358Ssam 	}
2479365Ssam top:
248*18358Ssam 	op = obuf;
24911803Sedward 	for (;;) {
2509365Ssam 		int local;
2519365Ssam 
25211803Sedward 		n = read(0, &c, 1);
253*18358Ssam 		if (n <= 0) {
254*18358Ssam 			if (n < 0 && errno == EINTR)
255*18358Ssam 				continue;
25611803Sedward 			break;
257*18358Ssam 		}
258*18358Ssam 		if (!eight)
2599365Ssam 			c &= 0177;
2609365Ssam 		/*
2619365Ssam 		 * If we're at the beginning of the line
2629365Ssam 		 * and recognize a command character, then
2639365Ssam 		 * we echo locally.  Otherwise, characters
2649365Ssam 		 * are echo'd remotely.  If the command
2659365Ssam 		 * character is doubled, this acts as a
2669365Ssam 		 * force and local echo is suppressed.
2679365Ssam 		 */
268*18358Ssam 		if (op == obuf)
2699365Ssam 			local = (c == cmdchar);
270*18358Ssam 		if (op == obuf + 1 && *obuf == cmdchar)
2719365Ssam 			local = (c != cmdchar);
2729365Ssam 		if (!local) {
2739365Ssam 			if (write(rem, &c, 1) == 0) {
2749365Ssam 				prf("line gone");
2759365Ssam 				return;
2766444Swnj 			}
277*18358Ssam 			if (!eight)
2789365Ssam 				c &= 0177;
2799365Ssam 		} else {
2809365Ssam 			if (c == '\r' || c == '\n') {
281*18358Ssam 				char cmdc = obuf[1];
2826444Swnj 
28313075Ssam 				if (cmdc == '.' || cmdc == deftc.t_eofc) {
2849365Ssam 					write(0, CRLF, sizeof(CRLF));
2859365Ssam 					return;
2869962Ssam 				}
28713075Ssam 				if (cmdc == defltc.t_suspc ||
28813075Ssam 				    cmdc == defltc.t_dsuspc) {
289*18358Ssam 					stop(cmdc);
2909365Ssam 					goto top;
2916444Swnj 				}
292*18358Ssam 				*op++ = c;
293*18358Ssam 				write(rem, obuf, op - obuf);
2949365Ssam 				goto top;
2956444Swnj 			}
2969365Ssam 			write(1, &c, 1);
2976444Swnj 		}
298*18358Ssam 		*op++ = c;
29913075Ssam 		if (c == deferase) {
300*18358Ssam 			op -= 2;
301*18358Ssam 			if (op < obuf)
3029365Ssam 				goto top;
3036444Swnj 		}
30413075Ssam 		if (c == defkill || c == deftc.t_eofc ||
3059365Ssam 		    c == '\r' || c == '\n')
3069365Ssam 			goto top;
307*18358Ssam 		if (op >= &obuf[sizeof (obuf)])
308*18358Ssam 			op--;
3096444Swnj 	}
3106444Swnj }
3116444Swnj 
312*18358Ssam stop(cmdc)
313*18358Ssam 	char cmdc;
314*18358Ssam {
315*18358Ssam 	struct winsize ws;
316*18358Ssam 
317*18358Ssam 	write(0, CRLF, sizeof(CRLF));
318*18358Ssam 	mode(0);
319*18358Ssam 	signal(SIGCHLD, SIG_IGN);
320*18358Ssam 	kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
321*18358Ssam 	signal(SIGCHLD, catchild);
322*18358Ssam 	mode(1);
323*18358Ssam 	sigwinch();			/* check for size changes */
324*18358Ssam }
325*18358Ssam 
326*18358Ssam sigwinch()
327*18358Ssam {
328*18358Ssam 	struct winsize ws;
329*18358Ssam 
330*18358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
331*18358Ssam 	    bcmp(&ws, &winsize, sizeof (ws))) {
332*18358Ssam 		winsize = ws;
333*18358Ssam 		longjmp(winsizechanged, 1);
334*18358Ssam 	}
335*18358Ssam }
336*18358Ssam 
3376444Swnj oob()
3386444Swnj {
33910839Ssam 	int out = 1+1, atmark;
3409365Ssam 	char waste[BUFSIZ], mark;
3416444Swnj 
3429365Ssam 	ioctl(1, TIOCFLUSH, (char *)&out);
3436444Swnj 	for (;;) {
34410839Ssam 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
3456444Swnj 			perror("ioctl");
3466444Swnj 			break;
3476444Swnj 		}
34810839Ssam 		if (atmark)
3496444Swnj 			break;
3509365Ssam 		(void) read(rem, waste, sizeof (waste));
3516444Swnj 	}
35212990Ssam 	recv(rem, &mark, 1, MSG_OOB);
3536444Swnj 	if (mark & TIOCPKT_NOSTOP) {
35413075Ssam 		notc.t_stopc = -1;
35513075Ssam 		notc.t_startc = -1;
35613075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3576444Swnj 	}
3586444Swnj 	if (mark & TIOCPKT_DOSTOP) {
35913075Ssam 		notc.t_stopc = deftc.t_stopc;
36013075Ssam 		notc.t_startc = deftc.t_startc;
36113075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3626444Swnj 	}
3636444Swnj }
3646444Swnj 
3659365Ssam /*
3669365Ssam  * reader: read from remote: line -> 1
3679365Ssam  */
3689365Ssam reader()
3696444Swnj {
3709365Ssam 	char rb[BUFSIZ];
3719365Ssam 	register int cnt;
3726444Swnj 
37312990Ssam 	signal(SIGURG, oob);
3746444Swnj 	{ int pid = -getpid();
3759365Ssam 	  ioctl(rem, SIOCSPGRP, (char *)&pid); }
3766444Swnj 	for (;;) {
3779365Ssam 		cnt = read(rem, rb, sizeof (rb));
37811396Ssam 		if (cnt == 0)
37911396Ssam 			break;
38011396Ssam 		if (cnt < 0) {
3819365Ssam 			if (errno == EINTR)
3826444Swnj 				continue;
3836444Swnj 			break;
3846444Swnj 		}
3859365Ssam 		write(1, rb, cnt);
3866444Swnj 	}
3876444Swnj }
3886444Swnj 
3896444Swnj mode(f)
3906444Swnj {
39113075Ssam 	struct tchars *tc;
39213075Ssam 	struct ltchars *ltc;
39313075Ssam 	struct sgttyb sb;
3949365Ssam 
39513075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
3969962Ssam 	switch (f) {
3979962Ssam 
3989962Ssam 	case 0:
39913075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
40013075Ssam 		sb.sg_flags |= defflags|tabflag;
4019962Ssam 		tc = &deftc;
40213075Ssam 		ltc = &defltc;
40313075Ssam 		sb.sg_kill = defkill;
40413075Ssam 		sb.sg_erase = deferase;
4059962Ssam 		break;
4069962Ssam 
4079962Ssam 	case 1:
40813075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
40913075Ssam 		sb.sg_flags &= ~defflags;
41012155Ssam 		/* preserve tab delays, but turn off XTABS */
41113075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
41213075Ssam 			sb.sg_flags &= ~TBDELAY;
4139962Ssam 		tc = &notc;
41413075Ssam 		ltc = &noltc;
41513075Ssam 		sb.sg_kill = sb.sg_erase = -1;
4169962Ssam 		break;
4179962Ssam 
4189962Ssam 	default:
4199962Ssam 		return;
4206444Swnj 	}
42113075Ssam 	ioctl(0, TIOCSLTC, (char *)ltc);
42213075Ssam 	ioctl(0, TIOCSETC, (char *)tc);
42313075Ssam 	ioctl(0, TIOCSETN, (char *)&sb);
4246444Swnj }
4256444Swnj 
4269365Ssam /*VARARGS*/
4276444Swnj prf(f, a1, a2, a3)
4289365Ssam 	char *f;
4296444Swnj {
4306444Swnj 	fprintf(stderr, f, a1, a2, a3);
4316444Swnj 	fprintf(stderr, CRLF);
4326444Swnj }
4336444Swnj 
4349365Ssam lostpeer()
4356444Swnj {
43612990Ssam 	signal(SIGPIPE, SIG_IGN);
43712155Ssam 	prf("\007Connection closed.");
4389365Ssam 	done();
4396444Swnj }
440