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