main.c revision 117738
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
35static const 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#if 0
42static char sccsid[] = "@(#)from: main.c	8.1 (Berkeley) 6/20/93";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/libexec/getty/main.c 117738 2003-07-18 16:25:55Z yar $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/ioctl.h>
50#include <sys/time.h>
51#include <sys/resource.h>
52#include <sys/stat.h>
53#include <sys/ttydefaults.h>
54#include <sys/utsname.h>
55
56#include <ctype.h>
57#include <errno.h>
58#include <fcntl.h>
59#include <locale.h>
60#include <libutil.h>
61#include <setjmp.h>
62#include <signal.h>
63#include <stdlib.h>
64#include <string.h>
65#include <syslog.h>
66#include <termios.h>
67#include <time.h>
68#include <unistd.h>
69
70#include "extern.h"
71#include "gettytab.h"
72#include "pathnames.h"
73
74/*
75 * Set the amount of running time that getty should accumulate
76 * before deciding that something is wrong and exit.
77 */
78#define GETTY_TIMEOUT	60 /* seconds */
79
80#undef CTRL
81#define CTRL(x)  (x&037)
82
83/* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
84
85#define PPP_FRAME           0x7e  /* PPP Framing character */
86#define PPP_STATION         0xff  /* "All Station" character */
87#define PPP_ESCAPE          0x7d  /* Escape Character */
88#define PPP_CONTROL         0x03  /* PPP Control Field */
89#define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
90#define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
91#define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
92
93/* original mode; flags've been reset using values from <sys/ttydefaults.h> */
94struct termios omode;
95/* current mode */
96struct termios tmode;
97
98int crmod, digit, lower, upper;
99
100char	hostname[MAXHOSTNAMELEN];
101char	name[MAXLOGNAME*3];
102char	dev[] = _PATH_DEV;
103char	ttyn[32];
104
105#define	OBUFSIZ		128
106#define	TABBUFSIZ	512
107
108char	defent[TABBUFSIZ];
109char	tabent[TABBUFSIZ];
110const	char *tname;
111
112char	*env[128];
113
114char partab[] = {
115	0001,0201,0201,0001,0201,0001,0001,0201,
116	0202,0004,0003,0205,0005,0206,0201,0001,
117	0201,0001,0001,0201,0001,0201,0201,0001,
118	0001,0201,0201,0001,0201,0001,0001,0201,
119	0200,0000,0000,0200,0000,0200,0200,0000,
120	0000,0200,0200,0000,0200,0000,0000,0200,
121	0000,0200,0200,0000,0200,0000,0000,0200,
122	0200,0000,0000,0200,0000,0200,0200,0000,
123	0200,0000,0000,0200,0000,0200,0200,0000,
124	0000,0200,0200,0000,0200,0000,0000,0200,
125	0000,0200,0200,0000,0200,0000,0000,0200,
126	0200,0000,0000,0200,0000,0200,0200,0000,
127	0000,0200,0200,0000,0200,0000,0000,0200,
128	0200,0000,0000,0200,0000,0200,0200,0000,
129	0200,0000,0000,0200,0000,0200,0200,0000,
130	0000,0200,0200,0000,0200,0000,0000,0201
131};
132
133#define	ERASE	tmode.c_cc[VERASE]
134#define	KILL	tmode.c_cc[VKILL]
135#define	EOT	tmode.c_cc[VEOF]
136
137#define	puts	Gputs
138
139static void	defttymode(void);
140static void	dingdong(int);
141static void	dogettytab(void);
142static int	getname(void);
143static void	interrupt(int);
144static void	oflush(void);
145static void	prompt(void);
146static void	putchr(int);
147static void	putf(const char *);
148static void	putpad(const char *);
149static void	puts(const char *);
150static void	timeoverrun(int);
151static char	*getline(int);
152static void	setttymode(int);
153static int	opentty(const char *, int);
154
155jmp_buf timeout;
156
157static void
158dingdong(int signo __unused)
159{
160	alarm(0);
161	longjmp(timeout, 1);
162}
163
164jmp_buf	intrupt;
165
166static void
167interrupt(int signo __unused)
168{
169	longjmp(intrupt, 1);
170}
171
172/*
173 * Action to take when getty is running too long.
174 */
175static void
176timeoverrun(int signo __unused)
177{
178
179	syslog(LOG_ERR, "getty exiting due to excessive running time");
180	exit(1);
181}
182
183int
184main(int argc, char *argv[])
185{
186	extern	char **environ;
187	int first_sleep = 1, first_time = 1;
188	struct rlimit limit;
189	int rval;
190
191	signal(SIGINT, SIG_IGN);
192	signal(SIGQUIT, SIG_IGN);
193
194	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
195	gethostname(hostname, sizeof(hostname) - 1);
196	hostname[sizeof(hostname) - 1] = '\0';
197	if (hostname[0] == '\0')
198		strcpy(hostname, "Amnesiac");
199
200	/*
201	 * Limit running time to deal with broken or dead lines.
202	 */
203	(void)signal(SIGXCPU, timeoverrun);
204	limit.rlim_max = RLIM_INFINITY;
205	limit.rlim_cur = GETTY_TIMEOUT;
206	(void)setrlimit(RLIMIT_CPU, &limit);
207
208	gettable("default", defent);
209	gendefaults();
210	tname = "default";
211	if (argc > 1)
212		tname = argv[1];
213
214	/*
215	 * The following is a work around for vhangup interactions
216	 * which cause great problems getting window systems started.
217	 * If the tty line is "-", we do the old style getty presuming
218	 * that the file descriptors are already set up for us.
219	 * J. Gettys - MIT Project Athena.
220	 */
221	if (argc <= 2 || strcmp(argv[2], "-") == 0)
222	    strcpy(ttyn, ttyname(STDIN_FILENO));
223	else {
224	    strcpy(ttyn, dev);
225	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
226	    if (strcmp(argv[0], "+") != 0) {
227		chown(ttyn, 0, 0);
228		chmod(ttyn, 0600);
229		revoke(ttyn);
230
231		/*
232		 * Do the first scan through gettytab.
233		 * Terminal mode parameters will be wrong until
234		 * defttymode() called, but they're irrelevant for
235		 * the initial setup of the terminal device.
236		 */
237		dogettytab();
238
239		/*
240		 * Init or answer modem sequence has been specified.
241		 */
242		if (IC || AC) {
243			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
244				exit(1);
245			defttymode();
246			setttymode(1);
247		}
248
249		if (IC) {
250			if (getty_chat(IC, CT, DC) > 0) {
251				syslog(LOG_ERR, "modem init problem on %s", ttyn);
252				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
253				exit(1);
254			}
255		}
256
257		if (AC) {
258			int i, rfds;
259			struct timeval to;
260
261        		rfds = 1 << 0;	/* FD_SET */
262        		to.tv_sec = RT;
263        		to.tv_usec = 0;
264        		i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
265        			       (fd_set*)NULL, RT ? &to : NULL);
266        		if (i < 0) {
267				syslog(LOG_ERR, "select %s: %m", ttyn);
268			} else if (i == 0) {
269				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
270				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
271				exit(0);  /* recycle for init */
272			}
273			i = getty_chat(AC, CT, DC);
274			if (i > 0) {
275				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
276				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
277				exit(1);
278			}
279		} else { /* maybe blocking open */
280			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
281				exit(1);
282		}
283	    }
284	}
285
286	defttymode();
287	for (;;) {
288
289		/*
290		 * if a delay was specified then sleep for that
291		 * number of seconds before writing the initial prompt
292		 */
293		if (first_sleep && DE) {
294		    sleep(DE);
295		    /* remove any noise */
296		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
297		}
298		first_sleep = 0;
299
300		setttymode(0);
301		if (AB) {
302			tname = autobaud();
303			dogettytab();
304			continue;
305		}
306		if (PS) {
307			tname = portselector();
308			dogettytab();
309			continue;
310		}
311		if (CL && *CL)
312			putpad(CL);
313		edithost(HE);
314
315		/* if this is the first time through this, and an
316		   issue file has been given, then send it */
317		if (first_time && IF) {
318			int fd;
319
320			if ((fd = open(IF, O_RDONLY)) != -1) {
321				char * cp;
322
323				while ((cp = getline(fd)) != NULL) {
324					  putf(cp);
325				}
326				close(fd);
327			}
328		}
329		first_time = 0;
330
331		if (IM && *IM && !(PL && PP))
332			putf(IM);
333		if (setjmp(timeout)) {
334			cfsetispeed(&tmode, B0);
335			cfsetospeed(&tmode, B0);
336			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
337			exit(1);
338		}
339		if (TO) {
340			signal(SIGALRM, dingdong);
341			alarm(TO);
342		}
343
344		rval = 0;
345		if (AL) {
346			const char *p = AL;
347			char *q = name;
348
349			while (*p && q < &name[sizeof name - 1]) {
350				if (isupper(*p))
351					upper = 1;
352				else if (islower(*p))
353					lower = 1;
354				else if (isdigit(*p))
355					digit = 1;
356				*q++ = *p++;
357			}
358		} else if (!(PL && PP))
359			rval = getname();
360		if (rval == 2 || (PL && PP)) {
361			oflush();
362			alarm(0);
363			limit.rlim_max = RLIM_INFINITY;
364			limit.rlim_cur = RLIM_INFINITY;
365			(void)setrlimit(RLIMIT_CPU, &limit);
366			execle(PP, "ppplogin", ttyn, (char *) 0, env);
367			syslog(LOG_ERR, "%s: %m", PP);
368			exit(1);
369		} else if (rval || AL) {
370			int i;
371
372			oflush();
373			alarm(0);
374			signal(SIGALRM, SIG_DFL);
375			if (name[0] == '\0')
376				continue;
377			if (name[0] == '-') {
378				puts("user names may not start with '-'.");
379				continue;
380			}
381			if (!(upper || lower || digit)) {
382				if (AL) {
383					syslog(LOG_ERR,
384					    "invalid auto-login name: %s", AL);
385					exit(1);
386				} else
387					continue;
388			}
389			set_flags(2);
390			if (crmod) {
391				tmode.c_iflag |= ICRNL;
392				tmode.c_oflag |= ONLCR;
393			}
394#if REALLY_OLD_TTYS
395			if (upper || UC)
396				tmode.sg_flags |= LCASE;
397			if (lower || LC)
398				tmode.sg_flags &= ~LCASE;
399#endif
400			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
401				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
402				exit(1);
403			}
404			signal(SIGINT, SIG_DFL);
405			for (i = 0; environ[i] != (char *)0; i++)
406				env[i] = environ[i];
407			makeenv(&env[i]);
408
409			limit.rlim_max = RLIM_INFINITY;
410			limit.rlim_cur = RLIM_INFINITY;
411			(void)setrlimit(RLIMIT_CPU, &limit);
412			execle(LO, "login", AL ? "-fp" : "-p", name,
413			    (char *) 0, env);
414			syslog(LOG_ERR, "%s: %m", LO);
415			exit(1);
416		}
417		alarm(0);
418		signal(SIGALRM, SIG_DFL);
419		signal(SIGINT, SIG_IGN);
420		if (NX && *NX) {
421			tname = NX;
422			dogettytab();
423		}
424	}
425}
426
427static int
428opentty(const char *tty, int flags)
429{
430	int i, j = 0;
431	int failopenlogged = 0;
432
433	while (j < 10 && (i = open(tty, flags)) == -1)
434	{
435		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
436			syslog(LOG_ERR, "open %s: %m", tty);
437			failopenlogged = 1;
438		}
439		j++;
440		sleep(60);
441	}
442	if (i == -1) {
443		syslog(LOG_ERR, "open %s: %m", tty);
444		return 0;
445	}
446	else {
447		if (login_tty(i) < 0) {
448			if (daemon(0,0) < 0) {
449				syslog(LOG_ERR,"daemon: %m");
450				close(i);
451				return 0;
452			}
453			if (login_tty(i) < 0) {
454				syslog(LOG_ERR, "login_tty %s: %m", tty);
455				close(i);
456				return 0;
457			}
458		}
459		return 1;
460	}
461}
462
463static void
464defttymode()
465{
466
467	/* Start with default tty settings. */
468	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
469		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
470		exit(1);
471	}
472	omode = tmode; /* fill c_cc for dogettytab() */
473	dogettytab();
474	/*
475	 * Don't rely on the driver too much, and initialize crucial
476	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
477	 * the c_cc[] settings however, the console drivers might wish
478	 * to leave their idea of the preferred VERASE key value
479	 * there.
480	 */
481	tmode.c_iflag = TTYDEF_IFLAG;
482	tmode.c_oflag = TTYDEF_OFLAG;
483	tmode.c_lflag = TTYDEF_LFLAG;
484	tmode.c_cflag = TTYDEF_CFLAG;
485	if (NC)
486		tmode.c_cflag |= CLOCAL;
487	omode = tmode;
488}
489
490static void
491setttymode(int raw)
492{
493	int off = 0;
494
495	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
496	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
497	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
498
499	if (IS)
500		cfsetispeed(&tmode, speed(IS));
501	else if (SP)
502		cfsetispeed(&tmode, speed(SP));
503	if (OS)
504		cfsetospeed(&tmode, speed(OS));
505	else if (SP)
506		cfsetospeed(&tmode, speed(SP));
507	set_flags(0);
508	setchars();
509	if (raw)
510		cfmakeraw(&tmode);
511	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
512		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
513		exit(1);
514	}
515}
516
517
518static int
519getname(void)
520{
521	int c;
522	char *np;
523	unsigned char cs;
524	int ppp_state = 0;
525	int ppp_connection = 0;
526
527	/*
528	 * Interrupt may happen if we use CBREAK mode
529	 */
530	if (setjmp(intrupt)) {
531		signal(SIGINT, SIG_IGN);
532		return (0);
533	}
534	signal(SIGINT, interrupt);
535	set_flags(1);
536	prompt();
537	oflush();
538	if (PF > 0) {
539		sleep(PF);
540		PF = 0;
541	}
542	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
543		syslog(LOG_ERR, "%s: %m", ttyn);
544		exit(1);
545	}
546	crmod = digit = lower = upper = 0;
547	np = name;
548	for (;;) {
549		oflush();
550		if (read(STDIN_FILENO, &cs, 1) <= 0)
551			exit(0);
552		if ((c = cs&0177) == 0)
553			return (0);
554
555		/* PPP detection state machine..
556		   Look for sequences:
557		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
558		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
559		   See RFC1662.
560		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
561		   and Erik 'PPP' Olson, <eriko@wrq.com>
562		 */
563
564		if (PP && (cs == PPP_FRAME)) {
565			ppp_state = 1;
566		} else if (ppp_state == 1 && cs == PPP_STATION) {
567			ppp_state = 2;
568		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
569			ppp_state = 3;
570		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
571			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
572			ppp_state = 4;
573		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
574			ppp_state = 5;
575		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
576			ppp_connection = 1;
577			break;
578		} else {
579			ppp_state = 0;
580		}
581
582		if (c == EOT || c == CTRL('d'))
583			exit(0);
584		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
585			putf("\r\n");
586			break;
587		}
588		if (islower(c))
589			lower = 1;
590		else if (isupper(c))
591			upper = 1;
592		else if (c == ERASE || c == '\b' || c == 0177) {
593			if (np > name) {
594				np--;
595				if (cfgetospeed(&tmode) >= 1200)
596					puts("\b \b");
597				else
598					putchr(cs);
599			}
600			continue;
601		} else if (c == KILL || c == CTRL('u')) {
602			putchr('\r');
603			if (cfgetospeed(&tmode) < 1200)
604				putchr('\n');
605			/* this is the way they do it down under ... */
606			else if (np > name)
607				puts("                                     \r");
608			prompt();
609			digit = lower = upper = 0;
610			np = name;
611			continue;
612		} else if (isdigit(c))
613			digit = 1;
614		if (IG && (c <= ' ' || c > 0176))
615			continue;
616		*np++ = c;
617		putchr(cs);
618	}
619	signal(SIGINT, SIG_IGN);
620	*np = 0;
621	if (c == '\r')
622		crmod = 1;
623	if ((upper && !lower && !LC) || UC)
624		for (np = name; *np; np++)
625			if (isupper(*np))
626				*np = tolower(*np);
627	return (1 + ppp_connection);
628}
629
630static void
631putpad(const char *s)
632{
633	int pad = 0;
634	speed_t ospeed = cfgetospeed(&tmode);
635
636	if (isdigit(*s)) {
637		while (isdigit(*s)) {
638			pad *= 10;
639			pad += *s++ - '0';
640		}
641		pad *= 10;
642		if (*s == '.' && isdigit(s[1])) {
643			pad += s[1] - '0';
644			s += 2;
645		}
646	}
647
648	puts(s);
649	/*
650	 * If no delay needed, or output speed is
651	 * not comprehensible, then don't try to delay.
652	 */
653	if (pad == 0 || ospeed <= 0)
654		return;
655
656	/*
657	 * Round up by a half a character frame, and then do the delay.
658	 * Too bad there are no user program accessible programmed delays.
659	 * Transmitting pad characters slows many terminals down and also
660	 * loads the system.
661	 */
662	pad = (pad * ospeed + 50000) / 100000;
663	while (pad--)
664		putchr(*PC);
665}
666
667static void
668puts(const char *s)
669{
670	while (*s)
671		putchr(*s++);
672}
673
674char	outbuf[OBUFSIZ];
675int	obufcnt = 0;
676
677static void
678putchr(int cc)
679{
680	char c;
681
682	c = cc;
683	if (!NP) {
684		c |= partab[c&0177] & 0200;
685		if (OP)
686			c ^= 0200;
687	}
688	if (!UB) {
689		outbuf[obufcnt++] = c;
690		if (obufcnt >= OBUFSIZ)
691			oflush();
692	} else
693		write(STDOUT_FILENO, &c, 1);
694}
695
696static void
697oflush(void)
698{
699	if (obufcnt)
700		write(STDOUT_FILENO, outbuf, obufcnt);
701	obufcnt = 0;
702}
703
704static void
705prompt(void)
706{
707
708	putf(LM);
709	if (CO)
710		putchr('\n');
711}
712
713
714static char *
715getline(int fd)
716{
717	int i = 0;
718	static char linebuf[512];
719
720	/*
721	 * This is certainly slow, but it avoids having to include
722	 * stdio.h unnecessarily. Issue files should be small anyway.
723	 */
724	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
725		if (linebuf[i] == '\n') {
726			/* Don't rely on newline mode, assume raw */
727			linebuf[i++] = '\r';
728			linebuf[i++] = '\n';
729			linebuf[i] = '\0';
730			return linebuf;
731		}
732		++i;
733	}
734	linebuf[i] = '\0';
735	return i ? linebuf : 0;
736}
737
738static void
739putf(const char *cp)
740{
741	extern char editedhost[];
742	time_t t;
743	char *slash, db[100];
744
745	static struct utsname kerninfo;
746
747	if (!*kerninfo.sysname)
748		uname(&kerninfo);
749
750	while (*cp) {
751		if (*cp != '%') {
752			putchr(*cp++);
753			continue;
754		}
755		switch (*++cp) {
756
757		case 't':
758			slash = strrchr(ttyn, '/');
759			if (slash == (char *) 0)
760				puts(ttyn);
761			else
762				puts(&slash[1]);
763			break;
764
765		case 'h':
766			puts(editedhost);
767			break;
768
769		case 'd': {
770			t = (time_t)0;
771			(void)time(&t);
772			if (Lo)
773				(void)setlocale(LC_TIME, Lo);
774			(void)strftime(db, sizeof(db), DF, localtime(&t));
775			puts(db);
776			break;
777
778		case 's':
779			puts(kerninfo.sysname);
780			break;
781
782		case 'm':
783			puts(kerninfo.machine);
784			break;
785
786		case 'r':
787			puts(kerninfo.release);
788			break;
789
790		case 'v':
791			puts(kerninfo.version);
792			break;
793		}
794
795		case '%':
796			putchr('%');
797			break;
798		}
799		cp++;
800	}
801}
802
803/*
804 * Read a gettytab database entry and perform necessary quirks.
805 */
806static void
807dogettytab()
808{
809
810	/* Read the database entry. */
811	gettable(tname, tabent);
812
813	/*
814	 * Avoid inheriting the parity values from the default entry
815	 * if any of them is set in the current entry.
816	 * Mixing different parity settings is unreasonable.
817	 */
818	if (OPset || EPset || APset || NPset)
819		OPset = EPset = APset = NPset = 1;
820
821	/* Fill in default values for unset capabilities. */
822	setdefaults();
823}
824