Home | History | Annotate | Line # | Download | only in getty
main.c revision 1.64
      1 /*	$NetBSD: main.c,v 1.64 2013/08/12 13:54:33 joerg 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. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 
     34 #ifndef lint
     35 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
     36  The Regents of the University of California.  All rights reserved.");
     37 #endif /* not lint */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
     42 #else
     43 __RCSID("$NetBSD: main.c,v 1.64 2013/08/12 13:54:33 joerg Exp $");
     44 #endif
     45 #endif /* not lint */
     46 
     47 #include <sys/param.h>
     48 #include <sys/ioctl.h>
     49 #include <sys/resource.h>
     50 #include <sys/utsname.h>
     51 
     52 #include <ctype.h>
     53 #include <errno.h>
     54 #include <fcntl.h>
     55 #include <limits.h>
     56 #include <pwd.h>
     57 #include <setjmp.h>
     58 #include <signal.h>
     59 #include <stdio.h>
     60 #include <stdlib.h>
     61 #include <string.h>
     62 #include <syslog.h>
     63 #include <term.h>
     64 #include <termios.h>
     65 #include <time.h>
     66 #include <ttyent.h>
     67 #include <unistd.h>
     68 #include <util.h>
     69 
     70 #include "gettytab.h"
     71 #include "pathnames.h"
     72 #include "extern.h"
     73 
     74 extern char editedhost[];
     75 
     76 /*
     77  * Set the amount of running time that getty should accumulate
     78  * before deciding that something is wrong and exit.
     79  */
     80 #define GETTY_TIMEOUT	60 /* seconds */
     81 
     82 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
     83 
     84 #define PPP_FRAME           0x7e  /* PPP Framing character */
     85 #define PPP_STATION         0xff  /* "All Station" character */
     86 #define PPP_ESCAPE          0x7d  /* Escape Character */
     87 #define PPP_CONTROL         0x03  /* PPP Control Field */
     88 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
     89 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
     90 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
     91 
     92 struct termios tmode, omode;
     93 
     94 int crmod, digit_or_punc, lower, upper;
     95 
     96 char	hostname[MAXHOSTNAMELEN + 1];
     97 struct	utsname kerninfo;
     98 char	name[LOGIN_NAME_MAX];
     99 char	dev[] = _PATH_DEV;
    100 char	ttyn[32];
    101 char	lockfile[512];
    102 uid_t	ttyowner;
    103 char	*rawttyn;
    104 
    105 #define	OBUFSIZ		128
    106 #define	TABBUFSIZ	512
    107 
    108 char	defent[TABBUFSIZ];
    109 char	tabent[TABBUFSIZ];
    110 
    111 char	*env[128];
    112 
    113 const unsigned char partab[] = {
    114 	0001,0201,0201,0001,0201,0001,0001,0201,
    115 	0202,0004,0003,0205,0005,0206,0201,0001,
    116 	0201,0001,0001,0201,0001,0201,0201,0001,
    117 	0001,0201,0201,0001,0201,0001,0001,0201,
    118 	0200,0000,0000,0200,0000,0200,0200,0000,
    119 	0000,0200,0200,0000,0200,0000,0000,0200,
    120 	0000,0200,0200,0000,0200,0000,0000,0200,
    121 	0200,0000,0000,0200,0000,0200,0200,0000,
    122 	0200,0000,0000,0200,0000,0200,0200,0000,
    123 	0000,0200,0200,0000,0200,0000,0000,0200,
    124 	0000,0200,0200,0000,0200,0000,0000,0200,
    125 	0200,0000,0000,0200,0000,0200,0200,0000,
    126 	0000,0200,0200,0000,0200,0000,0000,0200,
    127 	0200,0000,0000,0200,0000,0200,0200,0000,
    128 	0200,0000,0000,0200,0000,0200,0200,0000,
    129 	0000,0200,0200,0000,0200,0000,0000,0201
    130 };
    131 
    132 #define	ERASE	tmode.c_cc[VERASE]
    133 #define	KILL	tmode.c_cc[VKILL]
    134 #define	EOT	tmode.c_cc[VEOF]
    135 
    136 static void	clearscreen(void);
    137 
    138 sigjmp_buf timeout;
    139 
    140 __dead static void
    141 /*ARGSUSED*/
    142 dingdong(int signo)
    143 {
    144 
    145 	(void)alarm(0);
    146 	(void)signal(SIGALRM, SIG_DFL);
    147 	siglongjmp(timeout, 1);
    148 }
    149 
    150 sigjmp_buf intrupt;
    151 
    152 __dead static void
    153 /*ARGSUSED*/
    154 interrupt(int signo)
    155 {
    156 
    157 	(void)signal(SIGINT, interrupt);
    158 	siglongjmp(intrupt, 1);
    159 }
    160 
    161 /*
    162  * Action to take when getty is running too long.
    163  */
    164 __dead static void
    165 /*ARGSUSED*/
    166 timeoverrun(int signo)
    167 {
    168 
    169 	syslog(LOG_ERR, "getty exiting due to excessive running time");
    170 	exit(1);
    171 }
    172 
    173 static int	getname(void);
    174 static void	oflush(void);
    175 static void	prompt(void);
    176 static int	putchr(int);
    177 static void	putf(const char *);
    178 static void	xputs(const char *);
    179 
    180 #define putpad(s) tputs(s, 1, putchr)
    181 
    182 int
    183 main(int argc, char *argv[], char *envp[])
    184 {
    185 	const char *progname;
    186 	int repcnt = 0, failopenlogged = 0, first_time = 1;
    187 	struct rlimit limit;
    188 	struct passwd *pw;
    189 	int rval;
    190 	/* this is used past the siglongjmp, so make sure it is not cached
    191 	   in registers that might become invalid. */
    192 	volatile int uugetty = 0;
    193 	const char * volatile tname = "default";
    194 
    195 	(void)signal(SIGINT, SIG_IGN);
    196 	openlog("getty", LOG_PID, LOG_AUTH);
    197 	(void)gethostname(hostname, sizeof(hostname));
    198 	hostname[sizeof(hostname) - 1] = '\0';
    199 	if (hostname[0] == '\0')
    200 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
    201 	(void)uname(&kerninfo);
    202 
    203 	progname = getprogname();
    204 	if (progname[0] == 'u' && progname[1] == 'u')
    205 		uugetty = 1;
    206 
    207 	/*
    208 	 * Find id of uucp login (if present) so we can chown tty properly.
    209 	 */
    210 	if (uugetty && (pw = getpwnam("uucp")))
    211 		ttyowner = pw->pw_uid;
    212 	else
    213 		ttyowner = 0;
    214 
    215 	/*
    216 	 * Limit running time to deal with broken or dead lines.
    217 	 */
    218 	(void)signal(SIGXCPU, timeoverrun);
    219 	limit.rlim_max = RLIM_INFINITY;
    220 	limit.rlim_cur = GETTY_TIMEOUT;
    221 	(void)setrlimit(RLIMIT_CPU, &limit);
    222 
    223 	/*
    224 	 * The following is a work around for vhangup interactions
    225 	 * which cause great problems getting window systems started.
    226 	 * If the tty line is "-", we do the old style getty presuming
    227 	 * that the file descriptors are already set up for us.
    228 	 * J. Gettys - MIT Project Athena.
    229 	 */
    230 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
    231 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
    232 	}
    233 	else {
    234 		int i;
    235 
    236 		rawttyn = argv[2];
    237 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
    238 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
    239 		if (uugetty)  {
    240 			(void)chown(ttyn, ttyowner, 0);
    241 			(void)strlcpy(lockfile, _PATH_LOCK,
    242 				sizeof(lockfile));
    243 			(void)strlcat(lockfile, argv[2],
    244 				sizeof(lockfile));
    245 			/*
    246 			 * wait for lockfiles to go away before we try
    247 			 * to open
    248 			 */
    249 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
    250 				syslog(LOG_ERR,
    251 					"%s: can't create lockfile", ttyn);
    252 				exit(1);
    253 			}
    254 			(void)unlink(lockfile);
    255 		}
    256 		if (strcmp(argv[0], "+") != 0) {
    257 			(void)chown(ttyn, ttyowner, 0);
    258 			(void)chmod(ttyn, 0600);
    259 			(void)revoke(ttyn);
    260 			if (ttyaction(ttyn, "getty", "root"))
    261 				syslog(LOG_WARNING, "%s: ttyaction failed",
    262 					ttyn);
    263 			/*
    264 			 * Delay the open so DTR stays down long enough
    265 			 * to be detected.
    266 			 */
    267 			(void)sleep(2);
    268 			while ((i = open(ttyn, O_RDWR)) == -1) {
    269 				if ((repcnt % 10 == 0) &&
    270 				    (errno != ENXIO || !failopenlogged)) {
    271 					syslog(LOG_WARNING, "%s: %m", ttyn);
    272 					closelog();
    273 					failopenlogged = 1;
    274 				}
    275 				repcnt++;
    276 				(void)sleep(60);
    277 			}
    278 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
    279 				syslog(LOG_ERR, "%s: can't create lockfile",
    280 					ttyn);
    281 				exit(1);
    282 			}
    283 			if (uugetty)
    284 				(void)chown(lockfile, ttyowner, 0);
    285 			(void)login_tty(i);
    286 		}
    287 	}
    288 
    289 	/* Start with default tty settings */
    290 	if (tcgetattr(0, &tmode) < 0) {
    291 		syslog(LOG_ERR, "%s: %m", ttyn);
    292 		exit(1);
    293 	}
    294 	omode = tmode;
    295 
    296 	gettable("default", defent);
    297 	gendefaults();
    298 	if (argc > 1)
    299 		tname = argv[1];
    300 	for (;;) {
    301 		int off;
    302 
    303 		rval = 0;
    304 		gettable(tname, tabent);
    305 		if (OPset || EPset || APset)
    306 			APset++, OPset++, EPset++;
    307 		setdefaults();
    308 		off = 0;
    309 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
    310 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
    311 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
    312 
    313 		if (IS)
    314 			(void)cfsetispeed(&tmode, (speed_t)IS);
    315 		else if (SP)
    316 			(void)cfsetispeed(&tmode, (speed_t)SP);
    317 		if (OS)
    318 			(void)cfsetospeed(&tmode, (speed_t)OS);
    319 		else if (SP)
    320 			(void)cfsetospeed(&tmode, (speed_t)SP);
    321 		setflags(0);
    322 		setchars();
    323 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    324 			syslog(LOG_ERR, "%s: %m", ttyn);
    325 			exit(1);
    326 		}
    327 		if (AB) {
    328 			tname = autobaud();
    329 			continue;
    330 		}
    331 		if (PS) {
    332 			tname = portselector();
    333 			continue;
    334 		}
    335 		if (CS)
    336 			clearscreen();
    337 		if (CL && *CL)
    338 			putpad(CL);
    339 		edithost(HE);
    340 
    341 		/*
    342 		 * If this is the first time through this, and an
    343 		 * issue file has been given, then send it.
    344 		 */
    345 		if (first_time != 0 && IF != NULL) {
    346 			char buf[_POSIX2_LINE_MAX];
    347 			FILE *fp;
    348 
    349 			if ((fp = fopen(IF, "r")) != NULL) {
    350 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
    351 					putf(buf);
    352 				(void)fclose(fp);
    353 			}
    354 		}
    355 		first_time = 0;
    356 
    357 		if (IM && *IM)
    358 			putf(IM);
    359 		oflush();
    360 		if (sigsetjmp(timeout, 1)) {
    361 			tmode.c_ispeed = tmode.c_ospeed = 0;
    362 			(void)tcsetattr(0, TCSANOW, &tmode);
    363 			exit(1);
    364 		}
    365 		if (TO) {
    366 			(void)signal(SIGALRM, dingdong);
    367 			(void)alarm((unsigned int)TO);
    368 		}
    369 		if (NN) {
    370 			name[0] = '\0';
    371 			lower = 1;
    372 			upper = digit_or_punc = 0;
    373 		} else if (AL) {
    374 			const char *p = AL;
    375 			char *q = name;
    376 
    377 			while (*p && q < &name[sizeof name - 1]) {
    378 				if (isupper((unsigned char)*p))
    379 					upper = 1;
    380 				else if (islower((unsigned char)*p))
    381 					lower = 1;
    382 				else if (isdigit((unsigned char)*p))
    383 					digit_or_punc = 1;
    384 				*q++ = *p++;
    385 			}
    386 		} else if ((rval = getname()) == 2) {
    387 			setflags(2);
    388 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
    389 			syslog(LOG_ERR, "%s: %m", PP);
    390 			exit(1);
    391 		}
    392 
    393 		if (rval || AL || NN) {
    394 			int i;
    395 
    396 			oflush();
    397 			(void)alarm(0);
    398 			(void)signal(SIGALRM, SIG_DFL);
    399 			if (name[0] == '-') {
    400 				xputs("user names may not start with '-'.");
    401 				continue;
    402 			}
    403 			if (!(upper || lower || digit_or_punc))
    404 				continue;
    405 			setflags(2);
    406 			if (crmod) {
    407 				tmode.c_iflag |= ICRNL;
    408 				tmode.c_oflag |= ONLCR;
    409 			}
    410 #if XXX
    411 			if (upper || UC)
    412 				tmode.sg_flags |= LCASE;
    413 			if (lower || LC)
    414 				tmode.sg_flags &= ~LCASE;
    415 #endif
    416 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    417 				syslog(LOG_ERR, "%s: %m", ttyn);
    418 				exit(1);
    419 			}
    420 			(void)signal(SIGINT, SIG_DFL);
    421 			for (i = 0; envp[i] != NULL; i++)
    422 				env[i] = envp[i];
    423 			makeenv(&env[i]);
    424 
    425 			limit.rlim_max = RLIM_INFINITY;
    426 			limit.rlim_cur = RLIM_INFINITY;
    427 			(void)setrlimit(RLIMIT_CPU, &limit);
    428 			if (NN)
    429 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    430 				    NULL, env);
    431 			else
    432 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    433 				    "--", name, NULL, env);
    434 			syslog(LOG_ERR, "%s: %m", LO);
    435 			exit(1);
    436 		}
    437 		(void)alarm(0);
    438 		(void)signal(SIGALRM, SIG_DFL);
    439 		(void)signal(SIGINT, SIG_IGN);
    440 		if (NX && *NX)
    441 			tname = NX;
    442 		if (uugetty)
    443 			(void)unlink(lockfile);
    444 	}
    445 }
    446 
    447 static int
    448 getname(void)
    449 {
    450 	int c;
    451 	char *np;
    452 	unsigned char cs;
    453 	int ppp_state, ppp_connection;
    454 
    455 	/*
    456 	 * Interrupt may happen if we use CBREAK mode
    457 	 */
    458 	if (sigsetjmp(intrupt, 1)) {
    459 		(void)signal(SIGINT, SIG_IGN);
    460 		return (0);
    461 	}
    462 	(void)signal(SIGINT, interrupt);
    463 	setflags(1);
    464 	prompt();
    465 	if (PF > 0) {
    466 		oflush();
    467 		(void)sleep((unsigned int)PF);
    468 		PF = 0;
    469 	}
    470 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    471 		syslog(LOG_ERR, "%s: %m", ttyn);
    472 		exit(1);
    473 	}
    474 	crmod = digit_or_punc = lower = upper = 0;
    475 	ppp_state = ppp_connection = 0;
    476 	np = name;
    477 	for (;;) {
    478 		oflush();
    479 		if (read(STDIN_FILENO, &cs, 1) <= 0)
    480 			exit(0);
    481 		if ((c = cs&0177) == 0)
    482 			return (0);
    483 
    484 		/*
    485 		 * PPP detection state machine..
    486 		 * Look for sequences:
    487 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
    488 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
    489 		 * See RFC1662.
    490 		 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
    491 		 * and Erik 'PPP' Olson <eriko (at) wrq.com>
    492 		 */
    493 		if (PP && cs == PPP_FRAME) {
    494 			ppp_state = 1;
    495 		} else if (ppp_state == 1 && cs == PPP_STATION) {
    496 			ppp_state = 2;
    497 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
    498 			ppp_state = 3;
    499 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
    500 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
    501 			ppp_state = 4;
    502 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
    503 			ppp_state = 5;
    504 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
    505 			ppp_connection = 1;
    506 			break;
    507 		} else {
    508 			ppp_state = 0;
    509 		}
    510 
    511 		if (c == EOT)
    512 			exit(1);
    513 		if (c == '\r' || c == '\n' ||
    514 		    np >= &name[LOGIN_NAME_MAX - 1]) {
    515 			*np = '\0';
    516 			putf("\r\n");
    517 			break;
    518 		}
    519 		if (islower(c))
    520 			lower = 1;
    521 		else if (isupper(c))
    522 			upper = 1;
    523 		else if (c == ERASE || c == '#' || c == '\b') {
    524 			if (np > name) {
    525 				np--;
    526 				if (cfgetospeed(&tmode) >= 1200)
    527 					xputs("\b \b");
    528 				else
    529 					putchr(cs);
    530 			}
    531 			continue;
    532 		} else if (c == KILL || c == '@') {
    533 			putchr(cs);
    534 			putchr('\r');
    535 			if (cfgetospeed(&tmode) < 1200)
    536 				putchr('\n');
    537 			/* this is the way they do it down under ... */
    538 			else if (np > name)
    539 				xputs(
    540 				    "                                     \r");
    541 			prompt();
    542 			np = name;
    543 			continue;
    544 		} else if (isdigit(c) || c == '_')
    545 			digit_or_punc = 1;
    546 		if (IG && (c <= ' ' || c > 0176))
    547 			continue;
    548 		*np++ = c;
    549 		putchr(cs);
    550 
    551 		/*
    552 		 * An MS-Windows direct connect PPP "client" won't send its
    553 		 * first PPP packet until we respond to its "CLIENT" poll
    554 		 * with a CRLF sequence.  We cater to yet another broken
    555 		 * implementation of a previously-standard protocol...
    556 		 */
    557 		*np = '\0';
    558 		if (strstr(name, "CLIENT"))
    559 			putf("\r\n");
    560 	}
    561 	(void)signal(SIGINT, SIG_IGN);
    562 	*np = 0;
    563 	if (c == '\r')
    564 		crmod = 1;
    565 	if ((upper && !lower && !LC) || UC)
    566 		for (np = name; *np; np++)
    567 			*np = tolower((unsigned char)*np);
    568 	return (1 + ppp_connection);
    569 }
    570 
    571 static void
    572 xputs(const char *s)
    573 {
    574 	while (*s)
    575 		putchr(*s++);
    576 }
    577 
    578 char	outbuf[OBUFSIZ];
    579 size_t	obufcnt = 0;
    580 
    581 static int
    582 putchr(int cc)
    583 {
    584 	unsigned char c;
    585 
    586 	c = cc;
    587 	if (!NP) {
    588 		c |= partab[c&0177] & 0200;
    589 		if (OP)
    590 			c ^= 0200;
    591 	}
    592 	if (!UB) {
    593 		outbuf[obufcnt++] = c;
    594 		if (obufcnt >= OBUFSIZ)
    595 			oflush();
    596 		return 1;
    597 	}
    598 	return write(STDOUT_FILENO, &c, 1);
    599 }
    600 
    601 static void
    602 oflush(void)
    603 {
    604 	if (obufcnt)
    605 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
    606 	obufcnt = 0;
    607 }
    608 
    609 static void
    610 prompt(void)
    611 {
    612 
    613 	putf(LM);
    614 	if (CO)
    615 		putchr('\n');
    616 }
    617 
    618 static void
    619 putf(const char *cp)
    620 {
    621 	time_t t;
    622 	char *slash, db[100];
    623 
    624 	while (*cp) {
    625 		if (*cp != '%') {
    626 			putchr(*cp++);
    627 			continue;
    628 		}
    629 		switch (*++cp) {
    630 
    631 		case 't':
    632 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
    633 				slash = strrchr(ttyn, '/');
    634 			if (slash == NULL)
    635 				xputs(ttyn);
    636 			else
    637 				xputs(&slash[1]);
    638 			break;
    639 
    640 		case 'h':
    641 			xputs(editedhost);
    642 			break;
    643 
    644 		case 'd':
    645 			(void)time(&t);
    646 			(void)strftime(db, sizeof(db),
    647 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
    648 			xputs(db);
    649 			break;
    650 
    651 		case 's':
    652 			xputs(kerninfo.sysname);
    653 			break;
    654 
    655 		case 'm':
    656 			xputs(kerninfo.machine);
    657 			break;
    658 
    659 		case 'r':
    660 			xputs(kerninfo.release);
    661 			break;
    662 
    663 		case 'v':
    664 			xputs(kerninfo.version);
    665 			break;
    666 
    667 		case '%':
    668 			putchr('%');
    669 			break;
    670 		}
    671 		if (*cp)
    672 			cp++;
    673 	}
    674 }
    675 
    676 static void
    677 clearscreen(void)
    678 {
    679 	struct ttyent *typ;
    680 	int err;
    681 
    682 	if (rawttyn == NULL)
    683 		return;
    684 
    685 	typ = getttynam(rawttyn);
    686 
    687 	if ((typ == NULL) || (typ->ty_type == NULL) ||
    688 	    (typ->ty_type[0] == 0))
    689 		return;
    690 
    691 	if (setupterm(typ->ty_type, 0, &err) == ERR)
    692 		return;
    693 
    694 	if (clear_screen)
    695 		putpad(clear_screen);
    696 
    697 	del_curterm(cur_term);
    698 	cur_term = NULL;
    699 }
    700