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