rlogin.c revision 17284
1/*
2 * Copyright (c) 1983, 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1983, 1990, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/*
45 * rlogin - remote login
46 */
47#include <sys/param.h>
48#include <sys/socket.h>
49#include <sys/time.h>
50#include <sys/resource.h>
51#include <sys/wait.h>
52
53#include <netinet/in.h>
54#include <netinet/in_systm.h>
55#include <netinet/ip.h>
56#include <netinet/tcp.h>
57
58#include <errno.h>
59#include <fcntl.h>
60#include <netdb.h>
61#include <pwd.h>
62#include <setjmp.h>
63#include <sgtty.h>
64#include <signal.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <unistd.h>
69
70#ifdef __STDC__
71#include <stdarg.h>
72#else
73#include <varargs.h>
74#endif
75
76#ifdef KERBEROS
77#include <des.h>
78#include <kerberosIV/krb.h>
79
80#include "krb.h"
81
82CREDENTIALS cred;
83Key_schedule schedule;
84int use_kerberos = 1, doencrypt;
85char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
86#endif
87
88#ifndef TIOCPKT_WINDOW
89#define	TIOCPKT_WINDOW	0x80
90#endif
91
92/* concession to Sun */
93#ifndef SIGUSR1
94#define	SIGUSR1	30
95#endif
96
97int eight, litout, rem;
98
99int noescape;
100u_char escapechar = '~';
101
102char *speeds[] = {
103	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
104	"1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
105#define	MAX_SPEED_LENGTH	(sizeof("115200") - 1)
106};
107
108#ifdef OLDSUN
109struct winsize {
110	unsigned short ws_row, ws_col;
111	unsigned short ws_xpixel, ws_ypixel;
112};
113#else
114#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
115#endif
116struct	winsize winsize;
117
118void		catch_child __P((int));
119void		copytochild __P((int));
120__dead void	doit __P((long));
121__dead void	done __P((int));
122void		echo __P((char));
123u_int		getescape __P((char *));
124void		lostpeer __P((int));
125void		mode __P((int));
126void		msg __P((char *));
127void		oob __P((int));
128int		reader __P((int));
129void		sendwindow __P((void));
130void		setsignal __P((int));
131void		sigwinch __P((int));
132void		stop __P((char));
133__dead void	usage __P((void));
134void		writer __P((void));
135void		writeroob __P((int));
136
137#ifdef	KERBEROS
138void		warning __P((const char *, ...));
139#endif
140#ifdef OLDSUN
141int		get_window_size __P((int, struct winsize *));
142#endif
143
144int
145main(argc, argv)
146	int argc;
147	char *argv[];
148{
149	extern char *optarg;
150	extern int optind;
151	struct passwd *pw;
152	struct servent *sp;
153	struct sgttyb ttyb;
154	long omask;
155	int argoff, ch, dflag, Dflag, one, uid;
156	char *host, *p, *user, term[1024];
157
158	argoff = dflag = Dflag = 0;
159	one = 1;
160	host = user = NULL;
161
162	if (p = rindex(argv[0], '/'))
163		++p;
164	else
165		p = argv[0];
166
167	if (strcmp(p, "rlogin"))
168		host = p;
169
170	/* handle "rlogin host flags" */
171	if (!host && argc > 2 && argv[1][0] != '-') {
172		host = argv[1];
173		argoff = 1;
174	}
175
176#ifdef KERBEROS
177#define	OPTIONS	"8DEKLde:k:l:x"
178#else
179#define	OPTIONS	"8DEKLde:l:"
180#endif
181	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
182		switch(ch) {
183		case '8':
184			eight = 1;
185			break;
186		case 'D':
187			Dflag = 1;
188			break;
189		case 'E':
190			noescape = 1;
191			break;
192		case 'K':
193#ifdef KERBEROS
194			use_kerberos = 0;
195#endif
196			break;
197		case 'L':
198			litout = 1;
199			break;
200		case 'd':
201			dflag = 1;
202			break;
203		case 'e':
204			noescape = 0;
205			escapechar = getescape(optarg);
206			break;
207#ifdef KERBEROS
208		case 'k':
209			dest_realm = dst_realm_buf;
210			(void)strncpy(dest_realm, optarg, REALM_SZ);
211			break;
212#endif
213		case 'l':
214			user = optarg;
215			break;
216#ifdef CRYPT
217#ifdef KERBEROS
218		case 'x':
219			doencrypt = 1;
220			break;
221#endif
222#endif
223		case '?':
224		default:
225			usage();
226		}
227	optind += argoff;
228	argc -= optind;
229	argv += optind;
230
231	/* if haven't gotten a host yet, do so */
232	if (!host && !(host = *argv++))
233		usage();
234
235	if (*argv)
236		usage();
237
238	if (!(pw = getpwuid(uid = getuid()))) {
239		(void)fprintf(stderr, "rlogin: unknown user id.\n");
240		exit(1);
241	}
242	if (!user)
243		user = pw->pw_name;
244
245	sp = NULL;
246#ifdef KERBEROS
247	if (use_kerberos) {
248		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
249		if (sp == NULL) {
250			use_kerberos = 0;
251			warning("can't get entry for %s/tcp service",
252			    doencrypt ? "eklogin" : "klogin");
253		}
254	}
255#endif
256	if (sp == NULL)
257		sp = getservbyname("login", "tcp");
258	if (sp == NULL) {
259		(void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
260		exit(1);
261	}
262
263#define	MAX_TERM_LENGTH	(sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
264
265	(void)strncpy(term, (p = getenv("TERM")) ? p : "network",
266		      MAX_TERM_LENGTH);
267	term[MAX_TERM_LENGTH] = '\0';
268	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
269		(void)strcat(term, "/");
270		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
271	}
272
273	(void)get_window_size(0, &winsize);
274
275	(void)signal(SIGPIPE, lostpeer);
276	/* will use SIGUSR1 for window size hack, so hold it off */
277	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
278	/*
279	 * We set SIGURG and SIGUSR1 below so that an
280	 * incoming signal will be held pending rather than being
281	 * discarded. Note that these routines will be ready to get
282	 * a signal by the time that they are unblocked below.
283	 */
284	(void)signal(SIGURG, copytochild);
285	(void)signal(SIGUSR1, writeroob);
286
287#ifdef KERBEROS
288try_connect:
289	if (use_kerberos) {
290		struct hostent *hp;
291
292		/* Fully qualify hostname (needed for krb_realmofhost). */
293		hp = gethostbyname(host);
294		if (hp != NULL && !(host = strdup(hp->h_name))) {
295			(void)fprintf(stderr, "rlogin: %s\n",
296			    strerror(ENOMEM));
297			exit(1);
298		}
299
300		rem = KSUCCESS;
301		errno = 0;
302		if (dest_realm == NULL)
303			dest_realm = krb_realmofhost(host);
304
305#ifdef CRYPT
306		if (doencrypt) {
307			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
308			    dest_realm, &cred, schedule);
309			des_set_key_krb(&cred.session, schedule);
310		} else
311#endif /* CRYPT */
312			rem = krcmd(&host, sp->s_port, user, term, 0,
313			    dest_realm);
314		if (rem < 0) {
315			use_kerberos = 0;
316			sp = getservbyname("login", "tcp");
317			if (sp == NULL) {
318				(void)fprintf(stderr,
319				    "rlogin: unknown service login/tcp.\n");
320				exit(1);
321			}
322			if (errno == ECONNREFUSED)
323				warning("remote host doesn't support Kerberos");
324			if (errno == ENOENT)
325				warning("can't provide Kerberos auth data");
326			goto try_connect;
327		}
328	} else {
329#ifdef CRYPT
330		if (doencrypt) {
331			(void)fprintf(stderr,
332			    "rlogin: the -x flag requires Kerberos authentication.\n");
333			exit(1);
334		}
335#endif /* CRYPT */
336		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
337	}
338#else
339	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
340#endif /* KERBEROS */
341
342	if (rem < 0)
343		exit(1);
344
345	if (dflag &&
346	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
347		(void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
348		    strerror(errno));
349	if (Dflag &&
350	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
351		perror("rlogin: setsockopt NODELAY (ignored)");
352
353	one = IPTOS_LOWDELAY;
354	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
355		perror("rlogin: setsockopt TOS (ignored)");
356
357	(void)setuid(uid);
358	doit(omask);
359	/*NOTREACHED*/
360}
361
362int child, defflags, deflflags, tabflag;
363char deferase, defkill;
364struct tchars deftc;
365struct ltchars defltc;
366struct tchars notc = { -1, -1, -1, -1, -1, -1 };
367struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
368
369void
370doit(omask)
371	long omask;
372{
373	struct sgttyb sb;
374
375	(void)ioctl(0, TIOCGETP, (char *)&sb);
376	defflags = sb.sg_flags;
377	tabflag = defflags & TBDELAY;
378	defflags &= ECHO | CRMOD;
379	deferase = sb.sg_erase;
380	defkill = sb.sg_kill;
381	(void)ioctl(0, TIOCLGET, &deflflags);
382	(void)ioctl(0, TIOCGETC, &deftc);
383	notc.t_startc = deftc.t_startc;
384	notc.t_stopc = deftc.t_stopc;
385	(void)ioctl(0, TIOCGLTC, &defltc);
386	(void)signal(SIGINT, SIG_IGN);
387	setsignal(SIGHUP);
388	setsignal(SIGQUIT);
389	child = fork();
390	if (child == -1) {
391		(void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
392		done(1);
393	}
394	if (child == 0) {
395		mode(1);
396		if (reader(omask) == 0) {
397			msg("connection closed.");
398			exit(0);
399		}
400		sleep(1);
401		msg("\007connection closed.");
402		exit(1);
403	}
404
405	/*
406	 * We may still own the socket, and may have a pending SIGURG (or might
407	 * receive one soon) that we really want to send to the reader.  When
408	 * one of these comes in, the trap copytochild simply copies such
409	 * signals to the child. We can now unblock SIGURG and SIGUSR1
410	 * that were set above.
411	 */
412	(void)sigsetmask(omask);
413	(void)signal(SIGCHLD, catch_child);
414	writer();
415	msg("closed connection.");
416	done(0);
417}
418
419/* trap a signal, unless it is being ignored. */
420void
421setsignal(sig)
422	int sig;
423{
424	int omask = sigblock(sigmask(sig));
425
426	if (signal(sig, exit) == SIG_IGN)
427		(void)signal(sig, SIG_IGN);
428	(void)sigsetmask(omask);
429}
430
431__dead void
432done(status)
433	int status;
434{
435	int w, wstatus;
436
437	mode(0);
438	if (child > 0) {
439		/* make sure catch_child does not snap it up */
440		(void)signal(SIGCHLD, SIG_DFL);
441		if (kill(child, SIGKILL) >= 0)
442			while ((w = wait(&wstatus)) > 0 && w != child);
443	}
444	exit(status);
445}
446
447int dosigwinch;
448
449/*
450 * This is called when the reader process gets the out-of-band (urgent)
451 * request to turn on the window-changing protocol.
452 */
453void
454writeroob(signo)
455	int signo;
456{
457	if (dosigwinch == 0) {
458		sendwindow();
459		(void)signal(SIGWINCH, sigwinch);
460	}
461	dosigwinch = 1;
462}
463
464void
465catch_child(signo)
466	int signo;
467{
468	union wait status;
469	int pid;
470
471	for (;;) {
472		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
473		if (pid == 0)
474			return;
475		/* if the child (reader) dies, just quit */
476		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
477			done((int)(status.w_termsig | status.w_retcode));
478	}
479	/* NOTREACHED */
480}
481
482/*
483 * writer: write to remote: 0 -> line.
484 * ~.				terminate
485 * ~^Z				suspend rlogin process.
486 * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
487 */
488void
489writer()
490{
491	register int bol, local, n;
492	char c;
493
494	bol = 1;			/* beginning of line */
495	local = 0;
496	for (;;) {
497		n = read(STDIN_FILENO, &c, 1);
498		if (n <= 0) {
499			if (n < 0 && errno == EINTR)
500				continue;
501			break;
502		}
503		/*
504		 * If we're at the beginning of the line and recognize a
505		 * command character, then we echo locally.  Otherwise,
506		 * characters are echo'd remotely.  If the command character
507		 * is doubled, this acts as a force and local echo is
508		 * suppressed.
509		 */
510		if (bol) {
511			bol = 0;
512			if (!noescape && c == escapechar) {
513				local = 1;
514				continue;
515			}
516		} else if (local) {
517			local = 0;
518			if (c == '.' || c == deftc.t_eofc) {
519				echo(c);
520				break;
521			}
522			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
523				bol = 1;
524				echo(c);
525				stop(c);
526				continue;
527			}
528			if (c != escapechar)
529#ifdef CRYPT
530#ifdef KERBEROS
531				if (doencrypt)
532					(void)des_write(rem,
533					    (char *)&escapechar, 1);
534				else
535#endif
536#endif
537					(void)write(rem, &escapechar, 1);
538		}
539
540#ifdef CRYPT
541#ifdef KERBEROS
542		if (doencrypt) {
543			if (des_write(rem, &c, 1) == 0) {
544				msg("line gone");
545				break;
546			}
547		} else
548#endif
549#endif
550			if (write(rem, &c, 1) == 0) {
551				msg("line gone");
552				break;
553			}
554		bol = c == defkill || c == deftc.t_eofc ||
555		    c == deftc.t_intrc || c == defltc.t_suspc ||
556		    c == '\r' || c == '\n';
557	}
558}
559
560void
561#if __STDC__
562echo(register char c)
563#else
564echo(c)
565	register char c;
566#endif
567{
568	register char *p;
569	char buf[8];
570
571	p = buf;
572	c &= 0177;
573	*p++ = escapechar;
574	if (c < ' ') {
575		*p++ = '^';
576		*p++ = c + '@';
577	} else if (c == 0177) {
578		*p++ = '^';
579		*p++ = '?';
580	} else
581		*p++ = c;
582	*p++ = '\r';
583	*p++ = '\n';
584	(void)write(STDOUT_FILENO, buf, p - buf);
585}
586
587void
588#if __STDC__
589stop(char cmdc)
590#else
591stop(cmdc)
592	char cmdc;
593#endif
594{
595	mode(0);
596	(void)signal(SIGCHLD, SIG_IGN);
597	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
598	(void)signal(SIGCHLD, catch_child);
599	mode(1);
600	sigwinch(0);			/* check for size changes */
601}
602
603void
604sigwinch(signo)
605	int signo;
606{
607	struct winsize ws;
608
609	if (dosigwinch && get_window_size(0, &ws) == 0 &&
610	    bcmp(&ws, &winsize, sizeof(ws))) {
611		winsize = ws;
612		sendwindow();
613	}
614}
615
616/*
617 * Send the window size to the server via the magic escape
618 */
619void
620sendwindow()
621{
622	struct winsize *wp;
623	char obuf[4 + sizeof (struct winsize)];
624
625	wp = (struct winsize *)(obuf+4);
626	obuf[0] = 0377;
627	obuf[1] = 0377;
628	obuf[2] = 's';
629	obuf[3] = 's';
630	wp->ws_row = htons(winsize.ws_row);
631	wp->ws_col = htons(winsize.ws_col);
632	wp->ws_xpixel = htons(winsize.ws_xpixel);
633	wp->ws_ypixel = htons(winsize.ws_ypixel);
634
635#ifdef CRYPT
636#ifdef KERBEROS
637	if(doencrypt)
638		(void)des_write(rem, obuf, sizeof(obuf));
639	else
640#endif
641#endif
642		(void)write(rem, obuf, sizeof(obuf));
643}
644
645/*
646 * reader: read from remote: line -> 1
647 */
648#define	READING	1
649#define	WRITING	2
650
651jmp_buf rcvtop;
652int ppid, rcvcnt, rcvstate;
653char rcvbuf[8 * 1024];
654
655void
656oob(signo)
657	int signo;
658{
659	struct sgttyb sb;
660	int atmark, n, out, rcvd;
661	char waste[BUFSIZ], mark;
662
663	out = O_RDWR;
664	rcvd = 0;
665	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
666		switch (errno) {
667		case EWOULDBLOCK:
668			/*
669			 * Urgent data not here yet.  It may not be possible
670			 * to send it yet if we are blocked for output and
671			 * our input buffer is full.
672			 */
673			if (rcvcnt < sizeof(rcvbuf)) {
674				n = read(rem, rcvbuf + rcvcnt,
675				    sizeof(rcvbuf) - rcvcnt);
676				if (n <= 0)
677					return;
678				rcvd += n;
679			} else {
680				n = read(rem, waste, sizeof(waste));
681				if (n <= 0)
682					return;
683			}
684			continue;
685		default:
686			return;
687		}
688	}
689	if (mark & TIOCPKT_WINDOW) {
690		/* Let server know about window size changes */
691		(void)kill(ppid, SIGUSR1);
692	}
693	if (!eight && (mark & TIOCPKT_NOSTOP)) {
694		(void)ioctl(0, TIOCGETP, (char *)&sb);
695		sb.sg_flags &= ~CBREAK;
696		sb.sg_flags |= RAW;
697		(void)ioctl(0, TIOCSETN, (char *)&sb);
698		notc.t_stopc = -1;
699		notc.t_startc = -1;
700		(void)ioctl(0, TIOCSETC, (char *)&notc);
701	}
702	if (!eight && (mark & TIOCPKT_DOSTOP)) {
703		(void)ioctl(0, TIOCGETP, (char *)&sb);
704		sb.sg_flags &= ~RAW;
705		sb.sg_flags |= CBREAK;
706		(void)ioctl(0, TIOCSETN, (char *)&sb);
707		notc.t_stopc = deftc.t_stopc;
708		notc.t_startc = deftc.t_startc;
709		(void)ioctl(0, TIOCSETC, (char *)&notc);
710	}
711	if (mark & TIOCPKT_FLUSHWRITE) {
712		(void)ioctl(1, TIOCFLUSH, (char *)&out);
713		for (;;) {
714			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
715				(void)fprintf(stderr, "rlogin: ioctl: %s.\n",
716				    strerror(errno));
717				break;
718			}
719			if (atmark)
720				break;
721			n = read(rem, waste, sizeof (waste));
722			if (n <= 0)
723				break;
724		}
725		/*
726		 * Don't want any pending data to be output, so clear the recv
727		 * buffer.  If we were hanging on a write when interrupted,
728		 * don't want it to restart.  If we were reading, restart
729		 * anyway.
730		 */
731		rcvcnt = 0;
732		longjmp(rcvtop, 1);
733	}
734
735	/* oob does not do FLUSHREAD (alas!) */
736
737	/*
738	 * If we filled the receive buffer while a read was pending, longjmp
739	 * to the top to restart appropriately.  Don't abort a pending write,
740	 * however, or we won't know how much was written.
741	 */
742	if (rcvd && rcvstate == READING)
743		longjmp(rcvtop, 1);
744}
745
746/* reader: read from remote: line -> 1 */
747int
748reader(omask)
749	int omask;
750{
751	int pid, n, remaining;
752	char *bufp;
753
754#if BSD >= 43 || defined(SUNOS4)
755	pid = getpid();		/* modern systems use positives for pid */
756#else
757	pid = -getpid();	/* old broken systems use negatives */
758#endif
759	(void)signal(SIGTTOU, SIG_IGN);
760	(void)signal(SIGURG, oob);
761	ppid = getppid();
762	(void)fcntl(rem, F_SETOWN, pid);
763	(void)setjmp(rcvtop);
764	(void)sigsetmask(omask);
765	bufp = rcvbuf;
766	for (;;) {
767		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
768			rcvstate = WRITING;
769			n = write(STDOUT_FILENO, bufp, remaining);
770			if (n < 0) {
771				if (errno != EINTR)
772					return (-1);
773				continue;
774			}
775			bufp += n;
776		}
777		bufp = rcvbuf;
778		rcvcnt = 0;
779		rcvstate = READING;
780
781#ifdef CRYPT
782#ifdef KERBEROS
783		if (doencrypt)
784			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
785		else
786#endif
787#endif
788			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
789		if (rcvcnt == 0)
790			return (0);
791		if (rcvcnt < 0) {
792			if (errno == EINTR)
793				continue;
794			(void)fprintf(stderr, "rlogin: read: %s.\n",
795			    strerror(errno));
796			return (-1);
797		}
798	}
799}
800
801void
802mode(f)
803	int f;
804{
805	struct ltchars *ltc;
806	struct sgttyb sb;
807	struct tchars *tc;
808	int lflags;
809
810	(void)ioctl(0, TIOCGETP, (char *)&sb);
811	(void)ioctl(0, TIOCLGET, (char *)&lflags);
812	switch(f) {
813	case 0:
814		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
815		sb.sg_flags |= defflags|tabflag;
816		tc = &deftc;
817		ltc = &defltc;
818		sb.sg_kill = defkill;
819		sb.sg_erase = deferase;
820		lflags = deflflags;
821		break;
822	case 1:
823		sb.sg_flags |= (eight ? RAW : CBREAK);
824		sb.sg_flags &= ~defflags;
825		/* preserve tab delays, but turn off XTABS */
826		if ((sb.sg_flags & TBDELAY) == XTABS)
827			sb.sg_flags &= ~TBDELAY;
828		tc = &notc;
829		ltc = &noltc;
830		sb.sg_kill = sb.sg_erase = -1;
831		if (litout)
832			lflags |= LLITOUT;
833		break;
834	default:
835		return;
836	}
837	(void)ioctl(0, TIOCSLTC, (char *)ltc);
838	(void)ioctl(0, TIOCSETC, (char *)tc);
839	(void)ioctl(0, TIOCSETN, (char *)&sb);
840	(void)ioctl(0, TIOCLSET, (char *)&lflags);
841}
842
843void
844lostpeer(signo)
845	int signo;
846{
847	(void)signal(SIGPIPE, SIG_IGN);
848	msg("\007connection closed.");
849	done(1);
850}
851
852/* copy SIGURGs to the child process. */
853void
854copytochild(signo)
855	int signo;
856{
857	(void)kill(child, SIGURG);
858}
859
860void
861msg(str)
862	char *str;
863{
864	(void)fprintf(stderr, "rlogin: %s\r\n", str);
865}
866
867#ifdef KERBEROS
868/* VARARGS */
869void
870#if __STDC__
871warning(const char *fmt, ...)
872#else
873warning(fmt, va_alist)
874	char *fmt;
875	va_dcl
876#endif
877{
878	va_list ap;
879
880	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
881#ifdef __STDC__
882	va_start(ap, fmt);
883#else
884	va_start(ap);
885#endif
886	vfprintf(stderr, fmt, ap);
887	va_end(ap);
888	(void)fprintf(stderr, ".\n");
889}
890#endif
891
892__dead void
893usage()
894{
895	(void)fprintf(stderr,
896	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
897#ifdef KERBEROS
898#ifdef CRYPT
899	    "8DEKLx", " [-k realm] ");
900#else
901	    "8DEKL", " [-k realm] ");
902#endif
903#else
904	    "8DEL", " ");
905#endif
906	exit(1);
907}
908
909/*
910 * The following routine provides compatibility (such as it is) between older
911 * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
912 */
913#ifdef OLDSUN
914int
915get_window_size(fd, wp)
916	int fd;
917	struct winsize *wp;
918{
919	struct ttysize ts;
920	int error;
921
922	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
923		return (error);
924	wp->ws_row = ts.ts_lines;
925	wp->ws_col = ts.ts_cols;
926	wp->ws_xpixel = 0;
927	wp->ws_ypixel = 0;
928	return (0);
929}
930#endif
931
932u_int
933getescape(p)
934	register char *p;
935{
936	long val;
937	int len;
938
939	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
940		return ((u_int)*p);
941					/* otherwise, \nnn */
942	if (*p == '\\' && len >= 2 && len <= 4) {
943		val = strtol(++p, NULL, 8);
944		for (;;) {
945			if (!*++p)
946				return ((u_int)val);
947			if (*p < '0' || *p > '8')
948				break;
949		}
950	}
951	msg("illegal option value -- e");
952	usage();
953	/* NOTREACHED */
954}
955