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