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