main.c revision 135941
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 135941 2004-09-29 20:55:14Z imp $";
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;
431	int failopenlogged = 0;
432
433	while ((i = open(tty, flags)) == -1)
434	{
435		if (!failopenlogged) {
436			syslog(LOG_ERR, "open %s: %m", tty);
437			failopenlogged = 1;
438		}
439		sleep(60);
440	}
441	if (login_tty(i) < 0) {
442		if (daemon(0,0) < 0) {
443			syslog(LOG_ERR,"daemon: %m");
444			close(i);
445			return 0;
446		}
447		if (login_tty(i) < 0) {
448			syslog(LOG_ERR, "login_tty %s: %m", tty);
449			close(i);
450			return 0;
451		}
452	}
453	return 1;
454}
455
456static void
457defttymode()
458{
459
460	/* Start with default tty settings. */
461	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
462		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
463		exit(1);
464	}
465	omode = tmode; /* fill c_cc for dogettytab() */
466	dogettytab();
467	/*
468	 * Don't rely on the driver too much, and initialize crucial
469	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
470	 * the c_cc[] settings however, the console drivers might wish
471	 * to leave their idea of the preferred VERASE key value
472	 * there.
473	 */
474	tmode.c_iflag = TTYDEF_IFLAG;
475	tmode.c_oflag = TTYDEF_OFLAG;
476	tmode.c_lflag = TTYDEF_LFLAG;
477	tmode.c_cflag = TTYDEF_CFLAG;
478	if (NC)
479		tmode.c_cflag |= CLOCAL;
480	omode = tmode;
481}
482
483static void
484setttymode(int raw)
485{
486	int off = 0;
487
488	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
489	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
490	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
491
492	if (IS)
493		cfsetispeed(&tmode, speed(IS));
494	else if (SP)
495		cfsetispeed(&tmode, speed(SP));
496	if (OS)
497		cfsetospeed(&tmode, speed(OS));
498	else if (SP)
499		cfsetospeed(&tmode, speed(SP));
500	set_flags(0);
501	setchars();
502	if (raw)
503		cfmakeraw(&tmode);
504	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
505		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
506		exit(1);
507	}
508}
509
510
511static int
512getname(void)
513{
514	int c;
515	char *np;
516	unsigned char cs;
517	int ppp_state = 0;
518	int ppp_connection = 0;
519
520	/*
521	 * Interrupt may happen if we use CBREAK mode
522	 */
523	if (setjmp(intrupt)) {
524		signal(SIGINT, SIG_IGN);
525		return (0);
526	}
527	signal(SIGINT, interrupt);
528	set_flags(1);
529	prompt();
530	oflush();
531	if (PF > 0) {
532		sleep(PF);
533		PF = 0;
534	}
535	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
536		syslog(LOG_ERR, "%s: %m", ttyn);
537		exit(1);
538	}
539	crmod = digit = lower = upper = 0;
540	np = name;
541	for (;;) {
542		oflush();
543		if (read(STDIN_FILENO, &cs, 1) <= 0)
544			exit(0);
545		if ((c = cs&0177) == 0)
546			return (0);
547
548		/* PPP detection state machine..
549		   Look for sequences:
550		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
551		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
552		   See RFC1662.
553		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
554		   and Erik 'PPP' Olson, <eriko@wrq.com>
555		 */
556
557		if (PP && (cs == PPP_FRAME)) {
558			ppp_state = 1;
559		} else if (ppp_state == 1 && cs == PPP_STATION) {
560			ppp_state = 2;
561		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
562			ppp_state = 3;
563		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
564			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
565			ppp_state = 4;
566		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
567			ppp_state = 5;
568		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
569			ppp_connection = 1;
570			break;
571		} else {
572			ppp_state = 0;
573		}
574
575		if (c == EOT || c == CTRL('d'))
576			exit(0);
577		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
578			putf("\r\n");
579			break;
580		}
581		if (islower(c))
582			lower = 1;
583		else if (isupper(c))
584			upper = 1;
585		else if (c == ERASE || c == '\b' || c == 0177) {
586			if (np > name) {
587				np--;
588				if (cfgetospeed(&tmode) >= 1200)
589					puts("\b \b");
590				else
591					putchr(cs);
592			}
593			continue;
594		} else if (c == KILL || c == CTRL('u')) {
595			putchr('\r');
596			if (cfgetospeed(&tmode) < 1200)
597				putchr('\n');
598			/* this is the way they do it down under ... */
599			else if (np > name)
600				puts("                                     \r");
601			prompt();
602			digit = lower = upper = 0;
603			np = name;
604			continue;
605		} else if (isdigit(c))
606			digit = 1;
607		if (IG && (c <= ' ' || c > 0176))
608			continue;
609		*np++ = c;
610		putchr(cs);
611	}
612	signal(SIGINT, SIG_IGN);
613	*np = 0;
614	if (c == '\r')
615		crmod = 1;
616	if ((upper && !lower && !LC) || UC)
617		for (np = name; *np; np++)
618			if (isupper(*np))
619				*np = tolower(*np);
620	return (1 + ppp_connection);
621}
622
623static void
624putpad(const char *s)
625{
626	int pad = 0;
627	speed_t ospeed = cfgetospeed(&tmode);
628
629	if (isdigit(*s)) {
630		while (isdigit(*s)) {
631			pad *= 10;
632			pad += *s++ - '0';
633		}
634		pad *= 10;
635		if (*s == '.' && isdigit(s[1])) {
636			pad += s[1] - '0';
637			s += 2;
638		}
639	}
640
641	puts(s);
642	/*
643	 * If no delay needed, or output speed is
644	 * not comprehensible, then don't try to delay.
645	 */
646	if (pad == 0 || ospeed <= 0)
647		return;
648
649	/*
650	 * Round up by a half a character frame, and then do the delay.
651	 * Too bad there are no user program accessible programmed delays.
652	 * Transmitting pad characters slows many terminals down and also
653	 * loads the system.
654	 */
655	pad = (pad * ospeed + 50000) / 100000;
656	while (pad--)
657		putchr(*PC);
658}
659
660static void
661puts(const char *s)
662{
663	while (*s)
664		putchr(*s++);
665}
666
667char	outbuf[OBUFSIZ];
668int	obufcnt = 0;
669
670static void
671putchr(int cc)
672{
673	char c;
674
675	c = cc;
676	if (!NP) {
677		c |= partab[c&0177] & 0200;
678		if (OP)
679			c ^= 0200;
680	}
681	if (!UB) {
682		outbuf[obufcnt++] = c;
683		if (obufcnt >= OBUFSIZ)
684			oflush();
685	} else
686		write(STDOUT_FILENO, &c, 1);
687}
688
689static void
690oflush(void)
691{
692	if (obufcnt)
693		write(STDOUT_FILENO, outbuf, obufcnt);
694	obufcnt = 0;
695}
696
697static void
698prompt(void)
699{
700
701	putf(LM);
702	if (CO)
703		putchr('\n');
704}
705
706
707static char *
708getline(int fd)
709{
710	int i = 0;
711	static char linebuf[512];
712
713	/*
714	 * This is certainly slow, but it avoids having to include
715	 * stdio.h unnecessarily. Issue files should be small anyway.
716	 */
717	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
718		if (linebuf[i] == '\n') {
719			/* Don't rely on newline mode, assume raw */
720			linebuf[i++] = '\r';
721			linebuf[i++] = '\n';
722			linebuf[i] = '\0';
723			return linebuf;
724		}
725		++i;
726	}
727	linebuf[i] = '\0';
728	return i ? linebuf : 0;
729}
730
731static void
732putf(const char *cp)
733{
734	extern char editedhost[];
735	time_t t;
736	char *slash, db[100];
737
738	static struct utsname kerninfo;
739
740	if (!*kerninfo.sysname)
741		uname(&kerninfo);
742
743	while (*cp) {
744		if (*cp != '%') {
745			putchr(*cp++);
746			continue;
747		}
748		switch (*++cp) {
749
750		case 't':
751			slash = strrchr(ttyn, '/');
752			if (slash == (char *) 0)
753				puts(ttyn);
754			else
755				puts(&slash[1]);
756			break;
757
758		case 'h':
759			puts(editedhost);
760			break;
761
762		case 'd': {
763			t = (time_t)0;
764			(void)time(&t);
765			if (Lo)
766				(void)setlocale(LC_TIME, Lo);
767			(void)strftime(db, sizeof(db), DF, localtime(&t));
768			puts(db);
769			break;
770
771		case 's':
772			puts(kerninfo.sysname);
773			break;
774
775		case 'm':
776			puts(kerninfo.machine);
777			break;
778
779		case 'r':
780			puts(kerninfo.release);
781			break;
782
783		case 'v':
784			puts(kerninfo.version);
785			break;
786		}
787
788		case '%':
789			putchr('%');
790			break;
791		}
792		cp++;
793	}
794}
795
796/*
797 * Read a gettytab database entry and perform necessary quirks.
798 */
799static void
800dogettytab()
801{
802
803	/* Read the database entry. */
804	gettable(tname, tabent);
805
806	/*
807	 * Avoid inheriting the parity values from the default entry
808	 * if any of them is set in the current entry.
809	 * Mixing different parity settings is unreasonable.
810	 */
811	if (OPset || EPset || APset || NPset)
812		OPset = EPset = APset = NPset = 1;
813
814	/* Fill in default values for unset capabilities. */
815	setdefaults();
816}
817