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