main.c revision 22491
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[] = "$FreeBSD: head/libexec/getty/main.c 22491 1997-02-09 16:12:08Z davidn $";
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 repcnt = 0, failopenlogged = 0;
187	int first_sleep = 1, first_time = 1;
188	struct rlimit limit;
189	int rval;
190
191	signal(SIGINT, SIG_IGN);
192	signal(SIGQUIT, SIG_IGN);
193
194	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
195	gethostname(hostname, sizeof(hostname));
196	if (hostname[0] == '\0')
197		strcpy(hostname, "Amnesiac");
198
199	/*
200	 * Limit running time to deal with broken or dead lines.
201	 */
202	(void)signal(SIGXCPU, timeoverrun);
203	limit.rlim_max = RLIM_INFINITY;
204	limit.rlim_cur = GETTY_TIMEOUT;
205	(void)setrlimit(RLIMIT_CPU, &limit);
206
207	gettable("default", defent);
208	gendefaults();
209	tname = "default";
210	if (argc > 1)
211		tname = argv[1];
212
213	/*
214	 * The following is a work around for vhangup interactions
215	 * which cause great problems getting window systems started.
216	 * If the tty line is "-", we do the old style getty presuming
217	 * that the file descriptors are already set up for us.
218	 * J. Gettys - MIT Project Athena.
219	 */
220	if (argc <= 2 || strcmp(argv[2], "-") == 0)
221	    strcpy(ttyn, ttyname(STDIN_FILENO));
222	else {
223	    int i;
224
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			execle(PP, "ppplogin", ttyn, (char *) 0, env);
353			syslog(LOG_ERR, "%s: %m", PP);
354			exit(1);
355		} else if (rval) {
356			register int i;
357
358			oflush();
359			alarm(0);
360			signal(SIGALRM, SIG_DFL);
361			if (name[0] == '-') {
362				puts("user names may not start with '-'.");
363				continue;
364			}
365			if (!(upper || lower || digit))
366				continue;
367			setflags(2);
368			if (crmod) {
369				tmode.c_iflag |= ICRNL;
370				tmode.c_oflag |= ONLCR;
371			}
372#if REALLY_OLD_TTYS
373			if (upper || UC)
374				tmode.sg_flags |= LCASE;
375			if (lower || LC)
376				tmode.sg_flags &= ~LCASE;
377#endif
378			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
379				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
380				exit(1);
381			}
382			signal(SIGINT, SIG_DFL);
383			for (i = 0; environ[i] != (char *)0; i++)
384				env[i] = environ[i];
385			makeenv(&env[i]);
386
387			limit.rlim_max = RLIM_INFINITY;
388			limit.rlim_cur = RLIM_INFINITY;
389			(void)setrlimit(RLIMIT_CPU, &limit);
390			execle(LO, "login", "-p", name, (char *) 0, env);
391			syslog(LOG_ERR, "%s: %m", LO);
392			exit(1);
393		}
394		alarm(0);
395		signal(SIGALRM, SIG_DFL);
396		signal(SIGINT, SIG_IGN);
397		if (NX && *NX)
398			tname = NX;
399	}
400}
401
402static int
403opentty(const char *ttyn, int flags)
404{
405	int i, j = 0;
406	int failopenlogged = 0;
407
408	while (j < 10 && (i = open(ttyn, flags)) == -1)
409	{
410		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
411			syslog(LOG_ERR, "open %s: %m", ttyn);
412			failopenlogged = 1;
413		}
414		j++;
415		sleep(60);
416	}
417	if (i == -1) {
418		syslog(LOG_ERR, "open %s: %m", ttyn);
419		return 0;
420	}
421	else {
422		login_tty(i);
423		return 1;
424	}
425}
426
427static void
428setdefttymode(tname)
429	const char * tname;
430{
431	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
432		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
433		exit(1);
434	}
435	tmode.c_iflag = TTYDEF_IFLAG;
436        tmode.c_oflag = TTYDEF_OFLAG;
437        tmode.c_lflag = TTYDEF_LFLAG;
438        tmode.c_cflag = TTYDEF_CFLAG;
439        omode = tmode;
440	setttymode(tname, 1);
441}
442
443static void
444setttymode(tname, raw)
445	const char * tname;
446	int raw;
447{
448	int off = 0;
449
450	gettable(tname, tabent);
451	if (OPset || EPset || APset)
452		APset++, OPset++, EPset++;
453	setdefaults();
454	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
455	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
456	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
457
458	if (IS)
459		cfsetispeed(&tmode, speed(IS));
460	else if (SP)
461		cfsetispeed(&tmode, speed(SP));
462	if (OS)
463		cfsetospeed(&tmode, speed(OS));
464	else if (SP)
465		cfsetospeed(&tmode, speed(SP));
466	setflags(0);
467	setchars();
468	if (raw)
469		cfmakeraw(&tmode);
470	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
471		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
472		exit(1);
473	}
474}
475
476
477static int
478getname()
479{
480	register int c;
481	register char *np;
482	unsigned char cs;
483	int ppp_state = 0;
484	int ppp_connection = 0;
485
486	/*
487	 * Interrupt may happen if we use CBREAK mode
488	 */
489	if (setjmp(intrupt)) {
490		signal(SIGINT, SIG_IGN);
491		return (0);
492	}
493	signal(SIGINT, interrupt);
494	setflags(1);
495	prompt();
496	oflush();
497	if (PF > 0) {
498		sleep(PF);
499		PF = 0;
500	}
501	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
502		syslog(LOG_ERR, "%s: %m", ttyn);
503		exit(1);
504	}
505	crmod = digit = lower = upper = 0;
506	np = name;
507	for (;;) {
508		oflush();
509		if (read(STDIN_FILENO, &cs, 1) <= 0)
510			exit(0);
511		if ((c = cs&0177) == 0)
512			return (0);
513
514		/* PPP detection state machine..
515		   Look for sequences:
516		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
517		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
518		   See RFC1662.
519		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
520		   and Erik 'PPP' Olson, <eriko@wrq.com>
521		 */
522
523		if (PP && (cs == PPP_FRAME)) {
524			ppp_state = 1;
525		} else if (ppp_state == 1 && cs == PPP_STATION) {
526			ppp_state = 2;
527		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
528			ppp_state = 3;
529		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
530			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
531			ppp_state = 4;
532		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
533			ppp_state = 5;
534		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
535			ppp_connection = 1;
536			break;
537		} else {
538			ppp_state = 0;
539		}
540
541		if (c == EOT || c == CTRL('d'))
542			exit(1);
543		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
544			putf("\r\n");
545			break;
546		}
547		if (islower(c))
548			lower = 1;
549		else if (isupper(c))
550			upper = 1;
551		else if (c == ERASE || c == '\b' || c == 0177) {
552			if (np > name) {
553				np--;
554				if (cfgetospeed(&tmode) >= 1200)
555					puts("\b \b");
556				else
557					putchr(cs);
558			}
559			continue;
560		} else if (c == KILL || c == CTRL('u')) {
561			putchr('\r');
562			if (cfgetospeed(&tmode) < 1200)
563				putchr('\n');
564			/* this is the way they do it down under ... */
565			else if (np > name)
566				puts("                                     \r");
567			prompt();
568			np = name;
569			continue;
570		} else if (isdigit(c))
571			digit++;
572		if (IG && (c <= ' ' || c > 0176))
573			continue;
574		*np++ = c;
575		putchr(cs);
576	}
577	signal(SIGINT, SIG_IGN);
578	*np = 0;
579	if (c == '\r')
580		crmod = 1;
581	if ((upper && !lower && !LC) || UC)
582		for (np = name; *np; np++)
583			if (isupper(*np))
584				*np = tolower(*np);
585	return (1 + ppp_connection);
586}
587
588static void
589putpad(s)
590	register const char *s;
591{
592	register pad = 0;
593	speed_t ospeed = cfgetospeed(&tmode);
594
595	if (isdigit(*s)) {
596		while (isdigit(*s)) {
597			pad *= 10;
598			pad += *s++ - '0';
599		}
600		pad *= 10;
601		if (*s == '.' && isdigit(s[1])) {
602			pad += s[1] - '0';
603			s += 2;
604		}
605	}
606
607	puts(s);
608	/*
609	 * If no delay needed, or output speed is
610	 * not comprehensible, then don't try to delay.
611	 */
612	if (pad == 0 || ospeed <= 0)
613		return;
614
615	/*
616	 * Round up by a half a character frame, and then do the delay.
617	 * Too bad there are no user program accessible programmed delays.
618	 * Transmitting pad characters slows many terminals down and also
619	 * loads the system.
620	 */
621	pad = (pad * ospeed + 50000) / 100000;
622	while (pad--)
623		putchr(*PC);
624}
625
626static void
627puts(s)
628	register const char *s;
629{
630	while (*s)
631		putchr(*s++);
632}
633
634char	outbuf[OBUFSIZ];
635int	obufcnt = 0;
636
637static void
638putchr(cc)
639	int cc;
640{
641	char c;
642
643	c = cc;
644	if (!NP) {
645		c |= partab[c&0177] & 0200;
646		if (OP)
647			c ^= 0200;
648	}
649	if (!UB) {
650		outbuf[obufcnt++] = c;
651		if (obufcnt >= OBUFSIZ)
652			oflush();
653	} else
654		write(STDOUT_FILENO, &c, 1);
655}
656
657static void
658oflush()
659{
660	if (obufcnt)
661		write(STDOUT_FILENO, outbuf, obufcnt);
662	obufcnt = 0;
663}
664
665static void
666prompt()
667{
668
669	putf(LM);
670	if (CO)
671		putchr('\n');
672}
673
674
675static char *
676getline(fd)
677	int fd;
678{
679	int i = 0;
680	static char linebuf[512];
681
682	/*
683	 * This is certainly slow, but it avoids having to include
684	 * stdio.h unnecessarily. Issue files should be small anyway.
685	 */
686	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
687		if (linebuf[i] == '\n') {
688			/* Don't rely on newline mode, assume raw */
689			linebuf[i++] = '\r';
690			linebuf[i++] = '\n';
691			linebuf[i] = '\0';
692			return linebuf;
693		}
694		++i;
695	}
696	linebuf[i] = '\0';
697	return i ? linebuf : 0;
698}
699
700static void
701putf(cp)
702	register const char *cp;
703{
704	extern char editedhost[];
705	time_t t;
706	char *slash, db[100];
707
708	static struct utsname kerninfo;
709
710	if (!*kerninfo.sysname)
711		uname(&kerninfo);
712
713	while (*cp) {
714		if (*cp != '%') {
715			putchr(*cp++);
716			continue;
717		}
718		switch (*++cp) {
719
720		case 't':
721			slash = strrchr(ttyn, '/');
722			if (slash == (char *) 0)
723				puts(ttyn);
724			else
725				puts(&slash[1]);
726			break;
727
728		case 'h':
729			puts(editedhost);
730			break;
731
732		case 'd': {
733			t = (time_t)0;
734			(void)time(&t);
735			if (Lo)
736				(void)setlocale(LC_TIME, Lo);
737			(void)strftime(db, sizeof(db), "%+", localtime(&t));
738			puts(db);
739			break;
740
741		case 's':
742			puts(kerninfo.sysname);
743			break;
744
745		case 'm':
746			puts(kerninfo.machine);
747			break;
748
749		case 'r':
750			puts(kerninfo.release);
751			break;
752
753		case 'v':
754			puts(kerninfo.version);
755			break;
756		}
757
758		case '%':
759			putchr('%');
760			break;
761		}
762		cp++;
763	}
764}
765