main.c revision 91216
1/*-
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1980, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)from: main.c	8.1 (Berkeley) 6/20/93";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/libexec/getty/main.c 91216 2002-02-25 02:30:04Z bde $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/ioctl.h>
50#include <sys/time.h>
51#include <sys/resource.h>
52#include <sys/stat.h>
53#include <sys/ttydefaults.h>
54#include <sys/utsname.h>
55
56#include <ctype.h>
57#include <errno.h>
58#include <fcntl.h>
59#include <locale.h>
60#include <libutil.h>
61#include <setjmp.h>
62#include <signal.h>
63#include <stdlib.h>
64#include <string.h>
65#include <syslog.h>
66#include <termios.h>
67#include <time.h>
68#include <unistd.h>
69
70#include "extern.h"
71#include "gettytab.h"
72#include "pathnames.h"
73
74/*
75 * Set the amount of running time that getty should accumulate
76 * before deciding that something is wrong and exit.
77 */
78#define GETTY_TIMEOUT	60 /* seconds */
79
80#undef CTRL
81#define CTRL(x)  (x&037)
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
93struct termios tmode, omode;
94
95int crmod, digit, lower, upper;
96
97char	hostname[MAXHOSTNAMELEN];
98char	name[MAXLOGNAME*3];
99char	dev[] = _PATH_DEV;
100char	ttyn[32];
101
102#define	OBUFSIZ		128
103#define	TABBUFSIZ	512
104
105char	defent[TABBUFSIZ];
106char	tabent[TABBUFSIZ];
107
108char	*env[128];
109
110char partab[] = {
111	0001,0201,0201,0001,0201,0001,0001,0201,
112	0202,0004,0003,0205,0005,0206,0201,0001,
113	0201,0001,0001,0201,0001,0201,0201,0001,
114	0001,0201,0201,0001,0201,0001,0001,0201,
115	0200,0000,0000,0200,0000,0200,0200,0000,
116	0000,0200,0200,0000,0200,0000,0000,0200,
117	0000,0200,0200,0000,0200,0000,0000,0200,
118	0200,0000,0000,0200,0000,0200,0200,0000,
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	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,0201
127};
128
129#define	ERASE	tmode.c_cc[VERASE]
130#define	KILL	tmode.c_cc[VKILL]
131#define	EOT	tmode.c_cc[VEOF]
132
133#define	puts	Gputs
134
135static void	dingdong(int);
136static int	getname(void);
137static void	interrupt(int);
138static void	oflush(void);
139static void	prompt(void);
140static void	putchr(int);
141static void	putf(const char *);
142static void	putpad(const char *);
143static void	puts(const char *);
144static void	timeoverrun(int);
145static char	*getline(int);
146static void	setttymode(const char *, int);
147static void	setdefttymode(const char *);
148static int	opentty(const char *, int);
149
150jmp_buf timeout;
151
152static void
153dingdong(int signo __unused)
154{
155	alarm(0);
156	longjmp(timeout, 1);
157}
158
159jmp_buf	intrupt;
160
161static void
162interrupt(int signo __unused)
163{
164	longjmp(intrupt, 1);
165}
166
167/*
168 * Action to take when getty is running too long.
169 */
170static void
171timeoverrun(int signo __unused)
172{
173
174	syslog(LOG_ERR, "getty exiting due to excessive running time");
175	exit(1);
176}
177
178int
179main(int argc, char *argv[])
180{
181	extern	char **environ;
182	const char *tname;
183	int first_sleep = 1, first_time = 1;
184	struct rlimit limit;
185	int rval;
186
187	signal(SIGINT, SIG_IGN);
188	signal(SIGQUIT, SIG_IGN);
189
190	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
191	gethostname(hostname, sizeof(hostname) - 1);
192	hostname[sizeof(hostname) - 1] = '\0';
193	if (hostname[0] == '\0')
194		strcpy(hostname, "Amnesiac");
195
196	/*
197	 * Limit running time to deal with broken or dead lines.
198	 */
199	(void)signal(SIGXCPU, timeoverrun);
200	limit.rlim_max = RLIM_INFINITY;
201	limit.rlim_cur = GETTY_TIMEOUT;
202	(void)setrlimit(RLIMIT_CPU, &limit);
203
204	gettable("default", defent);
205	gendefaults();
206	tname = "default";
207	if (argc > 1)
208		tname = argv[1];
209
210	/*
211	 * The following is a work around for vhangup interactions
212	 * which cause great problems getting window systems started.
213	 * If the tty line is "-", we do the old style getty presuming
214	 * that the file descriptors are already set up for us.
215	 * J. Gettys - MIT Project Athena.
216	 */
217	if (argc <= 2 || strcmp(argv[2], "-") == 0)
218	    strcpy(ttyn, ttyname(STDIN_FILENO));
219	else {
220	    strcpy(ttyn, dev);
221	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
222	    if (strcmp(argv[0], "+") != 0) {
223		chown(ttyn, 0, 0);
224		chmod(ttyn, 0600);
225		revoke(ttyn);
226
227		gettable(tname, tabent);
228
229		/* Init modem sequence has been specified
230		 */
231		if (IC) {
232			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
233				exit(1);
234			setdefttymode(tname);
235			if (getty_chat(IC, CT, DC) > 0) {
236				syslog(LOG_ERR, "modem init problem on %s", ttyn);
237				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
238				exit(1);
239			}
240		}
241
242		if (AC) {
243			int i, rfds;
244			struct timeval to;
245
246			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
247				exit(1);
248        		setdefttymode(tname);
249        		rfds = 1 << 0;	/* FD_SET */
250        		to.tv_sec = RT;
251        		to.tv_usec = 0;
252        		i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
253        			       (fd_set*)NULL, RT ? &to : NULL);
254        		if (i < 0) {
255				syslog(LOG_ERR, "select %s: %m", ttyn);
256			} else if (i == 0) {
257				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
258				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
259				exit(0);  /* recycle for init */
260			}
261			i = getty_chat(AC, CT, DC);
262			if (i > 0) {
263				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
264				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
265				exit(1);
266			}
267		} else { /* maybe blocking open */
268			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
269				exit(1);
270		}
271	    }
272	}
273
274	/* Start with default tty settings */
275	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
276		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
277		exit(1);
278	}
279	/*
280	 * Don't rely on the driver too much, and initialize crucial
281	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
282	 * the c_cc[] settings however, the console drivers might wish
283	 * to leave their idea of the preferred VERASE key value
284	 * there.
285	 */
286	tmode.c_iflag = TTYDEF_IFLAG;
287	tmode.c_oflag = TTYDEF_OFLAG;
288	tmode.c_lflag = TTYDEF_LFLAG;
289	tmode.c_cflag = TTYDEF_CFLAG;
290	tmode.c_cflag |= (NC ? CLOCAL : 0);
291	omode = tmode;
292
293	for (;;) {
294
295		/*
296		 * if a delay was specified then sleep for that
297		 * number of seconds before writing the initial prompt
298		 */
299		if (first_sleep && DE) {
300		    sleep(DE);
301		    /* remove any noise */
302		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
303		}
304		first_sleep = 0;
305
306		setttymode(tname, 0);
307		if (AB) {
308			tname = autobaud();
309			continue;
310		}
311		if (PS) {
312			tname = portselector();
313			continue;
314		}
315		if (CL && *CL)
316			putpad(CL);
317		edithost(HE);
318
319		/* if this is the first time through this, and an
320		   issue file has been given, then send it */
321		if (first_time && IF) {
322			int fd;
323
324			if ((fd = open(IF, O_RDONLY)) != -1) {
325				char * cp;
326
327				while ((cp = getline(fd)) != NULL) {
328					  putf(cp);
329				}
330				close(fd);
331			}
332		}
333		first_time = 0;
334
335		if (IM && *IM)
336			putf(IM);
337		if (setjmp(timeout)) {
338			cfsetispeed(&tmode, B0);
339			cfsetospeed(&tmode, B0);
340			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
341			exit(1);
342		}
343		if (TO) {
344			signal(SIGALRM, dingdong);
345			alarm(TO);
346		}
347		if (AL) {
348			const char *p = AL;
349			char *q = name;
350
351			while (*p && q < &name[sizeof name - 1]) {
352				if (isupper(*p))
353					upper = 1;
354				else if (islower(*p))
355					lower = 1;
356				else if (isdigit(*p))
357					digit++;
358				*q++ = *p++;
359			}
360		} else
361			rval = getname();
362		if (rval == 2) {
363			oflush();
364			alarm(0);
365			limit.rlim_max = RLIM_INFINITY;
366			limit.rlim_cur = RLIM_INFINITY;
367			(void)setrlimit(RLIMIT_CPU, &limit);
368			execle(PP, "ppplogin", ttyn, (char *) 0, env);
369			syslog(LOG_ERR, "%s: %m", PP);
370			exit(1);
371		} else if (rval || AL) {
372			int i;
373
374			oflush();
375			alarm(0);
376			signal(SIGALRM, SIG_DFL);
377			if (name[0] == '-') {
378				puts("user names may not start with '-'.");
379				continue;
380			}
381			if (!(upper || lower || digit))
382				continue;
383			set_flags(2);
384			if (crmod) {
385				tmode.c_iflag |= ICRNL;
386				tmode.c_oflag |= ONLCR;
387			}
388#if REALLY_OLD_TTYS
389			if (upper || UC)
390				tmode.sg_flags |= LCASE;
391			if (lower || LC)
392				tmode.sg_flags &= ~LCASE;
393#endif
394			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
395				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
396				exit(1);
397			}
398			signal(SIGINT, SIG_DFL);
399			for (i = 0; environ[i] != (char *)0; i++)
400				env[i] = environ[i];
401			makeenv(&env[i]);
402
403			limit.rlim_max = RLIM_INFINITY;
404			limit.rlim_cur = RLIM_INFINITY;
405			(void)setrlimit(RLIMIT_CPU, &limit);
406			execle(LO, "login", AL ? "-fp" : "-p", name,
407			    (char *) 0, env);
408			syslog(LOG_ERR, "%s: %m", LO);
409			exit(1);
410		}
411		alarm(0);
412		signal(SIGALRM, SIG_DFL);
413		signal(SIGINT, SIG_IGN);
414		if (NX && *NX)
415			tname = NX;
416	}
417}
418
419static int
420opentty(const char *tty, int flags)
421{
422	int i, j = 0;
423	int failopenlogged = 0;
424
425	while (j < 10 && (i = open(tty, flags)) == -1)
426	{
427		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
428			syslog(LOG_ERR, "open %s: %m", tty);
429			failopenlogged = 1;
430		}
431		j++;
432		sleep(60);
433	}
434	if (i == -1) {
435		syslog(LOG_ERR, "open %s: %m", tty);
436		return 0;
437	}
438	else {
439		if (login_tty(i) < 0) {
440			if (daemon(0,0) < 0) {
441				syslog(LOG_ERR,"daemon: %m");
442				close(i);
443				return 0;
444			}
445			if (login_tty(i) < 0) {
446				syslog(LOG_ERR, "login_tty %s: %m", tty);
447				close(i);
448				return 0;
449			}
450		}
451		return 1;
452	}
453}
454
455static void
456setdefttymode(const char *tname)
457{
458	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
459		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
460		exit(1);
461	}
462	tmode.c_iflag = TTYDEF_IFLAG;
463        tmode.c_oflag = TTYDEF_OFLAG;
464        tmode.c_lflag = TTYDEF_LFLAG;
465        tmode.c_cflag = TTYDEF_CFLAG;
466        omode = tmode;
467	setttymode(tname, 1);
468}
469
470static void
471setttymode(const char *tname, int raw)
472{
473	int off = 0;
474
475	gettable(tname, tabent);
476	if (OPset || EPset || APset)
477		APset++, OPset++, EPset++;
478	setdefaults();
479	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
480	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
481	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
482
483	if (IS)
484		cfsetispeed(&tmode, speed(IS));
485	else if (SP)
486		cfsetispeed(&tmode, speed(SP));
487	if (OS)
488		cfsetospeed(&tmode, speed(OS));
489	else if (SP)
490		cfsetospeed(&tmode, speed(SP));
491	set_flags(0);
492	setchars();
493	if (raw)
494		cfmakeraw(&tmode);
495	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
496		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
497		exit(1);
498	}
499}
500
501
502static int
503getname(void)
504{
505	int c;
506	char *np;
507	unsigned char cs;
508	int ppp_state = 0;
509	int ppp_connection = 0;
510
511	/*
512	 * Interrupt may happen if we use CBREAK mode
513	 */
514	if (setjmp(intrupt)) {
515		signal(SIGINT, SIG_IGN);
516		return (0);
517	}
518	signal(SIGINT, interrupt);
519	set_flags(1);
520	prompt();
521	oflush();
522	if (PF > 0) {
523		sleep(PF);
524		PF = 0;
525	}
526	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
527		syslog(LOG_ERR, "%s: %m", ttyn);
528		exit(1);
529	}
530	crmod = digit = lower = upper = 0;
531	np = name;
532	for (;;) {
533		oflush();
534		if (read(STDIN_FILENO, &cs, 1) <= 0)
535			exit(0);
536		if ((c = cs&0177) == 0)
537			return (0);
538
539		/* PPP detection state machine..
540		   Look for sequences:
541		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
542		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
543		   See RFC1662.
544		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
545		   and Erik 'PPP' Olson, <eriko@wrq.com>
546		 */
547
548		if (PP && (cs == PPP_FRAME)) {
549			ppp_state = 1;
550		} else if (ppp_state == 1 && cs == PPP_STATION) {
551			ppp_state = 2;
552		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
553			ppp_state = 3;
554		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
555			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
556			ppp_state = 4;
557		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
558			ppp_state = 5;
559		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
560			ppp_connection = 1;
561			break;
562		} else {
563			ppp_state = 0;
564		}
565
566		if (c == EOT || c == CTRL('d'))
567			exit(1);
568		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
569			putf("\r\n");
570			break;
571		}
572		if (islower(c))
573			lower = 1;
574		else if (isupper(c))
575			upper = 1;
576		else if (c == ERASE || c == '\b' || c == 0177) {
577			if (np > name) {
578				np--;
579				if (cfgetospeed(&tmode) >= 1200)
580					puts("\b \b");
581				else
582					putchr(cs);
583			}
584			continue;
585		} else if (c == KILL || c == CTRL('u')) {
586			putchr('\r');
587			if (cfgetospeed(&tmode) < 1200)
588				putchr('\n');
589			/* this is the way they do it down under ... */
590			else if (np > name)
591				puts("                                     \r");
592			prompt();
593			np = name;
594			continue;
595		} else if (isdigit(c))
596			digit++;
597		if (IG && (c <= ' ' || c > 0176))
598			continue;
599		*np++ = c;
600		putchr(cs);
601	}
602	signal(SIGINT, SIG_IGN);
603	*np = 0;
604	if (c == '\r')
605		crmod = 1;
606	if ((upper && !lower && !LC) || UC)
607		for (np = name; *np; np++)
608			if (isupper(*np))
609				*np = tolower(*np);
610	return (1 + ppp_connection);
611}
612
613static void
614putpad(const char *s)
615{
616	int pad = 0;
617	speed_t ospeed = cfgetospeed(&tmode);
618
619	if (isdigit(*s)) {
620		while (isdigit(*s)) {
621			pad *= 10;
622			pad += *s++ - '0';
623		}
624		pad *= 10;
625		if (*s == '.' && isdigit(s[1])) {
626			pad += s[1] - '0';
627			s += 2;
628		}
629	}
630
631	puts(s);
632	/*
633	 * If no delay needed, or output speed is
634	 * not comprehensible, then don't try to delay.
635	 */
636	if (pad == 0 || ospeed <= 0)
637		return;
638
639	/*
640	 * Round up by a half a character frame, and then do the delay.
641	 * Too bad there are no user program accessible programmed delays.
642	 * Transmitting pad characters slows many terminals down and also
643	 * loads the system.
644	 */
645	pad = (pad * ospeed + 50000) / 100000;
646	while (pad--)
647		putchr(*PC);
648}
649
650static void
651puts(const char *s)
652{
653	while (*s)
654		putchr(*s++);
655}
656
657char	outbuf[OBUFSIZ];
658int	obufcnt = 0;
659
660static void
661putchr(int cc)
662{
663	char c;
664
665	c = cc;
666	if (!NP) {
667		c |= partab[c&0177] & 0200;
668		if (OP)
669			c ^= 0200;
670	}
671	if (!UB) {
672		outbuf[obufcnt++] = c;
673		if (obufcnt >= OBUFSIZ)
674			oflush();
675	} else
676		write(STDOUT_FILENO, &c, 1);
677}
678
679static void
680oflush(void)
681{
682	if (obufcnt)
683		write(STDOUT_FILENO, outbuf, obufcnt);
684	obufcnt = 0;
685}
686
687static void
688prompt(void)
689{
690
691	putf(LM);
692	if (CO)
693		putchr('\n');
694}
695
696
697static char *
698getline(int fd)
699{
700	int i = 0;
701	static char linebuf[512];
702
703	/*
704	 * This is certainly slow, but it avoids having to include
705	 * stdio.h unnecessarily. Issue files should be small anyway.
706	 */
707	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
708		if (linebuf[i] == '\n') {
709			/* Don't rely on newline mode, assume raw */
710			linebuf[i++] = '\r';
711			linebuf[i++] = '\n';
712			linebuf[i] = '\0';
713			return linebuf;
714		}
715		++i;
716	}
717	linebuf[i] = '\0';
718	return i ? linebuf : 0;
719}
720
721static void
722putf(const char *cp)
723{
724	extern char editedhost[];
725	time_t t;
726	char *slash, db[100];
727
728	static struct utsname kerninfo;
729
730	if (!*kerninfo.sysname)
731		uname(&kerninfo);
732
733	while (*cp) {
734		if (*cp != '%') {
735			putchr(*cp++);
736			continue;
737		}
738		switch (*++cp) {
739
740		case 't':
741			slash = strrchr(ttyn, '/');
742			if (slash == (char *) 0)
743				puts(ttyn);
744			else
745				puts(&slash[1]);
746			break;
747
748		case 'h':
749			puts(editedhost);
750			break;
751
752		case 'd': {
753			t = (time_t)0;
754			(void)time(&t);
755			if (Lo)
756				(void)setlocale(LC_TIME, Lo);
757			(void)strftime(db, sizeof(db), DF, localtime(&t));
758			puts(db);
759			break;
760
761		case 's':
762			puts(kerninfo.sysname);
763			break;
764
765		case 'm':
766			puts(kerninfo.machine);
767			break;
768
769		case 'r':
770			puts(kerninfo.release);
771			break;
772
773		case 'v':
774			puts(kerninfo.version);
775			break;
776		}
777
778		case '%':
779			putchr('%');
780			break;
781		}
782		cp++;
783	}
784}
785