xref: /freebsd/libexec/getty/main.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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[] = "@(#)main.c	8.1 (Berkeley) 6/20/93";
42 #endif /* not lint */
43 
44 #define USE_OLD_TTY
45 
46 #include <sys/param.h>
47 #include <sys/stat.h>
48 #include <sys/resource.h>
49 
50 #include <ctype.h>
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <setjmp.h>
54 #include <sgtty.h>
55 #include <signal.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <time.h>
60 #include <unistd.h>
61 
62 #include "gettytab.h"
63 #include "pathnames.h"
64 #include "extern.h"
65 
66 /*
67  * Set the amount of running time that getty should accumulate
68  * before deciding that something is wrong and exit.
69  */
70 #define GETTY_TIMEOUT	60 /* seconds */
71 
72 struct	sgttyb tmode = {
73 	0, 0, CERASE, CKILL, 0
74 };
75 struct	tchars tc = {
76 	CINTR, CQUIT, CSTART,
77 	CSTOP, CEOF, CBRK,
78 };
79 struct	ltchars ltc = {
80 	CSUSP, CDSUSP, CRPRNT,
81 	CFLUSH, CWERASE, CLNEXT
82 };
83 
84 int crmod, digit, lower, upper;
85 
86 char	hostname[MAXHOSTNAMELEN];
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.sg_erase
121 #define	KILL	tmode.sg_kill
122 #define	EOT	tc.t_eofc
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;
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_AUTH);
181 	gethostname(hostname, sizeof(hostname));
182 	if (hostname[0] == '\0')
183 		strcpy(hostname, "Amnesiac");
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 	    strcpy(ttyn, ttyname(0));
202 	else {
203 	    int i;
204 
205 	    strcpy(ttyn, dev);
206 	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
207 	    if (strcmp(argv[0], "+") != 0) {
208 		chown(ttyn, 0, 0);
209 		chmod(ttyn, 0600);
210 		revoke(ttyn);
211 		/*
212 		 * Delay the open so DTR stays down long enough to be detected.
213 		 */
214 		sleep(2);
215 		while ((i = open(ttyn, O_RDWR)) == -1) {
216 			if (repcnt % 10 == 0) {
217 				syslog(LOG_ERR, "%s: %m", ttyn);
218 				closelog();
219 			}
220 			repcnt++;
221 			sleep(60);
222 		}
223 		login_tty(i);
224 	    }
225 	}
226 
227 	gettable("default", defent);
228 	gendefaults();
229 	tname = "default";
230 	if (argc > 1)
231 		tname = argv[1];
232 	for (;;) {
233 		int off;
234 
235 		gettable(tname, tabent);
236 		if (OPset || EPset || APset)
237 			APset++, OPset++, EPset++;
238 		setdefaults();
239 		off = 0;
240 		ioctl(0, TIOCFLUSH, &off);	/* clear out the crap */
241 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
242 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
243 		if (IS)
244 			tmode.sg_ispeed = speed(IS);
245 		else if (SP)
246 			tmode.sg_ispeed = speed(SP);
247 		if (OS)
248 			tmode.sg_ospeed = speed(OS);
249 		else if (SP)
250 			tmode.sg_ospeed = speed(SP);
251 		tmode.sg_flags = setflags(0);
252 		ioctl(0, TIOCSETP, &tmode);
253 		setchars();
254 		ioctl(0, TIOCSETC, &tc);
255 		if (HC)
256 			ioctl(0, TIOCHPCL, 0);
257 		if (AB) {
258 			extern char *autobaud();
259 
260 			tname = autobaud();
261 			continue;
262 		}
263 		if (PS) {
264 			tname = portselector();
265 			continue;
266 		}
267 		if (CL && *CL)
268 			putpad(CL);
269 		edithost(HE);
270 		if (IM && *IM)
271 			putf(IM);
272 		if (setjmp(timeout)) {
273 			tmode.sg_ispeed = tmode.sg_ospeed = 0;
274 			ioctl(0, TIOCSETP, &tmode);
275 			exit(1);
276 		}
277 		if (TO) {
278 			signal(SIGALRM, dingdong);
279 			alarm(TO);
280 		}
281 		if (getname()) {
282 			register int i;
283 
284 			oflush();
285 			alarm(0);
286 			signal(SIGALRM, SIG_DFL);
287 			if (name[0] == '-') {
288 				puts("user names may not start with '-'.");
289 				continue;
290 			}
291 			if (!(upper || lower || digit))
292 				continue;
293 			allflags = setflags(2);
294 			tmode.sg_flags = allflags & 0xffff;
295 			allflags >>= 16;
296 			if (crmod || NL)
297 				tmode.sg_flags |= CRMOD;
298 			if (upper || UC)
299 				tmode.sg_flags |= LCASE;
300 			if (lower || LC)
301 				tmode.sg_flags &= ~LCASE;
302 			ioctl(0, TIOCSETP, &tmode);
303 			ioctl(0, TIOCSLTC, &ltc);
304 			ioctl(0, TIOCLSET, &allflags);
305 			signal(SIGINT, SIG_DFL);
306 			for (i = 0; environ[i] != (char *)0; i++)
307 				env[i] = environ[i];
308 			makeenv(&env[i]);
309 
310 			/*
311 			 * this is what login was doing anyway.
312 			 * soon we rewrite getty completely.
313 			 */
314 			set_ttydefaults(0);
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 	tmode.sg_flags = setflags(0);
346 	ioctl(0, TIOCSETP, &tmode);
347 	tmode.sg_flags = setflags(1);
348 	prompt();
349 	if (PF > 0) {
350 		oflush();
351 		sleep(PF);
352 		PF = 0;
353 	}
354 	ioctl(0, TIOCSETP, &tmode);
355 	crmod = digit = lower = upper = 0;
356 	np = name;
357 	for (;;) {
358 		oflush();
359 		if (read(STDIN_FILENO, &cs, 1) <= 0)
360 			exit(0);
361 		if ((c = cs&0177) == 0)
362 			return (0);
363 		if (c == EOT)
364 			exit(1);
365 		if (c == '\r' || c == '\n' || np >= &name[sizeof name]) {
366 			putf("\r\n");
367 			break;
368 		}
369 		if (islower(c))
370 			lower = 1;
371 		else if (isupper(c))
372 			upper = 1;
373 		else if (c == ERASE || c == '#' || c == '\b') {
374 			if (np > name) {
375 				np--;
376 				if (tmode.sg_ospeed >= B1200)
377 					puts("\b \b");
378 				else
379 					putchr(cs);
380 			}
381 			continue;
382 		} else if (c == KILL || c == '@') {
383 			putchr(cs);
384 			putchr('\r');
385 			if (tmode.sg_ospeed < B1200)
386 				putchr('\n');
387 			/* this is the way they do it down under ... */
388 			else if (np > name)
389 				puts("                                     \r");
390 			prompt();
391 			np = name;
392 			continue;
393 		} else if (isdigit(c))
394 			digit++;
395 		if (IG && (c <= ' ' || c > 0176))
396 			continue;
397 		*np++ = c;
398 		putchr(cs);
399 	}
400 	signal(SIGINT, SIG_IGN);
401 	*np = 0;
402 	if (c == '\r')
403 		crmod = 1;
404 	if (upper && !lower && !LC || UC)
405 		for (np = name; *np; np++)
406 			if (isupper(*np))
407 				*np = tolower(*np);
408 	return (1);
409 }
410 
411 static
412 short	tmspc10[] = {
413 	0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15
414 };
415 
416 static void
417 putpad(s)
418 	register char *s;
419 {
420 	register pad = 0;
421 	register mspc10;
422 
423 	if (isdigit(*s)) {
424 		while (isdigit(*s)) {
425 			pad *= 10;
426 			pad += *s++ - '0';
427 		}
428 		pad *= 10;
429 		if (*s == '.' && isdigit(s[1])) {
430 			pad += s[1] - '0';
431 			s += 2;
432 		}
433 	}
434 
435 	puts(s);
436 	/*
437 	 * If no delay needed, or output speed is
438 	 * not comprehensible, then don't try to delay.
439 	 */
440 	if (pad == 0)
441 		return;
442 	if (tmode.sg_ospeed <= 0 ||
443 	    tmode.sg_ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
444 		return;
445 
446 	/*
447 	 * Round up by a half a character frame, and then do the delay.
448 	 * Too bad there are no user program accessible programmed delays.
449 	 * Transmitting pad characters slows many terminals down and also
450 	 * loads the system.
451 	 */
452 	mspc10 = tmspc10[tmode.sg_ospeed];
453 	pad += mspc10 / 2;
454 	for (pad /= mspc10; pad > 0; pad--)
455 		putchr(*PC);
456 }
457 
458 static void
459 puts(s)
460 	register char *s;
461 {
462 	while (*s)
463 		putchr(*s++);
464 }
465 
466 char	outbuf[OBUFSIZ];
467 int	obufcnt = 0;
468 
469 static void
470 putchr(cc)
471 	int cc;
472 {
473 	char c;
474 
475 	c = cc;
476 	if (!NP) {
477 		c |= partab[c&0177] & 0200;
478 		if (OP)
479 			c ^= 0200;
480 	}
481 	if (!UB) {
482 		outbuf[obufcnt++] = c;
483 		if (obufcnt >= OBUFSIZ)
484 			oflush();
485 	} else
486 		write(STDOUT_FILENO, &c, 1);
487 }
488 
489 static void
490 oflush()
491 {
492 	if (obufcnt)
493 		write(STDOUT_FILENO, outbuf, obufcnt);
494 	obufcnt = 0;
495 }
496 
497 static void
498 prompt()
499 {
500 
501 	putf(LM);
502 	if (CO)
503 		putchr('\n');
504 }
505 
506 static void
507 putf(cp)
508 	register char *cp;
509 {
510 	extern char editedhost[];
511 	time_t t;
512 	char *slash, db[100];
513 
514 	while (*cp) {
515 		if (*cp != '%') {
516 			putchr(*cp++);
517 			continue;
518 		}
519 		switch (*++cp) {
520 
521 		case 't':
522 			slash = strrchr(ttyn, '/');
523 			if (slash == (char *) 0)
524 				puts(ttyn);
525 			else
526 				puts(&slash[1]);
527 			break;
528 
529 		case 'h':
530 			puts(editedhost);
531 			break;
532 
533 		case 'd': {
534 			static char fmt[] = "%l:% %P on %A, %d %B %Y";
535 
536 			fmt[4] = 'M';		/* I *hate* SCCS... */
537 			(void)time(&t);
538 			(void)strftime(db, sizeof(db), fmt, localtime(&t));
539 			puts(db);
540 			break;
541 		}
542 
543 		case '%':
544 			putchr('%');
545 			break;
546 		}
547 		cp++;
548 	}
549 }
550