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