xref: /netbsd-src/libexec/getty/main.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /*	$NetBSD: main.c,v 1.31 2000/01/14 02:10:08 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 
38 #ifndef lint
39 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n");
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
46 #else
47 __RCSID("$NetBSD: main.c,v 1.31 2000/01/14 02:10:08 ad Exp $");
48 #endif
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <termios.h>
54 #include <sys/ioctl.h>
55 #include <sys/resource.h>
56 #include <sys/utsname.h>
57 
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <time.h>
61 #include <ctype.h>
62 #include <fcntl.h>
63 #include <pwd.h>
64 #include <setjmp.h>
65 #include <signal.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <syslog.h>
69 #include <time.h>
70 #include <unistd.h>
71 #include <util.h>
72 #include <limits.h>
73 
74 #include "gettytab.h"
75 #include "pathnames.h"
76 #include "extern.h"
77 
78 extern char *__progname;
79 
80 /*
81  * Set the amount of running time that getty should accumulate
82  * before deciding that something is wrong and exit.
83  */
84 #define GETTY_TIMEOUT	60 /* seconds */
85 
86 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
87 
88 #define PPP_FRAME           0x7e  /* PPP Framing character */
89 #define PPP_STATION         0xff  /* "All Station" character */
90 #define PPP_ESCAPE          0x7d  /* Escape Character */
91 #define PPP_CONTROL         0x03  /* PPP Control Field */
92 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
93 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
94 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
95 
96 struct termios tmode, omode;
97 
98 int crmod, digit, lower, upper;
99 
100 char	hostname[MAXHOSTNAMELEN + 1];
101 struct	utsname kerninfo;
102 char	name[LOGIN_NAME_MAX];
103 char	dev[] = _PATH_DEV;
104 char	ttyn[32];
105 char	lockfile[512];
106 uid_t	ttyowner;
107 
108 #define	OBUFSIZ		128
109 #define	TABBUFSIZ	512
110 
111 char	defent[TABBUFSIZ];
112 char	tabent[TABBUFSIZ];
113 
114 char	*env[128];
115 
116 char partab[] = {
117 	0001,0201,0201,0001,0201,0001,0001,0201,
118 	0202,0004,0003,0205,0005,0206,0201,0001,
119 	0201,0001,0001,0201,0001,0201,0201,0001,
120 	0001,0201,0201,0001,0201,0001,0001,0201,
121 	0200,0000,0000,0200,0000,0200,0200,0000,
122 	0000,0200,0200,0000,0200,0000,0000,0200,
123 	0000,0200,0200,0000,0200,0000,0000,0200,
124 	0200,0000,0000,0200,0000,0200,0200,0000,
125 	0200,0000,0000,0200,0000,0200,0200,0000,
126 	0000,0200,0200,0000,0200,0000,0000,0200,
127 	0000,0200,0200,0000,0200,0000,0000,0200,
128 	0200,0000,0000,0200,0000,0200,0200,0000,
129 	0000,0200,0200,0000,0200,0000,0000,0200,
130 	0200,0000,0000,0200,0000,0200,0200,0000,
131 	0200,0000,0000,0200,0000,0200,0200,0000,
132 	0000,0200,0200,0000,0200,0000,0000,0201
133 };
134 
135 #define	ERASE	tmode.c_cc[VERASE]
136 #define	KILL	tmode.c_cc[VKILL]
137 #define	EOT	tmode.c_cc[VEOF]
138 
139 static void	dingdong __P((int));
140 static void	interrupt __P((int));
141 void		timeoverrun __P((int));
142 
143 jmp_buf timeout;
144 
145 static void
146 dingdong(signo)
147 	int signo;
148 {
149 
150 	alarm(0);
151 	signal(SIGALRM, SIG_DFL);
152 	longjmp(timeout, 1);
153 }
154 
155 jmp_buf	intrupt;
156 
157 static void
158 interrupt(signo)
159 	int signo;
160 {
161 
162 	signal(SIGINT, interrupt);
163 	longjmp(intrupt, 1);
164 }
165 
166 /*
167  * Action to take when getty is running too long.
168  */
169 void
170 timeoverrun(signo)
171 	int signo;
172 {
173 
174 	syslog(LOG_ERR, "getty exiting due to excessive running time");
175 	exit(1);
176 }
177 
178 int		main __P((int, char **));
179 static int	getname __P((void));
180 static void	oflush __P((void));
181 static void	prompt __P((void));
182 static void	putchr __P((int));
183 static void	putf __P((const char *));
184 static void	putpad __P((const char *));
185 static void	xputs __P((const char *));
186 
187 int
188 main(argc, argv)
189 	int argc;
190 	char *argv[];
191 {
192 	extern char **environ;
193 	char *tname;
194 	int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
195 	struct rlimit limit;
196 	struct passwd *pw;
197         int rval;
198 
199 #ifdef __GNUC__
200 	(void)&tname;		/* XXX gcc -Wall */
201 #endif
202 
203 	signal(SIGINT, SIG_IGN);
204 /*
205 	signal(SIGQUIT, SIG_DFL);
206 */
207 	openlog("getty", LOG_ODELAY|LOG_PID, LOG_AUTH);
208 	gethostname(hostname, sizeof(hostname));
209 	hostname[sizeof(hostname) - 1] = '\0';
210 	if (hostname[0] == '\0')
211 		strcpy(hostname, "Amnesiac");
212 	uname(&kerninfo);
213 
214 	if (__progname[0] == 'u' && __progname[1] == 'u')
215 		uugetty = 1;
216 
217 	/*
218 	 * Find id of uucp login (if present) so we can chown tty properly.
219 	 */
220 	if (uugetty && (pw = getpwnam("uucp")))
221 		ttyowner = pw->pw_uid;
222 	else
223 		ttyowner = 0;
224 
225 	/*
226 	 * Limit running time to deal with broken or dead lines.
227 	 */
228 	(void)signal(SIGXCPU, timeoverrun);
229 	limit.rlim_max = RLIM_INFINITY;
230 	limit.rlim_cur = GETTY_TIMEOUT;
231 	(void)setrlimit(RLIMIT_CPU, &limit);
232 
233 	/*
234 	 * The following is a work around for vhangup interactions
235 	 * which cause great problems getting window systems started.
236 	 * If the tty line is "-", we do the old style getty presuming
237 	 * that the file descriptors are already set up for us.
238 	 * J. Gettys - MIT Project Athena.
239 	 */
240 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
241 	    strncpy(ttyn, ttyname(0), 32);
242 	    ttyn[31] = (char)NULL;
243 	}
244 	else {
245 	    int i;
246 
247 	    strcpy(ttyn, dev);
248 	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
249 
250 	    if (uugetty)  {
251 		chown(ttyn, ttyowner, 0);
252 		strcpy(lockfile, _PATH_LOCK);
253 		strncat(lockfile, argv[2], sizeof(lockfile)-sizeof(_PATH_LOCK));
254 		/* wait for lockfiles to go away before we try to open */
255 		if ( pidlock(lockfile, 0, 0, 0) != 0 )  {
256 		    syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
257 		    exit(1);
258 		}
259 		unlink(lockfile);
260 	    }
261 	    if (strcmp(argv[0], "+") != 0) {
262 		chown(ttyn, ttyowner, 0);
263 		chmod(ttyn, 0600);
264 		revoke(ttyn);
265 		if (ttyaction(ttyn, "getty", "root"))
266 			syslog(LOG_ERR,"%s: ttyaction failed", ttyn);
267 		/*
268 		 * Delay the open so DTR stays down long enough to be detected.
269 		 */
270 		sleep(2);
271 		while ((i = open(ttyn, O_RDWR)) == -1) {
272 			if ((repcnt % 10 == 0) &&
273 			    (errno != ENXIO || !failopenlogged)) {
274 				syslog(LOG_ERR, "%s: %m", ttyn);
275 				closelog();
276 				failopenlogged = 1;
277 			}
278 			repcnt++;
279 			sleep(60);
280 		}
281 		if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
282 			syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
283 			exit(1);
284 		}
285 		(void) chown(lockfile, ttyowner, 0);
286 		login_tty(i);
287 	    }
288 	}
289 
290 	/* Start with default tty settings */
291 	if (tcgetattr(0, &tmode) < 0) {
292 		syslog(LOG_ERR, "%s: %m", ttyn);
293 		exit(1);
294 	}
295 	omode = tmode;
296 
297 	gettable("default", defent);
298 	gendefaults();
299 	tname = "default";
300 	if (argc > 1)
301 		tname = argv[1];
302 	for (;;) {
303 		int off;
304 
305 		gettable(tname, tabent);
306 		if (OPset || EPset || APset)
307 			APset++, OPset++, EPset++;
308 		setdefaults();
309 		off = 0;
310 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
311 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
312 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
313 
314 		if (IS)
315 			cfsetispeed(&tmode, IS);
316 		else if (SP)
317 			cfsetispeed(&tmode, SP);
318 		if (OS)
319 			cfsetospeed(&tmode, OS);
320 		else if (SP)
321 			cfsetospeed(&tmode, SP);
322 		setflags(0);
323 		setchars();
324 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
325 			syslog(LOG_ERR, "%s: %m", ttyn);
326 			exit(1);
327 		}
328 		if (AB) {
329 			tname = autobaud();
330 			continue;
331 		}
332 		if (PS) {
333 			tname = portselector();
334 			continue;
335 		}
336 		if (CL && *CL)
337 			putpad(CL);
338 		edithost(HE);
339 
340                 /*
341                  * If this is the first time through this, and an
342                  * issue file has been given, then send it.
343                  */
344 		if (first_time != 0 && IF != NULL) {
345 			char buf[_POSIX2_LINE_MAX];
346 			FILE *fd;
347 
348 			if ((fd = fopen(IF, "r")) != NULL) {
349 				while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
350 					putf(buf);
351 				fclose(fd);
352 			}
353 		}
354 		first_time = 0;
355 
356 		if (IM && *IM)
357 			putf(IM);
358 		oflush();
359 		if (setjmp(timeout)) {
360 			tmode.c_ispeed = tmode.c_ospeed = 0;
361 			(void)tcsetattr(0, TCSANOW, &tmode);
362 			exit(1);
363 		}
364 		if (TO) {
365 			signal(SIGALRM, dingdong);
366 			alarm(TO);
367 		}
368 		if (AL) {
369 			const char *p = AL;
370 			char *q = name;
371 
372 			while (*p && q < &name[sizeof name - 1]) {
373 				if (isupper(*p))
374 					upper = 1;
375 				else if (islower(*p))
376 					lower = 1;
377 				else if (isdigit(*p))
378 					digit++;
379 				*q++ = *p++;
380 			}
381 		} else if ((rval = getname()) == 2) {
382 		        execle(PP, "ppplogin", ttyn, (char *) 0, env);
383 		        syslog(LOG_ERR, "%s: %m", PP);
384 		        exit(1);
385 		}
386 
387 		if (rval || AL) {
388 			int i;
389 
390 			oflush();
391 			alarm(0);
392 			signal(SIGALRM, SIG_DFL);
393 			if (name[0] == '-') {
394 				xputs("user names may not start with '-'.");
395 				continue;
396 			}
397 			if (!(upper || lower || digit))
398 				continue;
399 			setflags(2);
400 			if (crmod) {
401 				tmode.c_iflag |= ICRNL;
402 				tmode.c_oflag |= ONLCR;
403 			}
404 #if XXX
405 			if (upper || UC)
406 				tmode.sg_flags |= LCASE;
407 			if (lower || LC)
408 				tmode.sg_flags &= ~LCASE;
409 #endif
410 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
411 				syslog(LOG_ERR, "%s: %m", ttyn);
412 				exit(1);
413 			}
414 			signal(SIGINT, SIG_DFL);
415 			for (i = 0; environ[i] != (char *)0; i++)
416 				env[i] = environ[i];
417 			makeenv(&env[i]);
418 
419 			limit.rlim_max = RLIM_INFINITY;
420 			limit.rlim_cur = RLIM_INFINITY;
421 			(void)setrlimit(RLIMIT_CPU, &limit);
422 			execle(LO, "login", AL ? "-fp" : "-p", "--", name,
423 			    (char *)0, env);
424 			syslog(LOG_ERR, "%s: %m", LO);
425 			exit(1);
426 		}
427 		alarm(0);
428 		signal(SIGALRM, SIG_DFL);
429 		signal(SIGINT, SIG_IGN);
430 		if (NX && *NX)
431 			tname = NX;
432 		unlink(lockfile);
433 	}
434 }
435 
436 static int
437 getname()
438 {
439 	int c;
440 	char *np;
441 	unsigned char cs;
442 	int ppp_state, ppp_connection;
443 
444 	/*
445 	 * Interrupt may happen if we use CBREAK mode
446 	 */
447 	if (setjmp(intrupt)) {
448 		signal(SIGINT, SIG_IGN);
449 		return (0);
450 	}
451 	signal(SIGINT, interrupt);
452 	setflags(1);
453 	prompt();
454 	if (PF > 0) {
455 		oflush();
456 		sleep(PF);
457 		PF = 0;
458 	}
459 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
460 		syslog(LOG_ERR, "%s: %m", ttyn);
461 		exit(1);
462 	}
463 	crmod = digit = lower = upper = 0;
464         ppp_state = ppp_connection = 0;
465 	np = name;
466 	for (;;) {
467 		oflush();
468 		if (read(STDIN_FILENO, &cs, 1) <= 0)
469 			exit(0);
470 		if ((c = cs&0177) == 0)
471 			return (0);
472 
473 		/*
474 		 * PPP detection state machine..
475 		 * Look for sequences:
476 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
477 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
478 		 * See RFC1662.
479 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
480 		 * and Erik 'PPP' Olson <eriko@wrq.com>
481 		 */
482 		if (PP && cs == PPP_FRAME) {
483 			ppp_state = 1;
484 		} else if (ppp_state == 1 && cs == PPP_STATION) {
485 			ppp_state = 2;
486 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
487 			ppp_state = 3;
488 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
489 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
490 			ppp_state = 4;
491 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
492 			ppp_state = 5;
493 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
494 			ppp_connection = 1;
495 			break;
496 		} else {
497 			ppp_state = 0;
498 		}
499 
500 		if (c == EOT)
501 			exit(1);
502 		if (c == '\r' || c == '\n' ||
503 		    np >= &name[LOGIN_NAME_MAX - 1]) {
504 			*np = '\0';
505 			putf("\r\n");
506 			break;
507 		}
508 		if (islower(c))
509 			lower = 1;
510 		else if (isupper(c))
511 			upper = 1;
512 		else if (c == ERASE || c == '#' || c == '\b') {
513 			if (np > name) {
514 				np--;
515 				if (cfgetospeed(&tmode) >= 1200)
516 					xputs("\b \b");
517 				else
518 					putchr(cs);
519 			}
520 			continue;
521 		} else if (c == KILL || c == '@') {
522 			putchr(cs);
523 			putchr('\r');
524 			if (cfgetospeed(&tmode) < 1200)
525 				putchr('\n');
526 			/* this is the way they do it down under ... */
527 			else if (np > name)
528 				xputs(
529 				    "                                     \r");
530 			prompt();
531 			np = name;
532 			continue;
533 		} else if (isdigit(c))
534 			digit++;
535 		if (IG && (c <= ' ' || c > 0176))
536 			continue;
537 		*np++ = c;
538 		putchr(cs);
539 	}
540 	signal(SIGINT, SIG_IGN);
541 	*np = 0;
542 	if (c == '\r')
543 		crmod = 1;
544 	if ((upper && !lower && !LC) || UC)
545 		for (np = name; *np; np++)
546 			if (isupper(*np))
547 				*np = tolower(*np);
548 	return (1 + ppp_connection);
549 }
550 
551 static void
552 putpad(s)
553 	const char *s;
554 {
555 	int pad = 0;
556 	speed_t ospeed = cfgetospeed(&tmode);
557 
558 	if (isdigit(*s)) {
559 		while (isdigit(*s)) {
560 			pad *= 10;
561 			pad += *s++ - '0';
562 		}
563 		pad *= 10;
564 		if (*s == '.' && isdigit(s[1])) {
565 			pad += s[1] - '0';
566 			s += 2;
567 		}
568 	}
569 
570 	xputs(s);
571 	/*
572 	 * If no delay needed, or output speed is
573 	 * not comprehensible, then don't try to delay.
574 	 */
575 	if (pad == 0 || ospeed <= 0)
576 		return;
577 
578 	/*
579 	 * Round up by a half a character frame, and then do the delay.
580 	 * Too bad there are no user program accessible programmed delays.
581 	 * Transmitting pad characters slows many terminals down and also
582 	 * loads the system.
583 	 */
584 	pad = (pad * ospeed + 50000) / 100000;
585 	while (pad--)
586 		putchr(*PC);
587 }
588 
589 static void
590 xputs(s)
591 	const char *s;
592 {
593 	while (*s)
594 		putchr(*s++);
595 }
596 
597 char	outbuf[OBUFSIZ];
598 int	obufcnt = 0;
599 
600 static void
601 putchr(cc)
602 	int cc;
603 {
604 	char c;
605 
606 	c = cc;
607 	if (!NP) {
608 		c |= partab[c&0177] & 0200;
609 		if (OP)
610 			c ^= 0200;
611 	}
612 	if (!UB) {
613 		outbuf[obufcnt++] = c;
614 		if (obufcnt >= OBUFSIZ)
615 			oflush();
616 	} else
617 		write(STDOUT_FILENO, &c, 1);
618 }
619 
620 static void
621 oflush()
622 {
623 	if (obufcnt)
624 		write(STDOUT_FILENO, outbuf, obufcnt);
625 	obufcnt = 0;
626 }
627 
628 static void
629 prompt()
630 {
631 
632 	putf(LM);
633 	if (CO)
634 		putchr('\n');
635 }
636 
637 static void
638 putf(cp)
639 	const char *cp;
640 {
641 	extern char editedhost[];
642 	time_t t;
643 	char *slash, db[100];
644 
645 	while (*cp) {
646 		if (*cp != '%') {
647 			putchr(*cp++);
648 			continue;
649 		}
650 		switch (*++cp) {
651 
652 		case 't':
653 			slash = strrchr(ttyn, '/');
654 			if (slash == (char *) 0)
655 				xputs(ttyn);
656 			else
657 				xputs(&slash[1]);
658 			break;
659 
660 		case 'h':
661 			xputs(editedhost);
662 			break;
663 
664 		case 'd': {
665 			static char fmt[] = "%l:% %p on %A, %d %B %Y";
666 
667 			fmt[4] = 'M';		/* I *hate* SCCS... */
668 			(void)time(&t);
669 			(void)strftime(db, sizeof(db), fmt, localtime(&t));
670 			xputs(db);
671 			break;
672 
673 		case 's':
674 			xputs(kerninfo.sysname);
675 			break;
676 
677 		case 'm':
678 			xputs(kerninfo.machine);
679 			break;
680 
681 		case 'r':
682 			xputs(kerninfo.release);
683 			break;
684 
685 		case 'v':
686 			xputs(kerninfo.version);
687 			break;
688 		}
689 
690 		case '%':
691 			putchr('%');
692 			break;
693 		}
694 		cp++;
695 	}
696 }
697