main.c revision 66908
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 66907 2000-10-10 01:50:26Z wollman $"; 46#endif /* not lint */ 47 48#include <sys/param.h> 49#include <sys/stat.h> 50#include <sys/ioctl.h> 51#include <sys/resource.h> 52#include <sys/ttydefaults.h> 53#include <sys/utsname.h> 54 55#include <ctype.h> 56#include <errno.h> 57#include <fcntl.h> 58#include <locale.h> 59#include <libutil.h> 60#include <signal.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 "gettytab.h" 71#include "pathnames.h" 72#include "extern.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 __P((int)); 136static int getname __P((void)); 137static void interrupt __P((int)); 138static void oflush __P((void)); 139static void prompt __P((void)); 140static void putchr __P((int)); 141static void putf __P((const char *)); 142static void putpad __P((const char *)); 143static void puts __P((const char *)); 144static void timeoverrun __P((int)); 145static char *getline __P((int)); 146static void setttymode __P((const char *, int)); 147static void setdefttymode __P((const char *)); 148static int opentty __P((const char *, int)); 149 150int main __P((int, char **)); 151 152jmp_buf timeout; 153 154static void 155dingdong(signo) 156 int signo; 157{ 158 alarm(0); 159 longjmp(timeout, 1); 160} 161 162jmp_buf intrupt; 163 164static void 165interrupt(signo) 166 int signo; 167{ 168 longjmp(intrupt, 1); 169} 170 171/* 172 * Action to take when getty is running too long. 173 */ 174static void 175timeoverrun(signo) 176 int signo; 177{ 178 179 syslog(LOG_ERR, "getty exiting due to excessive running time"); 180 exit(1); 181} 182 183int 184main(argc, argv) 185 int argc; 186 char **argv; 187{ 188 extern char **environ; 189 const char *tname; 190 int first_sleep = 1, first_time = 1; 191 struct rlimit limit; 192 int rval; 193 194 signal(SIGINT, SIG_IGN); 195 signal(SIGQUIT, SIG_IGN); 196 197 openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH); 198 gethostname(hostname, sizeof(hostname) - 1); 199 hostname[sizeof(hostname) - 1] = '\0'; 200 if (hostname[0] == '\0') 201 strcpy(hostname, "Amnesiac"); 202 203 /* 204 * Limit running time to deal with broken or dead lines. 205 */ 206 (void)signal(SIGXCPU, timeoverrun); 207 limit.rlim_max = RLIM_INFINITY; 208 limit.rlim_cur = GETTY_TIMEOUT; 209 (void)setrlimit(RLIMIT_CPU, &limit); 210 211 gettable("default", defent); 212 gendefaults(); 213 tname = "default"; 214 if (argc > 1) 215 tname = argv[1]; 216 217 /* 218 * The following is a work around for vhangup interactions 219 * which cause great problems getting window systems started. 220 * If the tty line is "-", we do the old style getty presuming 221 * that the file descriptors are already set up for us. 222 * J. Gettys - MIT Project Athena. 223 */ 224 if (argc <= 2 || strcmp(argv[2], "-") == 0) 225 strcpy(ttyn, ttyname(STDIN_FILENO)); 226 else { 227 strcpy(ttyn, dev); 228 strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev)); 229 if (strcmp(argv[0], "+") != 0) { 230 chown(ttyn, 0, 0); 231 chmod(ttyn, 0600); 232 revoke(ttyn); 233 234 gettable(tname, tabent); 235 236 /* Init modem sequence has been specified 237 */ 238 if (IC) { 239 if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) 240 exit(1); 241 setdefttymode(tname); 242 if (getty_chat(IC, CT, DC) > 0) { 243 syslog(LOG_ERR, "modem init problem on %s", ttyn); 244 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 245 exit(1); 246 } 247 } 248 249 if (AC) { 250 int i, rfds; 251 struct timeval timeout; 252 253 if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) 254 exit(1); 255 setdefttymode(tname); 256 rfds = 1 << 0; /* FD_SET */ 257 timeout.tv_sec = RT; 258 timeout.tv_usec = 0; 259 i = select(32, (fd_set*)&rfds, (fd_set*)NULL, 260 (fd_set*)NULL, RT ? &timeout : NULL); 261 if (i < 0) { 262 syslog(LOG_ERR, "select %s: %m", ttyn); 263 } else if (i == 0) { 264 syslog(LOG_NOTICE, "recycle tty %s", ttyn); 265 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 266 exit(0); /* recycle for init */ 267 } 268 i = getty_chat(AC, CT, DC); 269 if (i > 0) { 270 syslog(LOG_ERR, "modem answer problem on %s", ttyn); 271 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 272 exit(1); 273 } 274 } else { /* maybe blocking open */ 275 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 ))) 276 exit(1); 277 } 278 } 279 } 280 281 /* Start with default tty settings */ 282 if (tcgetattr(STDIN_FILENO, &tmode) < 0) { 283 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); 284 exit(1); 285 } 286 /* 287 * Don't rely on the driver too much, and initialize crucial 288 * things according to <sys/ttydefaults.h>. Avoid clobbering 289 * the c_cc[] settings however, the console drivers might wish 290 * to leave their idea of the preferred VERASE key value 291 * there. 292 */ 293 tmode.c_iflag = TTYDEF_IFLAG; 294 tmode.c_oflag = TTYDEF_OFLAG; 295 tmode.c_lflag = TTYDEF_LFLAG; 296 tmode.c_cflag = TTYDEF_CFLAG; 297 tmode.c_cflag |= (NC ? CLOCAL : 0); 298 omode = tmode; 299 300 for (;;) { 301 302 /* 303 * if a delay was specified then sleep for that 304 * number of seconds before writing the initial prompt 305 */ 306 if (first_sleep && DE) { 307 sleep(DE); 308 /* remove any noise */ 309 (void)tcflush(STDIN_FILENO, TCIOFLUSH); 310 } 311 first_sleep = 0; 312 313 setttymode(tname, 0); 314 if (AB) { 315 tname = autobaud(); 316 continue; 317 } 318 if (PS) { 319 tname = portselector(); 320 continue; 321 } 322 if (CL && *CL) 323 putpad(CL); 324 edithost(HE); 325 326 /* if this is the first time through this, and an 327 issue file has been given, then send it */ 328 if (first_time && IF) { 329 int fd; 330 331 if ((fd = open(IF, O_RDONLY)) != -1) { 332 char * cp; 333 334 while ((cp = getline(fd)) != NULL) { 335 putf(cp); 336 } 337 close(fd); 338 } 339 } 340 first_time = 0; 341 342 if (IM && *IM) 343 putf(IM); 344 if (setjmp(timeout)) { 345 cfsetispeed(&tmode, B0); 346 cfsetospeed(&tmode, B0); 347 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 348 exit(1); 349 } 350 if (TO) { 351 signal(SIGALRM, dingdong); 352 alarm(TO); 353 } 354 if (AL) { 355 const char *p = AL; 356 char *q = name; 357 int n = sizeof name; 358 359 while (*p && q < &name[sizeof name - 1]) { 360 if (isupper(*p)) 361 upper = 1; 362 else if (islower(*p)) 363 lower = 1; 364 else if (isdigit(*p)) 365 digit++; 366 *q++ = *p++; 367 } 368 } else 369 rval = getname(); 370 if (rval == 2) { 371 oflush(); 372 alarm(0); 373 limit.rlim_max = RLIM_INFINITY; 374 limit.rlim_cur = RLIM_INFINITY; 375 (void)setrlimit(RLIMIT_CPU, &limit); 376 execle(PP, "ppplogin", ttyn, (char *) 0, env); 377 syslog(LOG_ERR, "%s: %m", PP); 378 exit(1); 379 } else if (rval || AL) { 380 register int i; 381 382 oflush(); 383 alarm(0); 384 signal(SIGALRM, SIG_DFL); 385 if (name[0] == '-') { 386 puts("user names may not start with '-'."); 387 continue; 388 } 389 if (!(upper || lower || digit)) 390 continue; 391 set_flags(2); 392 if (crmod) { 393 tmode.c_iflag |= ICRNL; 394 tmode.c_oflag |= ONLCR; 395 } 396#if REALLY_OLD_TTYS 397 if (upper || UC) 398 tmode.sg_flags |= LCASE; 399 if (lower || LC) 400 tmode.sg_flags &= ~LCASE; 401#endif 402 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 403 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 404 exit(1); 405 } 406 signal(SIGINT, SIG_DFL); 407 for (i = 0; environ[i] != (char *)0; i++) 408 env[i] = environ[i]; 409 makeenv(&env[i]); 410 411 limit.rlim_max = RLIM_INFINITY; 412 limit.rlim_cur = RLIM_INFINITY; 413 (void)setrlimit(RLIMIT_CPU, &limit); 414 execle(LO, "login", AL ? "-fp" : "-p", name, 415 (char *) 0, env); 416 syslog(LOG_ERR, "%s: %m", LO); 417 exit(1); 418 } 419 alarm(0); 420 signal(SIGALRM, SIG_DFL); 421 signal(SIGINT, SIG_IGN); 422 if (NX && *NX) 423 tname = NX; 424 } 425} 426 427static int 428opentty(const char *ttyn, int flags) 429{ 430 int i, j = 0; 431 int failopenlogged = 0; 432 433 while (j < 10 && (i = open(ttyn, flags)) == -1) 434 { 435 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { 436 syslog(LOG_ERR, "open %s: %m", ttyn); 437 failopenlogged = 1; 438 } 439 j++; 440 sleep(60); 441 } 442 if (i == -1) { 443 syslog(LOG_ERR, "open %s: %m", ttyn); 444 return 0; 445 } 446 else { 447 login_tty(i); 448 return 1; 449 } 450} 451 452static void 453setdefttymode(tname) 454 const char * tname; 455{ 456 if (tcgetattr(STDIN_FILENO, &tmode) < 0) { 457 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); 458 exit(1); 459 } 460 tmode.c_iflag = TTYDEF_IFLAG; 461 tmode.c_oflag = TTYDEF_OFLAG; 462 tmode.c_lflag = TTYDEF_LFLAG; 463 tmode.c_cflag = TTYDEF_CFLAG; 464 omode = tmode; 465 setttymode(tname, 1); 466} 467 468static void 469setttymode(tname, raw) 470 const char * tname; 471 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() 504{ 505 register int c; 506 register 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(s) 615 register const char *s; 616{ 617 register pad = 0; 618 speed_t ospeed = cfgetospeed(&tmode); 619 620 if (isdigit(*s)) { 621 while (isdigit(*s)) { 622 pad *= 10; 623 pad += *s++ - '0'; 624 } 625 pad *= 10; 626 if (*s == '.' && isdigit(s[1])) { 627 pad += s[1] - '0'; 628 s += 2; 629 } 630 } 631 632 puts(s); 633 /* 634 * If no delay needed, or output speed is 635 * not comprehensible, then don't try to delay. 636 */ 637 if (pad == 0 || ospeed <= 0) 638 return; 639 640 /* 641 * Round up by a half a character frame, and then do the delay. 642 * Too bad there are no user program accessible programmed delays. 643 * Transmitting pad characters slows many terminals down and also 644 * loads the system. 645 */ 646 pad = (pad * ospeed + 50000) / 100000; 647 while (pad--) 648 putchr(*PC); 649} 650 651static void 652puts(s) 653 register const char *s; 654{ 655 while (*s) 656 putchr(*s++); 657} 658 659char outbuf[OBUFSIZ]; 660int obufcnt = 0; 661 662static void 663putchr(cc) 664 int cc; 665{ 666 char c; 667 668 c = cc; 669 if (!NP) { 670 c |= partab[c&0177] & 0200; 671 if (OP) 672 c ^= 0200; 673 } 674 if (!UB) { 675 outbuf[obufcnt++] = c; 676 if (obufcnt >= OBUFSIZ) 677 oflush(); 678 } else 679 write(STDOUT_FILENO, &c, 1); 680} 681 682static void 683oflush() 684{ 685 if (obufcnt) 686 write(STDOUT_FILENO, outbuf, obufcnt); 687 obufcnt = 0; 688} 689 690static void 691prompt() 692{ 693 694 putf(LM); 695 if (CO) 696 putchr('\n'); 697} 698 699 700static char * 701getline(fd) 702 int fd; 703{ 704 int i = 0; 705 static char linebuf[512]; 706 707 /* 708 * This is certainly slow, but it avoids having to include 709 * stdio.h unnecessarily. Issue files should be small anyway. 710 */ 711 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) { 712 if (linebuf[i] == '\n') { 713 /* Don't rely on newline mode, assume raw */ 714 linebuf[i++] = '\r'; 715 linebuf[i++] = '\n'; 716 linebuf[i] = '\0'; 717 return linebuf; 718 } 719 ++i; 720 } 721 linebuf[i] = '\0'; 722 return i ? linebuf : 0; 723} 724 725static void 726putf(cp) 727 register const char *cp; 728{ 729 extern char editedhost[]; 730 time_t t; 731 char *slash, db[100]; 732 733 static struct utsname kerninfo; 734 735 if (!*kerninfo.sysname) 736 uname(&kerninfo); 737 738 while (*cp) { 739 if (*cp != '%') { 740 putchr(*cp++); 741 continue; 742 } 743 switch (*++cp) { 744 745 case 't': 746 slash = strrchr(ttyn, '/'); 747 if (slash == (char *) 0) 748 puts(ttyn); 749 else 750 puts(&slash[1]); 751 break; 752 753 case 'h': 754 puts(editedhost); 755 break; 756 757 case 'd': { 758 t = (time_t)0; 759 (void)time(&t); 760 if (Lo) 761 (void)setlocale(LC_TIME, Lo); 762 (void)strftime(db, sizeof(db), "%+", localtime(&t)); 763 puts(db); 764 break; 765 766 case 's': 767 puts(kerninfo.sysname); 768 break; 769 770 case 'm': 771 puts(kerninfo.machine); 772 break; 773 774 case 'r': 775 puts(kerninfo.release); 776 break; 777 778 case 'v': 779 puts(kerninfo.version); 780 break; 781 } 782 783 case '%': 784 putchr('%'); 785 break; 786 } 787 cp++; 788 } 789} 790