main.c revision 26999
1/*-
2 * Copyright (c) 1980, 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) 1980, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41/*static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";*/
42static char rcsid[] = "$Id: main.c,v 1.20 1997/06/03 12:56:47 davidn Exp $";
43#endif /* not lint */
44
45#include <sys/param.h>
46#include <sys/stat.h>
47#include <sys/ioctl.h>
48#include <sys/resource.h>
49#include <sys/ttydefaults.h>
50#include <sys/utsname.h>
51#include <errno.h>
52#include <signal.h>
53#include <fcntl.h>
54#include <time.h>
55#include <ctype.h>
56#include <fcntl.h>
57#include <libutil.h>
58#include <locale.h>
59#include <setjmp.h>
60#include <signal.h>
61#include <stdlib.h>
62#include <string.h>
63#include <syslog.h>
64#include <termios.h>
65#include <time.h>
66#include <unistd.h>
67
68#include "gettytab.h"
69#include "pathnames.h"
70#include "extern.h"
71
72/*
73 * Set the amount of running time that getty should accumulate
74 * before deciding that something is wrong and exit.
75 */
76#define GETTY_TIMEOUT	60 /* seconds */
77
78#undef CTRL
79#define CTRL(x)  (x&037)
80
81/* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
82
83#define PPP_FRAME           0x7e  /* PPP Framing character */
84#define PPP_STATION         0xff  /* "All Station" character */
85#define PPP_ESCAPE          0x7d  /* Escape Character */
86#define PPP_CONTROL         0x03  /* PPP Control Field */
87#define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
88#define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
89#define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
90
91struct termios tmode, omode;
92
93int crmod, digit, lower, upper;
94
95char	hostname[MAXHOSTNAMELEN];
96char	name[MAXLOGNAME*3];
97char	dev[] = _PATH_DEV;
98char	ttyn[32];
99
100#define	OBUFSIZ		128
101#define	TABBUFSIZ	512
102
103char	defent[TABBUFSIZ];
104char	tabent[TABBUFSIZ];
105
106char	*env[128];
107
108char partab[] = {
109	0001,0201,0201,0001,0201,0001,0001,0201,
110	0202,0004,0003,0205,0005,0206,0201,0001,
111	0201,0001,0001,0201,0001,0201,0201,0001,
112	0001,0201,0201,0001,0201,0001,0001,0201,
113	0200,0000,0000,0200,0000,0200,0200,0000,
114	0000,0200,0200,0000,0200,0000,0000,0200,
115	0000,0200,0200,0000,0200,0000,0000,0200,
116	0200,0000,0000,0200,0000,0200,0200,0000,
117	0200,0000,0000,0200,0000,0200,0200,0000,
118	0000,0200,0200,0000,0200,0000,0000,0200,
119	0000,0200,0200,0000,0200,0000,0000,0200,
120	0200,0000,0000,0200,0000,0200,0200,0000,
121	0000,0200,0200,0000,0200,0000,0000,0200,
122	0200,0000,0000,0200,0000,0200,0200,0000,
123	0200,0000,0000,0200,0000,0200,0200,0000,
124	0000,0200,0200,0000,0200,0000,0000,0201
125};
126
127#define	ERASE	tmode.c_cc[VERASE]
128#define	KILL	tmode.c_cc[VKILL]
129#define	EOT	tmode.c_cc[VEOF]
130
131static void	dingdong __P((int));
132static int	getname __P((void));
133static void	interrupt __P((int));
134static void	oflush __P((void));
135static void	prompt __P((void));
136static void	putchr __P((int));
137static void	putf __P((const char *));
138static void	putpad __P((const char *));
139static void	puts __P((const char *));
140static void	timeoverrun __P((int));
141static char	*getline __P((int));
142static void	setttymode __P((const char *, int));
143static void	setdefttymode __P((const char *));
144static int	opentty __P((const char *, int));
145
146int		main __P((int, char **));
147
148jmp_buf timeout;
149
150static void
151dingdong(signo)
152	int signo;
153{
154	alarm(0);
155	longjmp(timeout, 1);
156}
157
158jmp_buf	intrupt;
159
160static void
161interrupt(signo)
162	int signo;
163{
164	longjmp(intrupt, 1);
165}
166
167/*
168 * Action to take when getty is running too long.
169 */
170static void
171timeoverrun(signo)
172	int signo;
173{
174
175	syslog(LOG_ERR, "getty exiting due to excessive running time\n");
176	exit(1);
177}
178
179int
180main(argc, argv)
181	int argc;
182	char **argv;
183{
184	extern	char **environ;
185	const char *tname;
186	int first_sleep = 1, first_time = 1;
187	struct rlimit limit;
188	int rval;
189
190	signal(SIGINT, SIG_IGN);
191	signal(SIGQUIT, SIG_IGN);
192
193	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
194	gethostname(hostname, sizeof(hostname));
195	if (hostname[0] == '\0')
196		strcpy(hostname, "Amnesiac");
197
198	/*
199	 * Limit running time to deal with broken or dead lines.
200	 */
201	(void)signal(SIGXCPU, timeoverrun);
202	limit.rlim_max = RLIM_INFINITY;
203	limit.rlim_cur = GETTY_TIMEOUT;
204	(void)setrlimit(RLIMIT_CPU, &limit);
205
206	gettable("default", defent);
207	gendefaults();
208	tname = "default";
209	if (argc > 1)
210		tname = argv[1];
211
212	/*
213	 * The following is a work around for vhangup interactions
214	 * which cause great problems getting window systems started.
215	 * If the tty line is "-", we do the old style getty presuming
216	 * that the file descriptors are already set up for us.
217	 * J. Gettys - MIT Project Athena.
218	 */
219	if (argc <= 2 || strcmp(argv[2], "-") == 0)
220	    strcpy(ttyn, ttyname(STDIN_FILENO));
221	else {
222	    strcpy(ttyn, dev);
223	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
224	    if (strcmp(argv[0], "+") != 0) {
225		chown(ttyn, 0, 0);
226		chmod(ttyn, 0600);
227		revoke(ttyn);
228
229		gettable(tname, tabent);
230
231		/* Init modem sequence has been specified
232		 */
233		if (IC) {
234			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
235				exit(1);
236			setdefttymode(tname);
237			if (getty_chat(IC, CT, DC) > 0) {
238				syslog(LOG_ERR, "modem init problem on %s", ttyn);
239				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
240				exit(1);
241			}
242		}
243
244		if (AC) {
245			int i, rfds;
246			struct timeval timeout;
247
248			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
249				exit(1);
250        		setdefttymode(tname);
251        		rfds = 1 << 0;	/* FD_SET */
252        		timeout.tv_sec = RT;
253        		timeout.tv_usec = 0;
254        		i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
255        			       (fd_set*)NULL, RT ? &timeout : NULL);
256        		if (i < 0) {
257				syslog(LOG_ERR, "select %s: %m", ttyn);
258			} else if (i == 0) {
259				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
260				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
261				exit(0);  /* recycle for init */
262			}
263			i = getty_chat(AC, CT, DC);
264			if (i > 0) {
265				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
266				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
267				exit(1);
268			}
269		} else { /* blocking open */
270			if (!opentty(ttyn, O_RDWR))
271				exit(1);
272		}
273	    }
274	}
275
276	/* Start with default tty settings */
277	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
278		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
279		exit(1);
280	}
281	/*
282	 * Don't rely on the driver too much, and initialize crucial
283	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
284	 * the c_cc[] settings however, the console drivers might wish
285	 * to leave their idea of the preferred VERASE key value
286	 * there.
287	 */
288	tmode.c_iflag = TTYDEF_IFLAG;
289	tmode.c_oflag = TTYDEF_OFLAG;
290	tmode.c_lflag = TTYDEF_LFLAG;
291	tmode.c_cflag = TTYDEF_CFLAG;
292	omode = tmode;
293
294	for (;;) {
295
296		/*
297		 * if a delay was specified then sleep for that
298		 * number of seconds before writing the initial prompt
299		 */
300		if (first_sleep && DE) {
301		    sleep(DE);
302		    /* remove any noise */
303		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
304		}
305		first_sleep = 0;
306
307		setttymode(tname, 0);
308		if (AB) {
309			tname = autobaud();
310			continue;
311		}
312		if (PS) {
313			tname = portselector();
314			continue;
315		}
316		if (CL && *CL)
317			putpad(CL);
318		edithost(HE);
319
320		/* if this is the first time through this, and an
321		   issue file has been given, then send it */
322		if (first_time && IF) {
323			int fd;
324
325			if ((fd = open(IF, O_RDONLY)) != -1) {
326				char * cp;
327
328				while ((cp = getline(fd)) != NULL) {
329					  putf(cp);
330				}
331				close(fd);
332			}
333		}
334		first_time = 0;
335
336		if (IM && *IM)
337			putf(IM);
338		if (setjmp(timeout)) {
339			cfsetispeed(&tmode, B0);
340			cfsetospeed(&tmode, B0);
341			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
342			exit(1);
343		}
344		if (TO) {
345			signal(SIGALRM, dingdong);
346			alarm(TO);
347		}
348		if ((rval = getname()) == 2) {
349			oflush();
350			alarm(0);
351			execle(PP, "ppplogin", ttyn, (char *) 0, env);
352			syslog(LOG_ERR, "%s: %m", PP);
353			exit(1);
354		} else if (rval) {
355			register int i;
356
357			oflush();
358			alarm(0);
359			signal(SIGALRM, SIG_DFL);
360			if (name[0] == '-') {
361				puts("user names may not start with '-'.");
362				continue;
363			}
364			if (!(upper || lower || digit))
365				continue;
366			setflags(2);
367			if (crmod) {
368				tmode.c_iflag |= ICRNL;
369				tmode.c_oflag |= ONLCR;
370			}
371#if REALLY_OLD_TTYS
372			if (upper || UC)
373				tmode.sg_flags |= LCASE;
374			if (lower || LC)
375				tmode.sg_flags &= ~LCASE;
376#endif
377			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
378				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
379				exit(1);
380			}
381			signal(SIGINT, SIG_DFL);
382			for (i = 0; environ[i] != (char *)0; i++)
383				env[i] = environ[i];
384			makeenv(&env[i]);
385
386			limit.rlim_max = RLIM_INFINITY;
387			limit.rlim_cur = RLIM_INFINITY;
388			(void)setrlimit(RLIMIT_CPU, &limit);
389			execle(LO, "login", "-p", name, (char *) 0, env);
390			syslog(LOG_ERR, "%s: %m", LO);
391			exit(1);
392		}
393		alarm(0);
394		signal(SIGALRM, SIG_DFL);
395		signal(SIGINT, SIG_IGN);
396		if (NX && *NX)
397			tname = NX;
398	}
399}
400
401static int
402opentty(const char *ttyn, int flags)
403{
404	int i, j = 0;
405	int failopenlogged = 0;
406
407	while (j < 10 && (i = open(ttyn, flags)) == -1)
408	{
409		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
410			syslog(LOG_ERR, "open %s: %m", ttyn);
411			failopenlogged = 1;
412		}
413		j++;
414		sleep(60);
415	}
416	if (i == -1) {
417		syslog(LOG_ERR, "open %s: %m", ttyn);
418		return 0;
419	}
420	else {
421		login_tty(i);
422		return 1;
423	}
424}
425
426static void
427setdefttymode(tname)
428	const char * tname;
429{
430	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
431		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
432		exit(1);
433	}
434	tmode.c_iflag = TTYDEF_IFLAG;
435        tmode.c_oflag = TTYDEF_OFLAG;
436        tmode.c_lflag = TTYDEF_LFLAG;
437        tmode.c_cflag = TTYDEF_CFLAG;
438        omode = tmode;
439	setttymode(tname, 1);
440}
441
442static void
443setttymode(tname, raw)
444	const char * tname;
445	int raw;
446{
447	int off = 0;
448
449	gettable(tname, tabent);
450	if (OPset || EPset || APset)
451		APset++, OPset++, EPset++;
452	setdefaults();
453	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
454	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
455	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
456
457	if (IS)
458		cfsetispeed(&tmode, speed(IS));
459	else if (SP)
460		cfsetispeed(&tmode, speed(SP));
461	if (OS)
462		cfsetospeed(&tmode, speed(OS));
463	else if (SP)
464		cfsetospeed(&tmode, speed(SP));
465	setflags(0);
466	setchars();
467	if (raw)
468		cfmakeraw(&tmode);
469	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
470		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
471		exit(1);
472	}
473}
474
475
476static int
477getname()
478{
479	register int c;
480	register char *np;
481	unsigned char cs;
482	int ppp_state = 0;
483	int ppp_connection = 0;
484
485	/*
486	 * Interrupt may happen if we use CBREAK mode
487	 */
488	if (setjmp(intrupt)) {
489		signal(SIGINT, SIG_IGN);
490		return (0);
491	}
492	signal(SIGINT, interrupt);
493	setflags(1);
494	prompt();
495	oflush();
496	if (PF > 0) {
497		sleep(PF);
498		PF = 0;
499	}
500	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
501		syslog(LOG_ERR, "%s: %m", ttyn);
502		exit(1);
503	}
504	crmod = digit = lower = upper = 0;
505	np = name;
506	for (;;) {
507		oflush();
508		if (read(STDIN_FILENO, &cs, 1) <= 0)
509			exit(0);
510		if ((c = cs&0177) == 0)
511			return (0);
512
513		/* PPP detection state machine..
514		   Look for sequences:
515		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
516		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
517		   See RFC1662.
518		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
519		   and Erik 'PPP' Olson, <eriko@wrq.com>
520		 */
521
522		if (PP && (cs == PPP_FRAME)) {
523			ppp_state = 1;
524		} else if (ppp_state == 1 && cs == PPP_STATION) {
525			ppp_state = 2;
526		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
527			ppp_state = 3;
528		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
529			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
530			ppp_state = 4;
531		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
532			ppp_state = 5;
533		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
534			ppp_connection = 1;
535			break;
536		} else {
537			ppp_state = 0;
538		}
539
540		if (c == EOT || c == CTRL('d'))
541			exit(1);
542		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
543			putf("\r\n");
544			break;
545		}
546		if (islower(c))
547			lower = 1;
548		else if (isupper(c))
549			upper = 1;
550		else if (c == ERASE || c == '\b' || c == 0177) {
551			if (np > name) {
552				np--;
553				if (cfgetospeed(&tmode) >= 1200)
554					puts("\b \b");
555				else
556					putchr(cs);
557			}
558			continue;
559		} else if (c == KILL || c == CTRL('u')) {
560			putchr('\r');
561			if (cfgetospeed(&tmode) < 1200)
562				putchr('\n');
563			/* this is the way they do it down under ... */
564			else if (np > name)
565				puts("                                     \r");
566			prompt();
567			np = name;
568			continue;
569		} else if (isdigit(c))
570			digit++;
571		if (IG && (c <= ' ' || c > 0176))
572			continue;
573		*np++ = c;
574		putchr(cs);
575	}
576	signal(SIGINT, SIG_IGN);
577	*np = 0;
578	if (c == '\r')
579		crmod = 1;
580	if ((upper && !lower && !LC) || UC)
581		for (np = name; *np; np++)
582			if (isupper(*np))
583				*np = tolower(*np);
584	return (1 + ppp_connection);
585}
586
587static void
588putpad(s)
589	register const char *s;
590{
591	register pad = 0;
592	speed_t ospeed = cfgetospeed(&tmode);
593
594	if (isdigit(*s)) {
595		while (isdigit(*s)) {
596			pad *= 10;
597			pad += *s++ - '0';
598		}
599		pad *= 10;
600		if (*s == '.' && isdigit(s[1])) {
601			pad += s[1] - '0';
602			s += 2;
603		}
604	}
605
606	puts(s);
607	/*
608	 * If no delay needed, or output speed is
609	 * not comprehensible, then don't try to delay.
610	 */
611	if (pad == 0 || ospeed <= 0)
612		return;
613
614	/*
615	 * Round up by a half a character frame, and then do the delay.
616	 * Too bad there are no user program accessible programmed delays.
617	 * Transmitting pad characters slows many terminals down and also
618	 * loads the system.
619	 */
620	pad = (pad * ospeed + 50000) / 100000;
621	while (pad--)
622		putchr(*PC);
623}
624
625static void
626puts(s)
627	register const char *s;
628{
629	while (*s)
630		putchr(*s++);
631}
632
633char	outbuf[OBUFSIZ];
634int	obufcnt = 0;
635
636static void
637putchr(cc)
638	int cc;
639{
640	char c;
641
642	c = cc;
643	if (!NP) {
644		c |= partab[c&0177] & 0200;
645		if (OP)
646			c ^= 0200;
647	}
648	if (!UB) {
649		outbuf[obufcnt++] = c;
650		if (obufcnt >= OBUFSIZ)
651			oflush();
652	} else
653		write(STDOUT_FILENO, &c, 1);
654}
655
656static void
657oflush()
658{
659	if (obufcnt)
660		write(STDOUT_FILENO, outbuf, obufcnt);
661	obufcnt = 0;
662}
663
664static void
665prompt()
666{
667
668	putf(LM);
669	if (CO)
670		putchr('\n');
671}
672
673
674static char *
675getline(fd)
676	int fd;
677{
678	int i = 0;
679	static char linebuf[512];
680
681	/*
682	 * This is certainly slow, but it avoids having to include
683	 * stdio.h unnecessarily. Issue files should be small anyway.
684	 */
685	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
686		if (linebuf[i] == '\n') {
687			/* Don't rely on newline mode, assume raw */
688			linebuf[i++] = '\r';
689			linebuf[i++] = '\n';
690			linebuf[i] = '\0';
691			return linebuf;
692		}
693		++i;
694	}
695	linebuf[i] = '\0';
696	return i ? linebuf : 0;
697}
698
699static void
700putf(cp)
701	register const char *cp;
702{
703	extern char editedhost[];
704	time_t t;
705	char *slash, db[100];
706
707	static struct utsname kerninfo;
708
709	if (!*kerninfo.sysname)
710		uname(&kerninfo);
711
712	while (*cp) {
713		if (*cp != '%') {
714			putchr(*cp++);
715			continue;
716		}
717		switch (*++cp) {
718
719		case 't':
720			slash = strrchr(ttyn, '/');
721			if (slash == (char *) 0)
722				puts(ttyn);
723			else
724				puts(&slash[1]);
725			break;
726
727		case 'h':
728			puts(editedhost);
729			break;
730
731		case 'd': {
732			t = (time_t)0;
733			(void)time(&t);
734			if (Lo)
735				(void)setlocale(LC_TIME, Lo);
736			(void)strftime(db, sizeof(db), "%+", localtime(&t));
737			puts(db);
738			break;
739
740		case 's':
741			puts(kerninfo.sysname);
742			break;
743
744		case 'm':
745			puts(kerninfo.machine);
746			break;
747
748		case 'r':
749			puts(kerninfo.release);
750			break;
751
752		case 'v':
753			puts(kerninfo.version);
754			break;
755		}
756
757		case '%':
758			putchr('%');
759			break;
760		}
761		cp++;
762	}
763}
764