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