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