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