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