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