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