Home | History | Annotate | Line # | Download | only in getty
main.c revision 1.33
      1 /*	$NetBSD: main.c,v 1.33 2000/10/08 16:04:02 bjh21 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.33 2000/10/08 16:04:02 bjh21 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 
     74 #include "gettytab.h"
     75 #include "pathnames.h"
     76 #include "extern.h"
     77 
     78 extern char *__progname;
     79 
     80 /*
     81  * Set the amount of running time that getty should accumulate
     82  * before deciding that something is wrong and exit.
     83  */
     84 #define GETTY_TIMEOUT	60 /* seconds */
     85 
     86 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
     87 
     88 #define PPP_FRAME           0x7e  /* PPP Framing character */
     89 #define PPP_STATION         0xff  /* "All Station" character */
     90 #define PPP_ESCAPE          0x7d  /* Escape Character */
     91 #define PPP_CONTROL         0x03  /* PPP Control Field */
     92 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
     93 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
     94 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
     95 
     96 struct termios tmode, omode;
     97 
     98 int crmod, digit, lower, upper;
     99 
    100 char	hostname[MAXHOSTNAMELEN + 1];
    101 struct	utsname kerninfo;
    102 char	name[LOGIN_NAME_MAX];
    103 char	dev[] = _PATH_DEV;
    104 char	ttyn[32];
    105 char	lockfile[512];
    106 uid_t	ttyowner;
    107 
    108 #define	OBUFSIZ		128
    109 #define	TABBUFSIZ	512
    110 
    111 char	defent[TABBUFSIZ];
    112 char	tabent[TABBUFSIZ];
    113 
    114 char	*env[128];
    115 
    116 char partab[] = {
    117 	0001,0201,0201,0001,0201,0001,0001,0201,
    118 	0202,0004,0003,0205,0005,0206,0201,0001,
    119 	0201,0001,0001,0201,0001,0201,0201,0001,
    120 	0001,0201,0201,0001,0201,0001,0001,0201,
    121 	0200,0000,0000,0200,0000,0200,0200,0000,
    122 	0000,0200,0200,0000,0200,0000,0000,0200,
    123 	0000,0200,0200,0000,0200,0000,0000,0200,
    124 	0200,0000,0000,0200,0000,0200,0200,0000,
    125 	0200,0000,0000,0200,0000,0200,0200,0000,
    126 	0000,0200,0200,0000,0200,0000,0000,0200,
    127 	0000,0200,0200,0000,0200,0000,0000,0200,
    128 	0200,0000,0000,0200,0000,0200,0200,0000,
    129 	0000,0200,0200,0000,0200,0000,0000,0200,
    130 	0200,0000,0000,0200,0000,0200,0200,0000,
    131 	0200,0000,0000,0200,0000,0200,0200,0000,
    132 	0000,0200,0200,0000,0200,0000,0000,0201
    133 };
    134 
    135 #define	ERASE	tmode.c_cc[VERASE]
    136 #define	KILL	tmode.c_cc[VKILL]
    137 #define	EOT	tmode.c_cc[VEOF]
    138 
    139 static void	dingdong __P((int));
    140 static void	interrupt __P((int));
    141 void		timeoverrun __P((int));
    142 
    143 jmp_buf timeout;
    144 
    145 static void
    146 dingdong(signo)
    147 	int signo;
    148 {
    149 
    150 	alarm(0);
    151 	signal(SIGALRM, SIG_DFL);
    152 	longjmp(timeout, 1);
    153 }
    154 
    155 jmp_buf	intrupt;
    156 
    157 static void
    158 interrupt(signo)
    159 	int signo;
    160 {
    161 
    162 	signal(SIGINT, interrupt);
    163 	longjmp(intrupt, 1);
    164 }
    165 
    166 /*
    167  * Action to take when getty is running too long.
    168  */
    169 void
    170 timeoverrun(signo)
    171 	int signo;
    172 {
    173 
    174 	syslog(LOG_ERR, "getty exiting due to excessive running time");
    175 	exit(1);
    176 }
    177 
    178 int		main __P((int, char **));
    179 static int	getname __P((void));
    180 static void	oflush __P((void));
    181 static void	prompt __P((void));
    182 static void	putchr __P((int));
    183 static void	putf __P((const char *));
    184 static void	putpad __P((const char *));
    185 static void	xputs __P((const char *));
    186 
    187 int
    188 main(argc, argv)
    189 	int argc;
    190 	char *argv[];
    191 {
    192 	extern char **environ;
    193 	char *tname;
    194 	int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
    195 	struct rlimit limit;
    196 	struct passwd *pw;
    197         int rval;
    198 
    199 #ifdef __GNUC__
    200 	(void)&tname;		/* XXX gcc -Wall */
    201 #endif
    202 
    203 	signal(SIGINT, SIG_IGN);
    204 /*
    205 	signal(SIGQUIT, SIG_DFL);
    206 */
    207 	openlog("getty", LOG_ODELAY|LOG_PID, LOG_AUTH);
    208 	gethostname(hostname, sizeof(hostname));
    209 	hostname[sizeof(hostname) - 1] = '\0';
    210 	if (hostname[0] == '\0')
    211 		strcpy(hostname, "Amnesiac");
    212 	uname(&kerninfo);
    213 
    214 	if (__progname[0] == 'u' && __progname[1] == 'u')
    215 		uugetty = 1;
    216 
    217 	/*
    218 	 * Find id of uucp login (if present) so we can chown tty properly.
    219 	 */
    220 	if (uugetty && (pw = getpwnam("uucp")))
    221 		ttyowner = pw->pw_uid;
    222 	else
    223 		ttyowner = 0;
    224 
    225 	/*
    226 	 * Limit running time to deal with broken or dead lines.
    227 	 */
    228 	(void)signal(SIGXCPU, timeoverrun);
    229 	limit.rlim_max = RLIM_INFINITY;
    230 	limit.rlim_cur = GETTY_TIMEOUT;
    231 	(void)setrlimit(RLIMIT_CPU, &limit);
    232 
    233 	/*
    234 	 * The following is a work around for vhangup interactions
    235 	 * which cause great problems getting window systems started.
    236 	 * If the tty line is "-", we do the old style getty presuming
    237 	 * that the file descriptors are already set up for us.
    238 	 * J. Gettys - MIT Project Athena.
    239 	 */
    240 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
    241 	    strlcpy(ttyn, ttyname(0), sizeof(ttyn));
    242 	}
    243 	else {
    244 	    int i;
    245 
    246 	    strlcpy(ttyn, dev, sizeof(ttyn));
    247 	    strlcat(ttyn, argv[2], sizeof(ttyn));
    248 
    249 	    if (uugetty)  {
    250 		chown(ttyn, ttyowner, 0);
    251 		strcpy(lockfile, _PATH_LOCK);
    252 		strlcat(lockfile, argv[2], sizeof(lockfile));
    253 		/* wait for lockfiles to go away before we try to open */
    254 		if ( pidlock(lockfile, 0, 0, 0) != 0 )  {
    255 		    syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
    256 		    exit(1);
    257 		}
    258 		unlink(lockfile);
    259 	    }
    260 	    if (strcmp(argv[0], "+") != 0) {
    261 		chown(ttyn, ttyowner, 0);
    262 		chmod(ttyn, 0600);
    263 		revoke(ttyn);
    264 		if (ttyaction(ttyn, "getty", "root"))
    265 			syslog(LOG_ERR,"%s: ttyaction failed", ttyn);
    266 		/*
    267 		 * Delay the open so DTR stays down long enough to be detected.
    268 		 */
    269 		sleep(2);
    270 		while ((i = open(ttyn, O_RDWR)) == -1) {
    271 			if ((repcnt % 10 == 0) &&
    272 			    (errno != ENXIO || !failopenlogged)) {
    273 				syslog(LOG_ERR, "%s: %m", ttyn);
    274 				closelog();
    275 				failopenlogged = 1;
    276 			}
    277 			repcnt++;
    278 			sleep(60);
    279 		}
    280 		if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
    281 			syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
    282 			exit(1);
    283 		}
    284 		(void) chown(lockfile, ttyowner, 0);
    285 		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 	tname = "default";
    299 	if (argc > 1)
    300 		tname = argv[1];
    301 	for (;;) {
    302 		int off;
    303 
    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 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
    311 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
    312 
    313 		if (IS)
    314 			cfsetispeed(&tmode, IS);
    315 		else if (SP)
    316 			cfsetispeed(&tmode, SP);
    317 		if (OS)
    318 			cfsetospeed(&tmode, OS);
    319 		else if (SP)
    320 			cfsetospeed(&tmode, 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 (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 *fd;
    346 
    347 			if ((fd = fopen(IF, "r")) != NULL) {
    348 				while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
    349 					putf(buf);
    350 				fclose(fd);
    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 			signal(SIGALRM, dingdong);
    365 			alarm(TO);
    366 		}
    367 		if (AL) {
    368 			const char *p = AL;
    369 			char *q = name;
    370 
    371 			while (*p && q < &name[sizeof name - 1]) {
    372 				if (isupper(*p))
    373 					upper = 1;
    374 				else if (islower(*p))
    375 					lower = 1;
    376 				else if (isdigit(*p))
    377 					digit++;
    378 				*q++ = *p++;
    379 			}
    380 		} else if ((rval = getname()) == 2) {
    381 		        execle(PP, "ppplogin", ttyn, (char *) 0, env);
    382 		        syslog(LOG_ERR, "%s: %m", PP);
    383 		        exit(1);
    384 		}
    385 
    386 		if (rval || AL) {
    387 			int i;
    388 
    389 			oflush();
    390 			alarm(0);
    391 			signal(SIGALRM, SIG_DFL);
    392 			if (name[0] == '-') {
    393 				xputs("user names may not start with '-'.");
    394 				continue;
    395 			}
    396 			if (!(upper || lower || digit))
    397 				continue;
    398 			setflags(2);
    399 			if (crmod) {
    400 				tmode.c_iflag |= ICRNL;
    401 				tmode.c_oflag |= ONLCR;
    402 			}
    403 #if XXX
    404 			if (upper || UC)
    405 				tmode.sg_flags |= LCASE;
    406 			if (lower || LC)
    407 				tmode.sg_flags &= ~LCASE;
    408 #endif
    409 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    410 				syslog(LOG_ERR, "%s: %m", ttyn);
    411 				exit(1);
    412 			}
    413 			signal(SIGINT, SIG_DFL);
    414 			for (i = 0; environ[i] != (char *)0; i++)
    415 				env[i] = environ[i];
    416 			makeenv(&env[i]);
    417 
    418 			limit.rlim_max = RLIM_INFINITY;
    419 			limit.rlim_cur = RLIM_INFINITY;
    420 			(void)setrlimit(RLIMIT_CPU, &limit);
    421 			execle(LO, "login", AL ? "-fp" : "-p", "--", name,
    422 			    (char *)0, env);
    423 			syslog(LOG_ERR, "%s: %m", LO);
    424 			exit(1);
    425 		}
    426 		alarm(0);
    427 		signal(SIGALRM, SIG_DFL);
    428 		signal(SIGINT, SIG_IGN);
    429 		if (NX && *NX)
    430 			tname = NX;
    431 		unlink(lockfile);
    432 	}
    433 }
    434 
    435 static int
    436 getname()
    437 {
    438 	int c;
    439 	char *np;
    440 	unsigned char cs;
    441 	int ppp_state, ppp_connection;
    442 
    443 	/*
    444 	 * Interrupt may happen if we use CBREAK mode
    445 	 */
    446 	if (setjmp(intrupt)) {
    447 		signal(SIGINT, SIG_IGN);
    448 		return (0);
    449 	}
    450 	signal(SIGINT, interrupt);
    451 	setflags(1);
    452 	prompt();
    453 	if (PF > 0) {
    454 		oflush();
    455 		sleep(PF);
    456 		PF = 0;
    457 	}
    458 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    459 		syslog(LOG_ERR, "%s: %m", ttyn);
    460 		exit(1);
    461 	}
    462 	crmod = digit = lower = upper = 0;
    463         ppp_state = ppp_connection = 0;
    464 	np = name;
    465 	for (;;) {
    466 		oflush();
    467 		if (read(STDIN_FILENO, &cs, 1) <= 0)
    468 			exit(0);
    469 		if ((c = cs&0177) == 0)
    470 			return (0);
    471 
    472 		/*
    473 		 * PPP detection state machine..
    474 		 * Look for sequences:
    475 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
    476 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
    477 		 * See RFC1662.
    478 		 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
    479 		 * and Erik 'PPP' Olson <eriko (at) wrq.com>
    480 		 */
    481 		if (PP && cs == PPP_FRAME) {
    482 			ppp_state = 1;
    483 		} else if (ppp_state == 1 && cs == PPP_STATION) {
    484 			ppp_state = 2;
    485 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
    486 			ppp_state = 3;
    487 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
    488 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
    489 			ppp_state = 4;
    490 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
    491 			ppp_state = 5;
    492 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
    493 			ppp_connection = 1;
    494 			break;
    495 		} else {
    496 			ppp_state = 0;
    497 		}
    498 
    499 		if (c == EOT)
    500 			exit(1);
    501 		if (c == '\r' || c == '\n' ||
    502 		    np >= &name[LOGIN_NAME_MAX - 1]) {
    503 			*np = '\0';
    504 			putf("\r\n");
    505 			break;
    506 		}
    507 		if (islower(c))
    508 			lower = 1;
    509 		else if (isupper(c))
    510 			upper = 1;
    511 		else if (c == ERASE || c == '#' || c == '\b') {
    512 			if (np > name) {
    513 				np--;
    514 				if (cfgetospeed(&tmode) >= 1200)
    515 					xputs("\b \b");
    516 				else
    517 					putchr(cs);
    518 			}
    519 			continue;
    520 		} else if (c == KILL || c == '@') {
    521 			putchr(cs);
    522 			putchr('\r');
    523 			if (cfgetospeed(&tmode) < 1200)
    524 				putchr('\n');
    525 			/* this is the way they do it down under ... */
    526 			else if (np > name)
    527 				xputs(
    528 				    "                                     \r");
    529 			prompt();
    530 			np = name;
    531 			continue;
    532 		} else if (isdigit(c))
    533 			digit++;
    534 		if (IG && (c <= ' ' || c > 0176))
    535 			continue;
    536 		*np++ = c;
    537 		putchr(cs);
    538 	}
    539 	signal(SIGINT, SIG_IGN);
    540 	*np = 0;
    541 	if (c == '\r')
    542 		crmod = 1;
    543 	if ((upper && !lower && !LC) || UC)
    544 		for (np = name; *np; np++)
    545 			if (isupper(*np))
    546 				*np = tolower(*np);
    547 	return (1 + ppp_connection);
    548 }
    549 
    550 static void
    551 putpad(s)
    552 	const char *s;
    553 {
    554 	int pad = 0;
    555 	speed_t ospeed = cfgetospeed(&tmode);
    556 
    557 	if (isdigit(*s)) {
    558 		while (isdigit(*s)) {
    559 			pad *= 10;
    560 			pad += *s++ - '0';
    561 		}
    562 		pad *= 10;
    563 		if (*s == '.' && isdigit(s[1])) {
    564 			pad += s[1] - '0';
    565 			s += 2;
    566 		}
    567 	}
    568 
    569 	xputs(s);
    570 	/*
    571 	 * If no delay needed, or output speed is
    572 	 * not comprehensible, then don't try to delay.
    573 	 */
    574 	if (pad == 0 || ospeed <= 0)
    575 		return;
    576 
    577 	/*
    578 	 * Round up by a half a character frame, and then do the delay.
    579 	 * Too bad there are no user program accessible programmed delays.
    580 	 * Transmitting pad characters slows many terminals down and also
    581 	 * loads the system.
    582 	 */
    583 	pad = (pad * ospeed + 50000) / 100000;
    584 	while (pad--)
    585 		putchr(*PC);
    586 }
    587 
    588 static void
    589 xputs(s)
    590 	const char *s;
    591 {
    592 	while (*s)
    593 		putchr(*s++);
    594 }
    595 
    596 char	outbuf[OBUFSIZ];
    597 int	obufcnt = 0;
    598 
    599 static void
    600 putchr(cc)
    601 	int cc;
    602 {
    603 	char c;
    604 
    605 	c = cc;
    606 	if (!NP) {
    607 		c |= partab[c&0177] & 0200;
    608 		if (OP)
    609 			c ^= 0200;
    610 	}
    611 	if (!UB) {
    612 		outbuf[obufcnt++] = c;
    613 		if (obufcnt >= OBUFSIZ)
    614 			oflush();
    615 	} else
    616 		write(STDOUT_FILENO, &c, 1);
    617 }
    618 
    619 static void
    620 oflush()
    621 {
    622 	if (obufcnt)
    623 		write(STDOUT_FILENO, outbuf, obufcnt);
    624 	obufcnt = 0;
    625 }
    626 
    627 static void
    628 prompt()
    629 {
    630 
    631 	putf(LM);
    632 	if (CO)
    633 		putchr('\n');
    634 }
    635 
    636 static void
    637 putf(cp)
    638 	const char *cp;
    639 {
    640 	extern char editedhost[];
    641 	time_t t;
    642 	char *slash, db[100];
    643 
    644 	while (*cp) {
    645 		if (*cp != '%') {
    646 			putchr(*cp++);
    647 			continue;
    648 		}
    649 		switch (*++cp) {
    650 
    651 		case 't':
    652 			slash = strrchr(ttyn, '/');
    653 			if (slash == NULL)
    654 				xputs(ttyn);
    655 			else
    656 				xputs(&slash[1]);
    657 			break;
    658 
    659 		case 'h':
    660 			xputs(editedhost);
    661 			break;
    662 
    663 		case 'd': {
    664 			static char fmt[] = "%l:% %p on %A, %d %B %Y";
    665 
    666 			fmt[4] = 'M';		/* I *hate* SCCS... */
    667 			(void)time(&t);
    668 			(void)strftime(db, sizeof(db), fmt, localtime(&t));
    669 			xputs(db);
    670 			break;
    671 
    672 		case 's':
    673 			xputs(kerninfo.sysname);
    674 			break;
    675 
    676 		case 'm':
    677 			xputs(kerninfo.machine);
    678 			break;
    679 
    680 		case 'r':
    681 			xputs(kerninfo.release);
    682 			break;
    683 
    684 		case 'v':
    685 			xputs(kerninfo.version);
    686 			break;
    687 		}
    688 
    689 		case '%':
    690 			putchr('%');
    691 			break;
    692 		}
    693 		if (*cp)
    694 			cp++;
    695 	}
    696 }
    697