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