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