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