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 { /* maybe blocking open */ 274 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 ))) 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 tmode.c_cflag |= (NC ? CLOCAL : 0); 297 omode = tmode; 298 299 for (;;) { 300 301 /* 302 * if a delay was specified then sleep for that 303 * number of seconds before writing the initial prompt 304 */ 305 if (first_sleep && DE) { 306 sleep(DE); 307 /* remove any noise */ 308 (void)tcflush(STDIN_FILENO, TCIOFLUSH); 309 } 310 first_sleep = 0; 311 312 setttymode(tname, 0); 313 if (AB) { 314 tname = autobaud(); 315 continue; 316 } 317 if (PS) { 318 tname = portselector(); 319 continue; 320 } 321 if (CL && *CL) 322 putpad(CL); 323 edithost(HE); 324 325 /* if this is the first time through this, and an 326 issue file has been given, then send it */ 327 if (first_time && IF) { 328 int fd; 329 330 if ((fd = open(IF, O_RDONLY)) != -1) { 331 char * cp; 332 333 while ((cp = getline(fd)) != NULL) { 334 putf(cp); 335 } 336 close(fd); 337 } 338 } 339 first_time = 0; 340 341 if (IM && *IM) 342 putf(IM); 343 if (setjmp(timeout)) { 344 cfsetispeed(&tmode, B0); 345 cfsetospeed(&tmode, B0); 346 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 347 exit(1); 348 } 349 if (TO) { 350 signal(SIGALRM, dingdong); 351 alarm(TO); 352 } 353 if (AL) { 354 const char *p = AL; 355 char *q = name; 356 int n = sizeof name; 357 358 while (*p && q < &name[sizeof name - 1]) { 359 if (isupper(*p)) 360 upper = 1; 361 else if (islower(*p)) 362 lower = 1; 363 else if (isdigit(*p)) 364 digit++; 365 *q++ = *p++; 366 } 367 } else 368 rval = getname(); 369 if (rval == 2) { 370 oflush(); 371 alarm(0); 372 limit.rlim_max = RLIM_INFINITY; 373 limit.rlim_cur = RLIM_INFINITY; 374 (void)setrlimit(RLIMIT_CPU, &limit); 375 execle(PP, "ppplogin", ttyn, (char *) 0, env); 376 syslog(LOG_ERR, "%s: %m", PP); 377 exit(1); 378 } else if (rval || AL) { 379 register int i; 380 381 oflush(); 382 alarm(0); 383 signal(SIGALRM, SIG_DFL); 384 if (name[0] == '-') { 385 puts("user names may not start with '-'."); 386 continue; 387 } 388 if (!(upper || lower || digit)) 389 continue; 390 set_flags(2); 391 if (crmod) { 392 tmode.c_iflag |= ICRNL; 393 tmode.c_oflag |= ONLCR; 394 } 395 #if REALLY_OLD_TTYS 396 if (upper || UC) 397 tmode.sg_flags |= LCASE; 398 if (lower || LC) 399 tmode.sg_flags &= ~LCASE; 400 #endif 401 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 402 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 403 exit(1); 404 } 405 signal(SIGINT, SIG_DFL); 406 for (i = 0; environ[i] != (char *)0; i++) 407 env[i] = environ[i]; 408 makeenv(&env[i]); 409 410 limit.rlim_max = RLIM_INFINITY; 411 limit.rlim_cur = RLIM_INFINITY; 412 (void)setrlimit(RLIMIT_CPU, &limit); 413 execle(LO, "login", AL ? "-fp" : "-p", name, 414 (char *) 0, env); 415 syslog(LOG_ERR, "%s: %m", LO); 416 exit(1); 417 } 418 alarm(0); 419 signal(SIGALRM, SIG_DFL); 420 signal(SIGINT, SIG_IGN); 421 if (NX && *NX) 422 tname = NX; 423 } 424 } 425 426 static int 427 opentty(const char *ttyn, int flags) 428 { 429 int i, j = 0; 430 int failopenlogged = 0; 431 432 while (j < 10 && (i = open(ttyn, flags)) == -1) 433 { 434 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { 435 syslog(LOG_ERR, "open %s: %m", ttyn); 436 failopenlogged = 1; 437 } 438 j++; 439 sleep(60); 440 } 441 if (i == -1) { 442 syslog(LOG_ERR, "open %s: %m", ttyn); 443 return 0; 444 } 445 else { 446 login_tty(i); 447 return 1; 448 } 449 } 450 451 static void 452 setdefttymode(tname) 453 const char * tname; 454 { 455 if (tcgetattr(STDIN_FILENO, &tmode) < 0) { 456 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); 457 exit(1); 458 } 459 tmode.c_iflag = TTYDEF_IFLAG; 460 tmode.c_oflag = TTYDEF_OFLAG; 461 tmode.c_lflag = TTYDEF_LFLAG; 462 tmode.c_cflag = TTYDEF_CFLAG; 463 omode = tmode; 464 setttymode(tname, 1); 465 } 466 467 static void 468 setttymode(tname, raw) 469 const char * tname; 470 int raw; 471 { 472 int off = 0; 473 474 gettable(tname, tabent); 475 if (OPset || EPset || APset) 476 APset++, OPset++, EPset++; 477 setdefaults(); 478 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */ 479 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */ 480 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */ 481 482 if (IS) 483 cfsetispeed(&tmode, speed(IS)); 484 else if (SP) 485 cfsetispeed(&tmode, speed(SP)); 486 if (OS) 487 cfsetospeed(&tmode, speed(OS)); 488 else if (SP) 489 cfsetospeed(&tmode, speed(SP)); 490 set_flags(0); 491 setchars(); 492 if (raw) 493 cfmakeraw(&tmode); 494 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 495 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 496 exit(1); 497 } 498 } 499 500 501 static int 502 getname() 503 { 504 register int c; 505 register char *np; 506 unsigned char cs; 507 int ppp_state = 0; 508 int ppp_connection = 0; 509 510 /* 511 * Interrupt may happen if we use CBREAK mode 512 */ 513 if (setjmp(intrupt)) { 514 signal(SIGINT, SIG_IGN); 515 return (0); 516 } 517 signal(SIGINT, interrupt); 518 set_flags(1); 519 prompt(); 520 oflush(); 521 if (PF > 0) { 522 sleep(PF); 523 PF = 0; 524 } 525 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 526 syslog(LOG_ERR, "%s: %m", ttyn); 527 exit(1); 528 } 529 crmod = digit = lower = upper = 0; 530 np = name; 531 for (;;) { 532 oflush(); 533 if (read(STDIN_FILENO, &cs, 1) <= 0) 534 exit(0); 535 if ((c = cs&0177) == 0) 536 return (0); 537 538 /* PPP detection state machine.. 539 Look for sequences: 540 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or 541 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) 542 See RFC1662. 543 Derived from code from Michael Hancock, <michaelh@cet.co.jp> 544 and Erik 'PPP' Olson, <eriko@wrq.com> 545 */ 546 547 if (PP && (cs == PPP_FRAME)) { 548 ppp_state = 1; 549 } else if (ppp_state == 1 && cs == PPP_STATION) { 550 ppp_state = 2; 551 } else if (ppp_state == 2 && cs == PPP_ESCAPE) { 552 ppp_state = 3; 553 } else if ((ppp_state == 2 && cs == PPP_CONTROL) 554 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { 555 ppp_state = 4; 556 } else if (ppp_state == 4 && cs == PPP_LCP_HI) { 557 ppp_state = 5; 558 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { 559 ppp_connection = 1; 560 break; 561 } else { 562 ppp_state = 0; 563 } 564 565 if (c == EOT || c == CTRL('d')) 566 exit(1); 567 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) { 568 putf("\r\n"); 569 break; 570 } 571 if (islower(c)) 572 lower = 1; 573 else if (isupper(c)) 574 upper = 1; 575 else if (c == ERASE || c == '\b' || c == 0177) { 576 if (np > name) { 577 np--; 578 if (cfgetospeed(&tmode) >= 1200) 579 puts("\b \b"); 580 else 581 putchr(cs); 582 } 583 continue; 584 } else if (c == KILL || c == CTRL('u')) { 585 putchr('\r'); 586 if (cfgetospeed(&tmode) < 1200) 587 putchr('\n'); 588 /* this is the way they do it down under ... */ 589 else if (np > name) 590 puts(" \r"); 591 prompt(); 592 np = name; 593 continue; 594 } else if (isdigit(c)) 595 digit++; 596 if (IG && (c <= ' ' || c > 0176)) 597 continue; 598 *np++ = c; 599 putchr(cs); 600 } 601 signal(SIGINT, SIG_IGN); 602 *np = 0; 603 if (c == '\r') 604 crmod = 1; 605 if ((upper && !lower && !LC) || UC) 606 for (np = name; *np; np++) 607 if (isupper(*np)) 608 *np = tolower(*np); 609 return (1 + ppp_connection); 610 } 611 612 static void 613 putpad(s) 614 register const char *s; 615 { 616 register 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 650 static void 651 puts(s) 652 register const char *s; 653 { 654 while (*s) 655 putchr(*s++); 656 } 657 658 char outbuf[OBUFSIZ]; 659 int obufcnt = 0; 660 661 static void 662 putchr(cc) 663 int cc; 664 { 665 char c; 666 667 c = cc; 668 if (!NP) { 669 c |= partab[c&0177] & 0200; 670 if (OP) 671 c ^= 0200; 672 } 673 if (!UB) { 674 outbuf[obufcnt++] = c; 675 if (obufcnt >= OBUFSIZ) 676 oflush(); 677 } else 678 write(STDOUT_FILENO, &c, 1); 679 } 680 681 static void 682 oflush() 683 { 684 if (obufcnt) 685 write(STDOUT_FILENO, outbuf, obufcnt); 686 obufcnt = 0; 687 } 688 689 static void 690 prompt() 691 { 692 693 putf(LM); 694 if (CO) 695 putchr('\n'); 696 } 697 698 699 static char * 700 getline(fd) 701 int fd; 702 { 703 int i = 0; 704 static char linebuf[512]; 705 706 /* 707 * This is certainly slow, but it avoids having to include 708 * stdio.h unnecessarily. Issue files should be small anyway. 709 */ 710 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) { 711 if (linebuf[i] == '\n') { 712 /* Don't rely on newline mode, assume raw */ 713 linebuf[i++] = '\r'; 714 linebuf[i++] = '\n'; 715 linebuf[i] = '\0'; 716 return linebuf; 717 } 718 ++i; 719 } 720 linebuf[i] = '\0'; 721 return i ? linebuf : 0; 722 } 723 724 static void 725 putf(cp) 726 register const char *cp; 727 { 728 extern char editedhost[]; 729 time_t t; 730 char *slash, db[100]; 731 732 static struct utsname kerninfo; 733 734 if (!*kerninfo.sysname) 735 uname(&kerninfo); 736 737 while (*cp) { 738 if (*cp != '%') { 739 putchr(*cp++); 740 continue; 741 } 742 switch (*++cp) { 743 744 case 't': 745 slash = strrchr(ttyn, '/'); 746 if (slash == (char *) 0) 747 puts(ttyn); 748 else 749 puts(&slash[1]); 750 break; 751 752 case 'h': 753 puts(editedhost); 754 break; 755 756 case 'd': { 757 t = (time_t)0; 758 (void)time(&t); 759 if (Lo) 760 (void)setlocale(LC_TIME, Lo); 761 (void)strftime(db, sizeof(db), "%+", localtime(&t)); 762 puts(db); 763 break; 764 765 case 's': 766 puts(kerninfo.sysname); 767 break; 768 769 case 'm': 770 puts(kerninfo.machine); 771 break; 772 773 case 'r': 774 puts(kerninfo.release); 775 break; 776 777 case 'v': 778 puts(kerninfo.version); 779 break; 780 } 781 782 case '%': 783 putchr('%'); 784 break; 785 } 786 cp++; 787 } 788 } 789