Home | History | Annotate | Line # | Download | only in getty
main.c revision 1.65
      1 /*	$NetBSD: main.c,v 1.65 2016/03/17 00:21:04 christos 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.65 2016/03/17 00:21:04 christos 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;
    187 	volatile int first_time = 1;
    188 	struct rlimit limit;
    189 	struct passwd *pw;
    190 	int rval;
    191 	/* this is used past the siglongjmp, so make sure it is not cached
    192 	   in registers that might become invalid. */
    193 	volatile int uugetty = 0;
    194 	const char * volatile tname = "default";
    195 
    196 	(void)signal(SIGINT, SIG_IGN);
    197 	openlog("getty", LOG_PID, LOG_AUTH);
    198 	(void)gethostname(hostname, sizeof(hostname));
    199 	hostname[sizeof(hostname) - 1] = '\0';
    200 	if (hostname[0] == '\0')
    201 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
    202 	(void)uname(&kerninfo);
    203 
    204 	progname = getprogname();
    205 	if (progname[0] == 'u' && progname[1] == 'u')
    206 		uugetty = 1;
    207 
    208 	/*
    209 	 * Find id of uucp login (if present) so we can chown tty properly.
    210 	 */
    211 	if (uugetty && (pw = getpwnam("uucp")))
    212 		ttyowner = pw->pw_uid;
    213 	else
    214 		ttyowner = 0;
    215 
    216 	/*
    217 	 * Limit running time to deal with broken or dead lines.
    218 	 */
    219 	(void)signal(SIGXCPU, timeoverrun);
    220 	limit.rlim_max = RLIM_INFINITY;
    221 	limit.rlim_cur = GETTY_TIMEOUT;
    222 	(void)setrlimit(RLIMIT_CPU, &limit);
    223 
    224 	/*
    225 	 * The following is a work around for vhangup interactions
    226 	 * which cause great problems getting window systems started.
    227 	 * If the tty line is "-", we do the old style getty presuming
    228 	 * that the file descriptors are already set up for us.
    229 	 * J. Gettys - MIT Project Athena.
    230 	 */
    231 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
    232 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
    233 	}
    234 	else {
    235 		int i;
    236 
    237 		rawttyn = argv[2];
    238 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
    239 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
    240 		if (uugetty)  {
    241 			(void)chown(ttyn, ttyowner, 0);
    242 			(void)strlcpy(lockfile, _PATH_LOCK,
    243 				sizeof(lockfile));
    244 			(void)strlcat(lockfile, argv[2],
    245 				sizeof(lockfile));
    246 			/*
    247 			 * wait for lockfiles to go away before we try
    248 			 * to open
    249 			 */
    250 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
    251 				syslog(LOG_ERR,
    252 					"%s: can't create lockfile", ttyn);
    253 				exit(1);
    254 			}
    255 			(void)unlink(lockfile);
    256 		}
    257 		if (strcmp(argv[0], "+") != 0) {
    258 			(void)chown(ttyn, ttyowner, 0);
    259 			(void)chmod(ttyn, 0600);
    260 			(void)revoke(ttyn);
    261 			if (ttyaction(ttyn, "getty", "root"))
    262 				syslog(LOG_WARNING, "%s: ttyaction failed",
    263 					ttyn);
    264 			/*
    265 			 * Delay the open so DTR stays down long enough
    266 			 * to be detected.
    267 			 */
    268 			(void)sleep(2);
    269 			while ((i = open(ttyn, O_RDWR)) == -1) {
    270 				if ((repcnt % 10 == 0) &&
    271 				    (errno != ENXIO || !failopenlogged)) {
    272 					syslog(LOG_WARNING, "%s: %m", ttyn);
    273 					closelog();
    274 					failopenlogged = 1;
    275 				}
    276 				repcnt++;
    277 				(void)sleep(60);
    278 			}
    279 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
    280 				syslog(LOG_ERR, "%s: can't create lockfile",
    281 					ttyn);
    282 				exit(1);
    283 			}
    284 			if (uugetty)
    285 				(void)chown(lockfile, ttyowner, 0);
    286 			(void)login_tty(i);
    287 		}
    288 	}
    289 
    290 	/* Start with default tty settings */
    291 	if (tcgetattr(0, &tmode) < 0) {
    292 		syslog(LOG_ERR, "%s: %m", ttyn);
    293 		exit(1);
    294 	}
    295 	omode = tmode;
    296 
    297 	gettable("default", defent);
    298 	gendefaults();
    299 	if (argc > 1)
    300 		tname = argv[1];
    301 	for (;;) {
    302 		int off;
    303 
    304 		rval = 0;
    305 		gettable(tname, tabent);
    306 		if (OPset || EPset || APset)
    307 			APset++, OPset++, EPset++;
    308 		setdefaults();
    309 		off = 0;
    310 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
    311 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
    312 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
    313 
    314 		if (IS)
    315 			(void)cfsetispeed(&tmode, (speed_t)IS);
    316 		else if (SP)
    317 			(void)cfsetispeed(&tmode, (speed_t)SP);
    318 		if (OS)
    319 			(void)cfsetospeed(&tmode, (speed_t)OS);
    320 		else if (SP)
    321 			(void)cfsetospeed(&tmode, (speed_t)SP);
    322 		setflags(0);
    323 		setchars();
    324 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    325 			syslog(LOG_ERR, "%s: %m", ttyn);
    326 			exit(1);
    327 		}
    328 		if (AB) {
    329 			tname = autobaud();
    330 			continue;
    331 		}
    332 		if (PS) {
    333 			tname = portselector();
    334 			continue;
    335 		}
    336 		if (CS)
    337 			clearscreen();
    338 		if (CL && *CL)
    339 			putpad(CL);
    340 		edithost(HE);
    341 
    342 		/*
    343 		 * If this is the first time through this, and an
    344 		 * issue file has been given, then send it.
    345 		 */
    346 		if (first_time != 0 && IF != NULL) {
    347 			char buf[_POSIX2_LINE_MAX];
    348 			FILE *fp;
    349 
    350 			if ((fp = fopen(IF, "r")) != NULL) {
    351 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
    352 					putf(buf);
    353 				(void)fclose(fp);
    354 			}
    355 		}
    356 		first_time = 0;
    357 
    358 		if (IM && *IM)
    359 			putf(IM);
    360 		oflush();
    361 		if (sigsetjmp(timeout, 1)) {
    362 			tmode.c_ispeed = tmode.c_ospeed = 0;
    363 			(void)tcsetattr(0, TCSANOW, &tmode);
    364 			exit(1);
    365 		}
    366 		if (TO) {
    367 			(void)signal(SIGALRM, dingdong);
    368 			(void)alarm((unsigned int)TO);
    369 		}
    370 		if (NN) {
    371 			name[0] = '\0';
    372 			lower = 1;
    373 			upper = digit_or_punc = 0;
    374 		} else if (AL) {
    375 			const char *p = AL;
    376 			char *q = name;
    377 
    378 			while (*p && q < &name[sizeof name - 1]) {
    379 				if (isupper((unsigned char)*p))
    380 					upper = 1;
    381 				else if (islower((unsigned char)*p))
    382 					lower = 1;
    383 				else if (isdigit((unsigned char)*p))
    384 					digit_or_punc = 1;
    385 				*q++ = *p++;
    386 			}
    387 		} else if ((rval = getname()) == 2) {
    388 			setflags(2);
    389 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
    390 			syslog(LOG_ERR, "%s: %m", PP);
    391 			exit(1);
    392 		}
    393 
    394 		if (rval || AL || NN) {
    395 			int i;
    396 
    397 			oflush();
    398 			(void)alarm(0);
    399 			(void)signal(SIGALRM, SIG_DFL);
    400 			if (name[0] == '-') {
    401 				xputs("user names may not start with '-'.");
    402 				continue;
    403 			}
    404 			if (!(upper || lower || digit_or_punc))
    405 				continue;
    406 			setflags(2);
    407 			if (crmod) {
    408 				tmode.c_iflag |= ICRNL;
    409 				tmode.c_oflag |= ONLCR;
    410 			}
    411 #if XXX
    412 			if (upper || UC)
    413 				tmode.sg_flags |= LCASE;
    414 			if (lower || LC)
    415 				tmode.sg_flags &= ~LCASE;
    416 #endif
    417 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    418 				syslog(LOG_ERR, "%s: %m", ttyn);
    419 				exit(1);
    420 			}
    421 			(void)signal(SIGINT, SIG_DFL);
    422 			for (i = 0; envp[i] != NULL; i++)
    423 				env[i] = envp[i];
    424 			makeenv(&env[i]);
    425 
    426 			limit.rlim_max = RLIM_INFINITY;
    427 			limit.rlim_cur = RLIM_INFINITY;
    428 			(void)setrlimit(RLIMIT_CPU, &limit);
    429 			if (NN)
    430 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    431 				    NULL, env);
    432 			else
    433 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    434 				    "--", name, NULL, env);
    435 			syslog(LOG_ERR, "%s: %m", LO);
    436 			exit(1);
    437 		}
    438 		(void)alarm(0);
    439 		(void)signal(SIGALRM, SIG_DFL);
    440 		(void)signal(SIGINT, SIG_IGN);
    441 		if (NX && *NX)
    442 			tname = NX;
    443 		if (uugetty)
    444 			(void)unlink(lockfile);
    445 	}
    446 }
    447 
    448 static int
    449 getname(void)
    450 {
    451 	int c;
    452 	char *np;
    453 	unsigned char cs;
    454 	int ppp_state, ppp_connection;
    455 
    456 	/*
    457 	 * Interrupt may happen if we use CBREAK mode
    458 	 */
    459 	if (sigsetjmp(intrupt, 1)) {
    460 		(void)signal(SIGINT, SIG_IGN);
    461 		return (0);
    462 	}
    463 	(void)signal(SIGINT, interrupt);
    464 	setflags(1);
    465 	prompt();
    466 	if (PF > 0) {
    467 		oflush();
    468 		(void)sleep((unsigned int)PF);
    469 		PF = 0;
    470 	}
    471 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    472 		syslog(LOG_ERR, "%s: %m", ttyn);
    473 		exit(1);
    474 	}
    475 	crmod = digit_or_punc = lower = upper = 0;
    476 	ppp_state = ppp_connection = 0;
    477 	np = name;
    478 	for (;;) {
    479 		oflush();
    480 		if (read(STDIN_FILENO, &cs, 1) <= 0)
    481 			exit(0);
    482 		if ((c = cs&0177) == 0)
    483 			return (0);
    484 
    485 		/*
    486 		 * PPP detection state machine..
    487 		 * Look for sequences:
    488 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
    489 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
    490 		 * See RFC1662.
    491 		 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
    492 		 * and Erik 'PPP' Olson <eriko (at) wrq.com>
    493 		 */
    494 		if (PP && cs == PPP_FRAME) {
    495 			ppp_state = 1;
    496 		} else if (ppp_state == 1 && cs == PPP_STATION) {
    497 			ppp_state = 2;
    498 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
    499 			ppp_state = 3;
    500 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
    501 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
    502 			ppp_state = 4;
    503 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
    504 			ppp_state = 5;
    505 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
    506 			ppp_connection = 1;
    507 			break;
    508 		} else {
    509 			ppp_state = 0;
    510 		}
    511 
    512 		if (c == EOT)
    513 			exit(1);
    514 		if (c == '\r' || c == '\n' ||
    515 		    np >= &name[LOGIN_NAME_MAX - 1]) {
    516 			*np = '\0';
    517 			putf("\r\n");
    518 			break;
    519 		}
    520 		if (islower(c))
    521 			lower = 1;
    522 		else if (isupper(c))
    523 			upper = 1;
    524 		else if (c == ERASE || c == '#' || c == '\b') {
    525 			if (np > name) {
    526 				np--;
    527 				if (cfgetospeed(&tmode) >= 1200)
    528 					xputs("\b \b");
    529 				else
    530 					putchr(cs);
    531 			}
    532 			continue;
    533 		} else if (c == KILL || c == '@') {
    534 			putchr(cs);
    535 			putchr('\r');
    536 			if (cfgetospeed(&tmode) < 1200)
    537 				putchr('\n');
    538 			/* this is the way they do it down under ... */
    539 			else if (np > name)
    540 				xputs(
    541 				    "                                     \r");
    542 			prompt();
    543 			np = name;
    544 			continue;
    545 		} else if (isdigit(c) || c == '_')
    546 			digit_or_punc = 1;
    547 		if (IG && (c <= ' ' || c > 0176))
    548 			continue;
    549 		*np++ = c;
    550 		putchr(cs);
    551 
    552 		/*
    553 		 * An MS-Windows direct connect PPP "client" won't send its
    554 		 * first PPP packet until we respond to its "CLIENT" poll
    555 		 * with a CRLF sequence.  We cater to yet another broken
    556 		 * implementation of a previously-standard protocol...
    557 		 */
    558 		*np = '\0';
    559 		if (strstr(name, "CLIENT"))
    560 			putf("\r\n");
    561 	}
    562 	(void)signal(SIGINT, SIG_IGN);
    563 	*np = 0;
    564 	if (c == '\r')
    565 		crmod = 1;
    566 	if ((upper && !lower && !LC) || UC)
    567 		for (np = name; *np; np++)
    568 			*np = tolower((unsigned char)*np);
    569 	return (1 + ppp_connection);
    570 }
    571 
    572 static void
    573 xputs(const char *s)
    574 {
    575 	while (*s)
    576 		putchr(*s++);
    577 }
    578 
    579 char	outbuf[OBUFSIZ];
    580 size_t	obufcnt = 0;
    581 
    582 static int
    583 putchr(int cc)
    584 {
    585 	unsigned char c;
    586 
    587 	c = cc;
    588 	if (!NP) {
    589 		c |= partab[c&0177] & 0200;
    590 		if (OP)
    591 			c ^= 0200;
    592 	}
    593 	if (!UB) {
    594 		outbuf[obufcnt++] = c;
    595 		if (obufcnt >= OBUFSIZ)
    596 			oflush();
    597 		return 1;
    598 	}
    599 	return write(STDOUT_FILENO, &c, 1);
    600 }
    601 
    602 static void
    603 oflush(void)
    604 {
    605 	if (obufcnt)
    606 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
    607 	obufcnt = 0;
    608 }
    609 
    610 static void
    611 prompt(void)
    612 {
    613 
    614 	putf(LM);
    615 	if (CO)
    616 		putchr('\n');
    617 }
    618 
    619 static void
    620 putf(const char *cp)
    621 {
    622 	time_t t;
    623 	char *slash, db[100];
    624 
    625 	while (*cp) {
    626 		if (*cp != '%') {
    627 			putchr(*cp++);
    628 			continue;
    629 		}
    630 		switch (*++cp) {
    631 
    632 		case 't':
    633 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
    634 				slash = strrchr(ttyn, '/');
    635 			if (slash == NULL)
    636 				xputs(ttyn);
    637 			else
    638 				xputs(&slash[1]);
    639 			break;
    640 
    641 		case 'h':
    642 			xputs(editedhost);
    643 			break;
    644 
    645 		case 'd':
    646 			(void)time(&t);
    647 			(void)strftime(db, sizeof(db),
    648 			    "%l:%M%p on %A, %d %B %Y", localtime(&t));
    649 			xputs(db);
    650 			break;
    651 
    652 		case 's':
    653 			xputs(kerninfo.sysname);
    654 			break;
    655 
    656 		case 'm':
    657 			xputs(kerninfo.machine);
    658 			break;
    659 
    660 		case 'r':
    661 			xputs(kerninfo.release);
    662 			break;
    663 
    664 		case 'v':
    665 			xputs(kerninfo.version);
    666 			break;
    667 
    668 		case '%':
    669 			putchr('%');
    670 			break;
    671 		}
    672 		if (*cp)
    673 			cp++;
    674 	}
    675 }
    676 
    677 static void
    678 clearscreen(void)
    679 {
    680 	struct ttyent *typ;
    681 	int err;
    682 
    683 	if (rawttyn == NULL)
    684 		return;
    685 
    686 	typ = getttynam(rawttyn);
    687 
    688 	if ((typ == NULL) || (typ->ty_type == NULL) ||
    689 	    (typ->ty_type[0] == 0))
    690 		return;
    691 
    692 	if (setupterm(typ->ty_type, 0, &err) == ERR)
    693 		return;
    694 
    695 	if (clear_screen)
    696 		putpad(clear_screen);
    697 
    698 	del_curterm(cur_term);
    699 	cur_term = NULL;
    700 }
    701