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