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