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