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