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