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