xref: /freebsd/libexec/getty/main.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1980, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";*/
42 static char rcsid[] = "$Id: main.c,v 1.9 1996/05/05 19:01:10 joerg Exp $";
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <sys/resource.h>
49 #include <sys/ttydefaults.h>
50 #include <sys/utsname.h>
51 #include <errno.h>
52 #include <signal.h>
53 #include <fcntl.h>
54 #include <time.h>
55 #include <ctype.h>
56 #include <fcntl.h>
57 #include <libutil.h>
58 #include <locale.h>
59 #include <setjmp.h>
60 #include <signal.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <termios.h>
65 #include <time.h>
66 #include <unistd.h>
67 
68 #include "gettytab.h"
69 #include "pathnames.h"
70 #include "extern.h"
71 
72 /*
73  * Set the amount of running time that getty should accumulate
74  * before deciding that something is wrong and exit.
75  */
76 #define GETTY_TIMEOUT	60 /* seconds */
77 
78 #undef CTRL
79 #define CTRL(x)  (x&037)
80 
81 struct termios tmode, omode;
82 
83 int crmod, digit, lower, upper;
84 
85 char	hostname[MAXHOSTNAMELEN];
86 struct utsname kerninfo;
87 char	name[16];
88 char	dev[] = _PATH_DEV;
89 char	ttyn[32];
90 
91 #define	OBUFSIZ		128
92 #define	TABBUFSIZ	512
93 
94 char	defent[TABBUFSIZ];
95 char	tabent[TABBUFSIZ];
96 
97 char	*env[128];
98 
99 char partab[] = {
100 	0001,0201,0201,0001,0201,0001,0001,0201,
101 	0202,0004,0003,0205,0005,0206,0201,0001,
102 	0201,0001,0001,0201,0001,0201,0201,0001,
103 	0001,0201,0201,0001,0201,0001,0001,0201,
104 	0200,0000,0000,0200,0000,0200,0200,0000,
105 	0000,0200,0200,0000,0200,0000,0000,0200,
106 	0000,0200,0200,0000,0200,0000,0000,0200,
107 	0200,0000,0000,0200,0000,0200,0200,0000,
108 	0200,0000,0000,0200,0000,0200,0200,0000,
109 	0000,0200,0200,0000,0200,0000,0000,0200,
110 	0000,0200,0200,0000,0200,0000,0000,0200,
111 	0200,0000,0000,0200,0000,0200,0200,0000,
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,0201
116 };
117 
118 #define	ERASE	tmode.c_cc[VERASE]
119 #define	KILL	tmode.c_cc[VKILL]
120 #define	EOT	tmode.c_cc[VEOF]
121 
122 jmp_buf timeout;
123 
124 static void	dingdong __P((int));
125 static int	getname __P((void));
126 static void	interrupt __P((int));
127 static void	oflush __P((void));
128 static void	prompt __P((void));
129 static void	putchr __P((int));
130 static void	putf __P((const char *));
131 static void	putpad __P((const char *));
132 static void	puts __P((const char *));
133 static void	timeoverrun __P((int));
134 
135 int		main __P((int, char **));
136 
137 static void
138 dingdong(signo)
139 	int signo;
140 {
141 	alarm(0);
142 	longjmp(timeout, 1);
143 }
144 
145 jmp_buf	intrupt;
146 
147 static void
148 interrupt(signo)
149 	int signo;
150 {
151 	longjmp(intrupt, 1);
152 }
153 
154 /*
155  * Action to take when getty is running too long.
156  */
157 static void
158 timeoverrun(signo)
159 	int signo;
160 {
161 
162 	syslog(LOG_ERR, "getty exiting due to excessive running time\n");
163 	exit(1);
164 }
165 
166 int
167 main(argc, argv)
168 	int argc;
169 	char **argv;
170 {
171 	extern	char **environ;
172 	const char *tname;
173 	int repcnt = 0, failopenlogged = 0;
174 	struct rlimit limit;
175 
176 	signal(SIGINT, SIG_IGN);
177 	signal(SIGQUIT, SIG_IGN);
178 
179 	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
180 	gethostname(hostname, sizeof(hostname));
181 	if (hostname[0] == '\0')
182 		strcpy(hostname, "Amnesiac");
183 
184 	/*
185 	 * Limit running time to deal with broken or dead lines.
186 	 */
187 	(void)signal(SIGXCPU, timeoverrun);
188 	limit.rlim_max = RLIM_INFINITY;
189 	limit.rlim_cur = GETTY_TIMEOUT;
190 	(void)setrlimit(RLIMIT_CPU, &limit);
191 
192 	/*
193 	 * The following is a work around for vhangup interactions
194 	 * which cause great problems getting window systems started.
195 	 * If the tty line is "-", we do the old style getty presuming
196 	 * that the file descriptors are already set up for us.
197 	 * J. Gettys - MIT Project Athena.
198 	 */
199 	if (argc <= 2 || strcmp(argv[2], "-") == 0)
200 	    strcpy(ttyn, ttyname(0));
201 	else {
202 	    int i;
203 
204 	    strcpy(ttyn, dev);
205 	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
206 	    if (strcmp(argv[0], "+") != 0) {
207 		chown(ttyn, 0, 0);
208 		chmod(ttyn, 0600);
209 		revoke(ttyn);
210 		while ((i = open(ttyn, O_RDWR)) == -1) {
211 			if ((repcnt % 10 == 0) &&
212 			    (errno != ENXIO || !failopenlogged)) {
213 				syslog(LOG_ERR, "%s: %m", ttyn);
214 				closelog();
215 				failopenlogged = 1;
216 			}
217 			repcnt++;
218 			sleep(60);
219 		}
220 		login_tty(i);
221 	    }
222 	}
223 
224 	/* Start with default tty settings */
225 	if (tcgetattr(0, &tmode) < 0) {
226 		syslog(LOG_ERR, "%s: %m", ttyn);
227 		exit(1);
228 	}
229 	/*
230 	 * Don't rely on the driver too much, and initialize crucial
231 	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
232 	 * the c_cc[] settings however, the console drivers might wish
233 	 * to leave their idea of the preferred VERASE key value
234 	 * there.
235 	 */
236 	tmode.c_iflag = TTYDEF_IFLAG;
237 	tmode.c_oflag = TTYDEF_OFLAG;
238 	tmode.c_lflag = TTYDEF_LFLAG;
239 	tmode.c_cflag = TTYDEF_CFLAG;
240 	omode = tmode;
241 
242 	gettable("default", defent);
243 	gendefaults();
244 	tname = "default";
245 	if (argc > 1)
246 		tname = argv[1];
247 	for (;;) {
248 		int off = 0;
249 
250 		gettable(tname, tabent);
251 		if (OPset || EPset || APset)
252 			APset++, OPset++, EPset++;
253 		setdefaults();
254 		off = 0;
255 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
256 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
257 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
258 
259 		if (IS)
260 			cfsetispeed(&tmode, speed(IS));
261 		else if (SP)
262 			cfsetispeed(&tmode, speed(SP));
263 		if (OS)
264 			cfsetospeed(&tmode, speed(OS));
265 		else if (SP)
266 			cfsetospeed(&tmode, speed(SP));
267 		setflags(0);
268 		setchars();
269 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
270 			syslog(LOG_ERR, "%s: %m", ttyn);
271 			exit(1);
272 		}
273 		if (AB) {
274 			tname = autobaud();
275 			continue;
276 		}
277 		if (PS) {
278 			tname = portselector();
279 			continue;
280 		}
281 		if (CL && *CL)
282 			putpad(CL);
283 		edithost(HE);
284 		if (IM && *IM)
285 			putf(IM);
286 		if (setjmp(timeout)) {
287 			cfsetispeed(&tmode, B0);
288 			cfsetospeed(&tmode, B0);
289 			(void)tcsetattr(0, TCSANOW, &tmode);
290 			exit(1);
291 		}
292 		if (TO) {
293 			signal(SIGALRM, dingdong);
294 			alarm(TO);
295 		}
296 		if (getname()) {
297 			register int i;
298 
299 			oflush();
300 			alarm(0);
301 			signal(SIGALRM, SIG_DFL);
302 			if (name[0] == '-') {
303 				puts("user names may not start with '-'.");
304 				continue;
305 			}
306 			if (!(upper || lower || digit))
307 				continue;
308 			setflags(2);
309 			if (crmod) {
310 				tmode.c_iflag |= ICRNL;
311 				tmode.c_oflag |= ONLCR;
312 			}
313 #if REALLY_OLD_TTYS
314 			if (upper || UC)
315 				tmode.sg_flags |= LCASE;
316 			if (lower || LC)
317 				tmode.sg_flags &= ~LCASE;
318 #endif
319 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
320 				syslog(LOG_ERR, "%s: %m", ttyn);
321 				exit(1);
322 			}
323 			signal(SIGINT, SIG_DFL);
324 			for (i = 0; environ[i] != (char *)0; i++)
325 				env[i] = environ[i];
326 			makeenv(&env[i]);
327 
328 			limit.rlim_max = RLIM_INFINITY;
329 			limit.rlim_cur = RLIM_INFINITY;
330 			(void)setrlimit(RLIMIT_CPU, &limit);
331 			execle(LO, "login", "-p", name, (char *) 0, env);
332 			syslog(LOG_ERR, "%s: %m", LO);
333 			exit(1);
334 		}
335 		alarm(0);
336 		signal(SIGALRM, SIG_DFL);
337 		signal(SIGINT, SIG_IGN);
338 		if (NX && *NX)
339 			tname = NX;
340 	}
341 }
342 
343 static int
344 getname()
345 {
346 	register int c;
347 	register char *np;
348 	char cs;
349 
350 	/*
351 	 * Interrupt may happen if we use CBREAK mode
352 	 */
353 	if (setjmp(intrupt)) {
354 		signal(SIGINT, SIG_IGN);
355 		return (0);
356 	}
357 	signal(SIGINT, interrupt);
358 	setflags(1);
359 	prompt();
360 	oflush();
361 	if (PF > 0) {
362 		sleep(PF);
363 		PF = 0;
364 	}
365 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
366 		syslog(LOG_ERR, "%s: %m", ttyn);
367 		exit(1);
368 	}
369 	crmod = digit = lower = upper = 0;
370 	np = name;
371 	for (;;) {
372 		oflush();
373 		if (read(STDIN_FILENO, &cs, 1) <= 0)
374 			exit(0);
375 		if ((c = cs&0177) == 0)
376 			return (0);
377 		if (c == EOT || c == CTRL('d'))
378 			exit(1);
379 		if (c == '\r' || c == '\n' || np >= &name[sizeof name]) {
380 			putf("\r\n");
381 			break;
382 		}
383 		if (islower(c))
384 			lower = 1;
385 		else if (isupper(c))
386 			upper = 1;
387 		else if (c == ERASE || c == '\b' || c == 0177) {
388 			if (np > name) {
389 				np--;
390 				if (cfgetospeed(&tmode) >= 1200)
391 					puts("\b \b");
392 				else
393 					putchr(cs);
394 			}
395 			continue;
396 		} else if (c == KILL || c == CTRL('u')) {
397 			putchr('\r');
398 			if (cfgetospeed(&tmode) < 1200)
399 				putchr('\n');
400 			/* this is the way they do it down under ... */
401 			else if (np > name)
402 				puts("                                     \r");
403 			prompt();
404 			np = name;
405 			continue;
406 		} else if (isdigit(c))
407 			digit++;
408 		if (IG && (c <= ' ' || c > 0176))
409 			continue;
410 		*np++ = c;
411 		putchr(cs);
412 	}
413 	signal(SIGINT, SIG_IGN);
414 	*np = 0;
415 	if (c == '\r')
416 		crmod = 1;
417 	if ((upper && !lower && !LC) || UC)
418 		for (np = name; *np; np++)
419 			if (isupper(*np))
420 				*np = tolower(*np);
421 	return (1);
422 }
423 
424 static void
425 putpad(s)
426 	register const char *s;
427 {
428 	register pad = 0;
429 	speed_t ospeed = cfgetospeed(&tmode);
430 
431 	if (isdigit(*s)) {
432 		while (isdigit(*s)) {
433 			pad *= 10;
434 			pad += *s++ - '0';
435 		}
436 		pad *= 10;
437 		if (*s == '.' && isdigit(s[1])) {
438 			pad += s[1] - '0';
439 			s += 2;
440 		}
441 	}
442 
443 	puts(s);
444 	/*
445 	 * If no delay needed, or output speed is
446 	 * not comprehensible, then don't try to delay.
447 	 */
448 	if (pad == 0 || ospeed <= 0)
449 		return;
450 
451 	/*
452 	 * Round up by a half a character frame, and then do the delay.
453 	 * Too bad there are no user program accessible programmed delays.
454 	 * Transmitting pad characters slows many terminals down and also
455 	 * loads the system.
456 	 */
457 	pad = (pad * ospeed + 50000) / 100000;
458 	while (pad--)
459 		putchr(*PC);
460 }
461 
462 static void
463 puts(s)
464 	register const char *s;
465 {
466 	while (*s)
467 		putchr(*s++);
468 }
469 
470 char	outbuf[OBUFSIZ];
471 int	obufcnt = 0;
472 
473 static void
474 putchr(cc)
475 	int cc;
476 {
477 	char c;
478 
479 	c = cc;
480 	if (!NP) {
481 		c |= partab[c&0177] & 0200;
482 		if (OP)
483 			c ^= 0200;
484 	}
485 	if (!UB) {
486 		outbuf[obufcnt++] = c;
487 		if (obufcnt >= OBUFSIZ)
488 			oflush();
489 	} else
490 		write(STDOUT_FILENO, &c, 1);
491 }
492 
493 static void
494 oflush()
495 {
496 	if (obufcnt)
497 		write(STDOUT_FILENO, outbuf, obufcnt);
498 	obufcnt = 0;
499 }
500 
501 static void
502 prompt()
503 {
504 
505 	putf(LM);
506 	if (CO)
507 		putchr('\n');
508 }
509 
510 static void
511 putf(cp)
512 	register const char *cp;
513 {
514 	extern char editedhost[];
515 	time_t t;
516 	char *slash, db[100];
517 
518 	while (*cp) {
519 		if (*cp != '%') {
520 			putchr(*cp++);
521 			continue;
522 		}
523 		switch (*++cp) {
524 
525 		case 't':
526 			slash = strrchr(ttyn, '/');
527 			if (slash == (char *) 0)
528 				puts(ttyn);
529 			else
530 				puts(&slash[1]);
531 			break;
532 
533 		case 'h':
534 			puts(editedhost);
535 			break;
536 
537 		case 'd': {
538 			t = (time_t)0;
539 			(void)time(&t);
540 			if (Lo)
541 				(void)setlocale(LC_TIME, Lo);
542 			(void)strftime(db, sizeof(db), "%+", localtime(&t));
543 			puts(db);
544 			break;
545 
546 		case 's':
547 			puts(kerninfo.sysname);
548 			break;
549 
550 		case 'm':
551 			puts(kerninfo.machine);
552 			break;
553 
554 		case 'r':
555 			puts(kerninfo.release);
556 			break;
557 
558 		case 'v':
559 			puts(kerninfo.version);
560 			break;
561 		}
562 
563 		case '%':
564 			putchr('%');
565 			break;
566 		}
567 		cp++;
568 	}
569 }
570