xref: /original-bsd/usr.bin/rlogin/rlogin.c (revision b6592f3d)
1 /*
2  * Copyright (c) 1983, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1983, 1990, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)rlogin.c	8.2 (Berkeley) 08/23/94";
16 #endif /* not lint */
17 
18 /*
19  * rlogin - remote login
20  */
21 #include <sys/param.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/wait.h>
26 #include <sys/ioctl.h>
27 
28 #include <netinet/in.h>
29 #include <netinet/in_systm.h>
30 #include <netinet/ip.h>
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <netdb.h>
35 #include <pwd.h>
36 #include <setjmp.h>
37 #include <termios.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #ifdef __STDC__
45 #include <stdarg.h>
46 #else
47 #include <varargs.h>
48 #endif
49 
50 #ifdef KERBEROS
51 #include <kerberosIV/des.h>
52 #include <kerberosIV/krb.h>
53 
54 #include "krb.h"
55 
56 CREDENTIALS cred;
57 Key_schedule schedule;
58 int use_kerberos = 1, doencrypt;
59 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
60 #endif
61 
62 #ifndef TIOCPKT_WINDOW
63 #define	TIOCPKT_WINDOW	0x80
64 #endif
65 
66 /* concession to Sun */
67 #ifndef SIGUSR1
68 #define	SIGUSR1	30
69 #endif
70 
71 int eight, litout, rem;
72 
73 int noescape;
74 u_char escapechar = '~';
75 
76 #ifdef OLDSUN
77 struct winsize {
78 	unsigned short ws_row, ws_col;
79 	unsigned short ws_xpixel, ws_ypixel;
80 };
81 #else
82 #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
83 #endif
84 struct	winsize winsize;
85 
86 void		catch_child __P((int));
87 void		copytochild __P((int));
88 __dead void	doit __P((sigset_t *));
89 __dead void	done __P((int));
90 void		echo __P((char));
91 u_int		getescape __P((char *));
92 void		lostpeer __P((int));
93 void		mode __P((int));
94 void		msg __P((char *));
95 void		oob __P((int));
96 int		reader __P((sigset_t *));
97 void		sendwindow __P((void));
98 void		setsignal __P((int));
99 void		sigwinch __P((int));
100 void		stop __P((char));
101 __dead void	usage __P((void));
102 void		writer __P((void));
103 void		writeroob __P((int));
104 
105 #ifdef	KERBEROS
106 void		warning __P((const char *, ...));
107 #endif
108 #ifdef OLDSUN
109 int		get_window_size __P((int, struct winsize *));
110 #endif
111 
112 int
113 main(argc, argv)
114 	int argc;
115 	char *argv[];
116 {
117 	struct passwd *pw;
118 	struct servent *sp;
119 	sigset_t smask;
120 	uid_t uid;
121 	int argoff, ch, dflag, one;
122 	char *host, *p, *user, term[1024];
123 	struct sigaction sa;
124 
125 	argoff = dflag = 0;
126 	one = 1;
127 	host = user = NULL;
128 
129 	if (p = strrchr(argv[0], '/'))
130 		++p;
131 	else
132 		p = argv[0];
133 
134 	if (strcmp(p, "rlogin") != 0)
135 		host = p;
136 
137 	/* handle "rlogin host flags" */
138 	if (!host && argc > 2 && argv[1][0] != '-') {
139 		host = argv[1];
140 		argoff = 1;
141 	}
142 
143 #ifdef KERBEROS
144 #define	OPTIONS	"8EKLde:k:l:x"
145 #else
146 #define	OPTIONS	"8EKLde:l:"
147 #endif
148 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
149 		switch(ch) {
150 		case '8':
151 			eight = 1;
152 			break;
153 		case 'E':
154 			noescape = 1;
155 			break;
156 		case 'K':
157 #ifdef KERBEROS
158 			use_kerberos = 0;
159 #endif
160 			break;
161 		case 'L':
162 			litout = 1;
163 			break;
164 		case 'd':
165 			dflag = 1;
166 			break;
167 		case 'e':
168 			noescape = 0;
169 			escapechar = getescape(optarg);
170 			break;
171 #ifdef KERBEROS
172 		case 'k':
173 			dest_realm = dst_realm_buf;
174 			(void)strncpy(dest_realm, optarg, REALM_SZ);
175 			break;
176 #endif
177 		case 'l':
178 			user = optarg;
179 			break;
180 #ifdef CRYPT
181 #ifdef KERBEROS
182 		case 'x':
183 			doencrypt = 1;
184 			des_set_key(cred.session, schedule);
185 			break;
186 #endif
187 #endif
188 		case '?':
189 		default:
190 			usage();
191 		}
192 	optind += argoff;
193 	argc -= optind;
194 	argv += optind;
195 
196 	/* if haven't gotten a host yet, do so */
197 	if (!host && !(host = *argv++))
198 		usage();
199 
200 	if (*argv)
201 		usage();
202 
203 	if (!(pw = getpwuid(uid = getuid())))
204 		errx(1, "unknown user id.");
205 
206 	if (!user)
207 		user = pw->pw_name;
208 
209 	sp = NULL;
210 #ifdef KERBEROS
211 	if (use_kerberos) {
212 		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
213 		if (sp == NULL) {
214 			use_kerberos = 0;
215 			warning("can't get entry for %s/tcp service",
216 			    doencrypt ? "eklogin" : "klogin");
217 		}
218 	}
219 #endif
220 	if (sp == NULL)
221 		sp = getservbyname("login", "tcp");
222 	if (sp == NULL)
223 		errx(1, "login/tcp: unknown service.");
224 
225 	(void)snprintf(term, sizeof(term), "%s/%d",
226 			((p = getenv("TERM")) ? p : "network"),
227 			cfgetispeed(0));
228 
229 	(void)get_window_size(0, &winsize);
230 
231 	sigemptyset(&sa.sa_mask);
232 	sa.sa_flags = SA_RESTART;
233 	sa.sa_handler = lostpeer;
234 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
235 	/* will use SIGUSR1 for window size hack, so hold it off */
236 	sigemptyset(&smask);
237 	sigaddset(&smask, SIGURG);
238 	sigaddset(&smask, SIGUSR1);
239 	(void)sigprocmask(SIG_SETMASK, &smask, &smask);
240 	/*
241 	 * We set SIGURG and SIGUSR1 below so that an
242 	 * incoming signal will be held pending rather than being
243 	 * discarded. Note that these routines will be ready to get
244 	 * a signal by the time that they are unblocked below.
245 	 */
246 	sa.sa_handler = copytochild;
247 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
248 	sa.sa_handler = writeroob;
249 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
250 
251 #ifdef KERBEROS
252 try_connect:
253 	if (use_kerberos) {
254 		struct hostent *hp;
255 
256 		/* Fully qualify hostname (needed for krb_realmofhost). */
257 		hp = gethostbyname(host);
258 		if (hp != NULL && !(host = strdup(hp->h_name)))
259 			errx(1, "%s", strerror(ENOMEM));
260 
261 		rem = KSUCCESS;
262 		errno = 0;
263 		if (dest_realm == NULL)
264 			dest_realm = krb_realmofhost(host);
265 
266 #ifdef CRYPT
267 		if (doencrypt)
268 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
269 			    dest_realm, &cred, schedule);
270 		else
271 #endif /* CRYPT */
272 			rem = krcmd(&host, sp->s_port, user, term, 0,
273 			    dest_realm);
274 		if (rem < 0) {
275 			use_kerberos = 0;
276 			sp = getservbyname("login", "tcp");
277 			if (sp == NULL)
278 				errx(1, "unknown service login/tcp.");
279 			if (errno == ECONNREFUSED)
280 				warning("remote host doesn't support Kerberos");
281 			if (errno == ENOENT)
282 				warning("can't provide Kerberos auth data");
283 			goto try_connect;
284 		}
285 	} else {
286 #ifdef CRYPT
287 		if (doencrypt)
288 			errx(1, "the -x flag requires Kerberos authentication.");
289 #endif /* CRYPT */
290 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
291 	}
292 #else
293 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
294 #endif /* KERBEROS */
295 
296 	if (rem < 0)
297 		exit(1);
298 
299 	if (dflag &&
300 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
301 		warn("setsockopt DEBUG (ignored)");
302 	one = IPTOS_LOWDELAY;
303 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
304 		warn("setsockopt TOS (ignored)");
305 
306 	(void)setuid(uid);
307 	doit(&smask);
308 	/*NOTREACHED*/
309 }
310 
311 pid_t child;
312 struct termios deftt;
313 struct termios nott;
314 
315 void
316 doit(smask)
317 	sigset_t *smask;
318 {
319 	int i;
320 	struct sigaction sa;
321 
322 	for (i = 0; i < NCCS; i++)
323 		nott.c_cc[i] = _POSIX_VDISABLE;
324 	tcgetattr(0, &deftt);
325 	nott.c_cc[VSTART] = deftt.c_cc[VSTART];
326 	nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
327 	sigemptyset(&sa.sa_mask);
328 	sa.sa_flags = SA_RESTART;
329 	sa.sa_handler = SIG_IGN;
330 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
331 	setsignal(SIGHUP);
332 	setsignal(SIGQUIT);
333 	child = fork();
334 	if (child == -1) {
335 		warn("fork");
336 		done(1);
337 	}
338 	if (child == 0) {
339 		mode(1);
340 		if (reader(smask) == 0) {
341 			msg("connection closed.");
342 			exit(0);
343 		}
344 		sleep(1);
345 		msg("\007connection closed.");
346 		exit(1);
347 	}
348 
349 	/*
350 	 * We may still own the socket, and may have a pending SIGURG (or might
351 	 * receive one soon) that we really want to send to the reader.  When
352 	 * one of these comes in, the trap copytochild simply copies such
353 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
354 	 * that were set above.
355 	 */
356 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
357 	sa.sa_handler = catch_child;
358 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
359 	writer();
360 	msg("closed connection.");
361 	done(0);
362 }
363 
364 /* trap a signal, unless it is being ignored. */
365 void
366 setsignal(sig)
367 	int sig;
368 {
369 	struct sigaction sa;
370 	sigset_t sigs;
371 
372 	sigemptyset(&sigs);
373 	sigaddset(&sigs, sig);
374 	sigprocmask(SIG_BLOCK, &sigs, &sigs);
375 
376 	sigemptyset(&sa.sa_mask);
377 	sa.sa_handler = exit;
378 	sa.sa_flags = SA_RESTART;
379 	(void)sigaction(sig, &sa, &sa);
380 	if (sa.sa_handler == SIG_IGN)
381 		(void)sigaction(sig, &sa, (struct sigaction *) 0);
382 
383 	(void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
384 }
385 
386 __dead void
387 done(status)
388 	int status;
389 {
390 	pid_t w;
391 	int wstatus;
392 	struct sigaction sa;
393 
394 	mode(0);
395 	if (child > 0) {
396 		/* make sure catch_child does not snap it up */
397 		sigemptyset(&sa.sa_mask);
398 		sa.sa_handler = SIG_DFL;
399 		sa.sa_flags = 0;
400 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
401 		if (kill(child, SIGKILL) >= 0)
402 			while ((w = wait(&wstatus)) > 0 && w != child)
403 				continue;
404 	}
405 	exit(status);
406 }
407 
408 int dosigwinch;
409 
410 /*
411  * This is called when the reader process gets the out-of-band (urgent)
412  * request to turn on the window-changing protocol.
413  */
414 void
415 writeroob(signo)
416 	int signo;
417 {
418 	struct sigaction sa;
419 
420 	if (dosigwinch == 0) {
421 		sendwindow();
422 		sigemptyset(&sa.sa_mask);
423 		sa.sa_handler = sigwinch;
424 		sa.sa_flags = SA_RESTART;
425 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
426 	}
427 	dosigwinch = 1;
428 }
429 
430 void
431 catch_child(signo)
432 	int signo;
433 {
434 	int status;
435 	pid_t pid;
436 
437 	for (;;) {
438 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
439 		if (pid == 0)
440 			return;
441 		/* if the child (reader) dies, just quit */
442 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
443 			done(WEXITSTATUS(status) | WTERMSIG(status));
444 	}
445 	/* NOTREACHED */
446 }
447 
448 /*
449  * writer: write to remote: 0 -> line.
450  * ~.				terminate
451  * ~^Z				suspend rlogin process.
452  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
453  */
454 void
455 writer()
456 {
457 	register int bol, local, n;
458 	char c;
459 
460 	bol = 1;			/* beginning of line */
461 	local = 0;
462 	for (;;) {
463 		n = read(STDIN_FILENO, &c, 1);
464 		if (n <= 0) {
465 			if (n < 0 && errno == EINTR)
466 				continue;
467 			break;
468 		}
469 		/*
470 		 * If we're at the beginning of the line and recognize a
471 		 * command character, then we echo locally.  Otherwise,
472 		 * characters are echo'd remotely.  If the command character
473 		 * is doubled, this acts as a force and local echo is
474 		 * suppressed.
475 		 */
476 		if (bol) {
477 			bol = 0;
478 			if (!noescape && c == escapechar) {
479 				local = 1;
480 				continue;
481 			}
482 		} else if (local) {
483 			local = 0;
484 			if (c == '.' || c == deftt.c_cc[VEOF]) {
485 				echo(c);
486 				break;
487 			}
488 			if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) {
489 				bol = 1;
490 				echo(c);
491 				stop(c);
492 				continue;
493 			}
494 			if (c != escapechar)
495 #ifdef CRYPT
496 #ifdef KERBEROS
497 				if (doencrypt)
498 					(void)des_write(rem,
499 					    (char *)&escapechar, 1);
500 				else
501 #endif
502 #endif
503 					(void)write(rem, &escapechar, 1);
504 		}
505 
506 #ifdef CRYPT
507 #ifdef KERBEROS
508 		if (doencrypt) {
509 			if (des_write(rem, &c, 1) == 0) {
510 				msg("line gone");
511 				break;
512 			}
513 		} else
514 #endif
515 #endif
516 			if (write(rem, &c, 1) == 0) {
517 				msg("line gone");
518 				break;
519 			}
520 		bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] ||
521 		    c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] ||
522 		    c == '\r' || c == '\n';
523 	}
524 }
525 
526 void
527 #if __STDC__
528 echo(register char c)
529 #else
530 echo(c)
531 	register char c;
532 #endif
533 {
534 	register char *p;
535 	char buf[8];
536 
537 	p = buf;
538 	c &= 0177;
539 	*p++ = escapechar;
540 	if (c < ' ') {
541 		*p++ = '^';
542 		*p++ = c + '@';
543 	} else if (c == 0177) {
544 		*p++ = '^';
545 		*p++ = '?';
546 	} else
547 		*p++ = c;
548 	*p++ = '\r';
549 	*p++ = '\n';
550 	(void)write(STDOUT_FILENO, buf, p - buf);
551 }
552 
553 void
554 #if __STDC__
555 stop(char cmdc)
556 #else
557 stop(cmdc)
558 	char cmdc;
559 #endif
560 {
561 	struct sigaction sa;
562 
563 	mode(0);
564 	sigemptyset(&sa.sa_mask);
565 	sa.sa_handler = SIG_IGN;
566 	sa.sa_flags = SA_RESTART;
567 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
568 	(void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP);
569 	sa.sa_handler = catch_child;
570 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
571 	mode(1);
572 	sigwinch(0);			/* check for size changes */
573 }
574 
575 void
576 sigwinch(signo)
577 	int signo;
578 {
579 	struct winsize ws;
580 
581 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
582 	    memcmp(&ws, &winsize, sizeof(ws))) {
583 		winsize = ws;
584 		sendwindow();
585 	}
586 }
587 
588 /*
589  * Send the window size to the server via the magic escape
590  */
591 void
592 sendwindow()
593 {
594 	struct winsize *wp;
595 	char obuf[4 + sizeof (struct winsize)];
596 
597 	wp = (struct winsize *)(obuf+4);
598 	obuf[0] = 0377;
599 	obuf[1] = 0377;
600 	obuf[2] = 's';
601 	obuf[3] = 's';
602 	wp->ws_row = htons(winsize.ws_row);
603 	wp->ws_col = htons(winsize.ws_col);
604 	wp->ws_xpixel = htons(winsize.ws_xpixel);
605 	wp->ws_ypixel = htons(winsize.ws_ypixel);
606 
607 #ifdef CRYPT
608 #ifdef KERBEROS
609 	if(doencrypt)
610 		(void)des_write(rem, obuf, sizeof(obuf));
611 	else
612 #endif
613 #endif
614 		(void)write(rem, obuf, sizeof(obuf));
615 }
616 
617 /*
618  * reader: read from remote: line -> 1
619  */
620 #define	READING	1
621 #define	WRITING	2
622 
623 jmp_buf rcvtop;
624 pid_t ppid;
625 int rcvcnt, rcvstate;
626 char rcvbuf[8 * 1024];
627 
628 void
629 oob(signo)
630 	int signo;
631 {
632 	struct termios tt;
633 	int atmark, n, out, rcvd;
634 	char waste[BUFSIZ], mark;
635 
636 	out = O_RDWR;
637 	rcvd = 0;
638 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
639 		switch (errno) {
640 		case EWOULDBLOCK:
641 			/*
642 			 * Urgent data not here yet.  It may not be possible
643 			 * to send it yet if we are blocked for output and
644 			 * our input buffer is full.
645 			 */
646 			if (rcvcnt < sizeof(rcvbuf)) {
647 				n = read(rem, rcvbuf + rcvcnt,
648 				    sizeof(rcvbuf) - rcvcnt);
649 				if (n <= 0)
650 					return;
651 				rcvd += n;
652 			} else {
653 				n = read(rem, waste, sizeof(waste));
654 				if (n <= 0)
655 					return;
656 			}
657 			continue;
658 		default:
659 			return;
660 		}
661 	}
662 	if (mark & TIOCPKT_WINDOW) {
663 		/* Let server know about window size changes */
664 		(void)kill(ppid, SIGUSR1);
665 	}
666 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
667 		tcgetattr(0, &tt);
668 		tt.c_iflag &= ~(IXON|IXOFF);
669 		tt.c_cc[VSTOP] = _POSIX_VDISABLE;
670 		tt.c_cc[VSTART] = _POSIX_VDISABLE;
671 		tcsetattr(0, TCSANOW, &tt);
672 	}
673 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
674 		tcgetattr(0, &tt);
675 		tt.c_iflag |= (IXON|IXOFF);
676 		tt.c_cc[VSTOP] = deftt.c_cc[VSTOP];
677 		tt.c_cc[VSTART] = deftt.c_cc[VSTART];
678 		tcsetattr(0, TCSANOW, &tt);
679 	}
680 	if (mark & TIOCPKT_FLUSHWRITE) {
681 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
682 		for (;;) {
683 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
684 				warn("ioctl SIOCATMARK (ignored)");
685 				break;
686 			}
687 			if (atmark)
688 				break;
689 			n = read(rem, waste, sizeof (waste));
690 			if (n <= 0)
691 				break;
692 		}
693 		/*
694 		 * Don't want any pending data to be output, so clear the recv
695 		 * buffer.  If we were hanging on a write when interrupted,
696 		 * don't want it to restart.  If we were reading, restart
697 		 * anyway.
698 		 */
699 		rcvcnt = 0;
700 		longjmp(rcvtop, 1);
701 	}
702 
703 	/* oob does not do FLUSHREAD (alas!) */
704 
705 	/*
706 	 * If we filled the receive buffer while a read was pending, longjmp
707 	 * to the top to restart appropriately.  Don't abort a pending write,
708 	 * however, or we won't know how much was written.
709 	 */
710 	if (rcvd && rcvstate == READING)
711 		longjmp(rcvtop, 1);
712 }
713 
714 /* reader: read from remote: line -> 1 */
715 int
716 reader(smask)
717 	sigset_t *smask;
718 {
719 	pid_t pid;
720 	int n, remaining;
721 	char *bufp;
722 	struct sigaction sa;
723 
724 #if BSD >= 43 || defined(SUNOS4)
725 	pid = getpid();		/* modern systems use positives for pid */
726 #else
727 	pid = -getpid();	/* old broken systems use negatives */
728 #endif
729 	sigemptyset(&sa.sa_mask);
730 	sa.sa_flags = SA_RESTART;
731 	sa.sa_handler = SIG_IGN;
732 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
733 	sa.sa_handler = oob;
734 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
735 	ppid = getppid();
736 	(void)fcntl(rem, F_SETOWN, pid);
737 	(void)setjmp(rcvtop);
738 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
739 	bufp = rcvbuf;
740 	for (;;) {
741 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
742 			rcvstate = WRITING;
743 			n = write(STDOUT_FILENO, bufp, remaining);
744 			if (n < 0) {
745 				if (errno != EINTR)
746 					return (-1);
747 				continue;
748 			}
749 			bufp += n;
750 		}
751 		bufp = rcvbuf;
752 		rcvcnt = 0;
753 		rcvstate = READING;
754 
755 #ifdef CRYPT
756 #ifdef KERBEROS
757 		if (doencrypt)
758 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
759 		else
760 #endif
761 #endif
762 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
763 		if (rcvcnt == 0)
764 			return (0);
765 		if (rcvcnt < 0) {
766 			if (errno == EINTR)
767 				continue;
768 			warn("read");
769 			return (-1);
770 		}
771 	}
772 }
773 
774 void
775 mode(f)
776 	int f;
777 {
778 	struct termios tt;
779 
780 	switch (f) {
781 	case 0:
782 		tcsetattr(0, TCSADRAIN, &deftt);
783 		break;
784 	case 1:
785 		tt = deftt;
786 		tt.c_oflag &= ~OPOST;
787 		tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
788 		tt.c_cc[VMIN] = 1;
789 		tt.c_cc[VTIME] = 0;
790 		if (eight) {
791 			tt.c_iflag &= ~(IXON | IXOFF);
792 			tt.c_cc[VSTOP] = _POSIX_VDISABLE;
793 			tt.c_cc[VSTART] = _POSIX_VDISABLE;
794 		}
795 		/*if (litout)
796 			lflags |= LLITOUT;*/
797 		tcsetattr(0, TCSADRAIN, &tt);
798 		break;
799 
800 	default:
801 		return;
802 	}
803 }
804 
805 void
806 lostpeer(signo)
807 	int signo;
808 {
809 	struct sigaction sa;
810 
811 	sigemptyset(&sa.sa_mask);
812 	sa.sa_flags = SA_RESTART;
813 	sa.sa_handler = SIG_IGN;
814 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
815 	msg("\007connection closed.");
816 	done(1);
817 }
818 
819 /* copy SIGURGs to the child process. */
820 void
821 copytochild(signo)
822 	int signo;
823 {
824 
825 	(void)kill(child, SIGURG);
826 }
827 
828 void
829 msg(str)
830 	char *str;
831 {
832 
833 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
834 }
835 
836 #ifdef KERBEROS
837 /* VARARGS */
838 void
839 #if __STDC__
840 warning(const char *fmt, ...)
841 #else
842 warning(fmt, va_alist)
843 	char *fmt;
844 	va_dcl
845 #endif
846 {
847 	va_list ap;
848 
849 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
850 #ifdef __STDC__
851 	va_start(ap, fmt);
852 #else
853 	va_start(ap);
854 #endif
855 	vfprintf(stderr, fmt, ap);
856 	va_end(ap);
857 	(void)fprintf(stderr, ".\n");
858 }
859 #endif
860 
861 __dead void
862 usage()
863 {
864 	(void)fprintf(stderr,
865 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
866 #ifdef KERBEROS
867 #ifdef CRYPT
868 	    "8EKLx", " [-k realm] ");
869 #else
870 	    "8EKL", " [-k realm] ");
871 #endif
872 #else
873 	    "8EL", " ");
874 #endif
875 	exit(1);
876 }
877 
878 /*
879  * The following routine provides compatibility (such as it is) between older
880  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
881  */
882 #ifdef OLDSUN
883 int
884 get_window_size(fd, wp)
885 	int fd;
886 	struct winsize *wp;
887 {
888 	struct ttysize ts;
889 	int error;
890 
891 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
892 		return (error);
893 	wp->ws_row = ts.ts_lines;
894 	wp->ws_col = ts.ts_cols;
895 	wp->ws_xpixel = 0;
896 	wp->ws_ypixel = 0;
897 	return (0);
898 }
899 #endif
900 
901 u_int
902 getescape(p)
903 	register char *p;
904 {
905 	long val;
906 	int len;
907 
908 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
909 		return ((u_int)*p);
910 					/* otherwise, \nnn */
911 	if (*p == '\\' && len >= 2 && len <= 4) {
912 		val = strtol(++p, NULL, 8);
913 		for (;;) {
914 			if (!*++p)
915 				return ((u_int)val);
916 			if (*p < '0' || *p > '8')
917 				break;
918 		}
919 	}
920 	msg("illegal option value -- e");
921 	usage();
922 	/* NOTREACHED */
923 }
924