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