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