main.c revision 115900
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 115900 2003-06-06 14:36:41Z yar $"; 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 && !(PL && PP)) 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 348 rval = 0; 349 if (AL) { 350 const char *p = AL; 351 char *q = name; 352 353 while (*p && q < &name[sizeof name - 1]) { 354 if (isupper(*p)) 355 upper = 1; 356 else if (islower(*p)) 357 lower = 1; 358 else if (isdigit(*p)) 359 digit++; 360 *q++ = *p++; 361 } 362 } else if (!(PL && PP)) 363 rval = getname(); 364 if (rval == 2 || (PL && PP)) { 365 oflush(); 366 alarm(0); 367 limit.rlim_max = RLIM_INFINITY; 368 limit.rlim_cur = RLIM_INFINITY; 369 (void)setrlimit(RLIMIT_CPU, &limit); 370 execle(PP, "ppplogin", ttyn, (char *) 0, env); 371 syslog(LOG_ERR, "%s: %m", PP); 372 exit(1); 373 } else if (rval || AL) { 374 int i; 375 376 oflush(); 377 alarm(0); 378 signal(SIGALRM, SIG_DFL); 379 if (name[0] == '\0') 380 continue; 381 if (name[0] == '-') { 382 puts("user names may not start with '-'."); 383 continue; 384 } 385 if (!(upper || lower || digit)) { 386 if (AL) { 387 syslog(LOG_ERR, 388 "invalid auto-login name: %s", AL); 389 exit(1); 390 } else 391 continue; 392 } 393 set_flags(2); 394 if (crmod) { 395 tmode.c_iflag |= ICRNL; 396 tmode.c_oflag |= ONLCR; 397 } 398#if REALLY_OLD_TTYS 399 if (upper || UC) 400 tmode.sg_flags |= LCASE; 401 if (lower || LC) 402 tmode.sg_flags &= ~LCASE; 403#endif 404 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 405 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 406 exit(1); 407 } 408 signal(SIGINT, SIG_DFL); 409 for (i = 0; environ[i] != (char *)0; i++) 410 env[i] = environ[i]; 411 makeenv(&env[i]); 412 413 limit.rlim_max = RLIM_INFINITY; 414 limit.rlim_cur = RLIM_INFINITY; 415 (void)setrlimit(RLIMIT_CPU, &limit); 416 execle(LO, "login", AL ? "-fp" : "-p", name, 417 (char *) 0, env); 418 syslog(LOG_ERR, "%s: %m", LO); 419 exit(1); 420 } 421 alarm(0); 422 signal(SIGALRM, SIG_DFL); 423 signal(SIGINT, SIG_IGN); 424 if (NX && *NX) 425 tname = NX; 426 } 427} 428 429static int 430opentty(const char *tty, int flags) 431{ 432 int i, j = 0; 433 int failopenlogged = 0; 434 435 while (j < 10 && (i = open(tty, flags)) == -1) 436 { 437 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { 438 syslog(LOG_ERR, "open %s: %m", tty); 439 failopenlogged = 1; 440 } 441 j++; 442 sleep(60); 443 } 444 if (i == -1) { 445 syslog(LOG_ERR, "open %s: %m", tty); 446 return 0; 447 } 448 else { 449 if (login_tty(i) < 0) { 450 if (daemon(0,0) < 0) { 451 syslog(LOG_ERR,"daemon: %m"); 452 close(i); 453 return 0; 454 } 455 if (login_tty(i) < 0) { 456 syslog(LOG_ERR, "login_tty %s: %m", tty); 457 close(i); 458 return 0; 459 } 460 } 461 return 1; 462 } 463} 464 465static void 466setdefttymode(const char *tname) 467{ 468 if (tcgetattr(STDIN_FILENO, &tmode) < 0) { 469 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); 470 exit(1); 471 } 472 tmode.c_iflag = TTYDEF_IFLAG; 473 tmode.c_oflag = TTYDEF_OFLAG; 474 tmode.c_lflag = TTYDEF_LFLAG; 475 tmode.c_cflag = TTYDEF_CFLAG; 476 omode = tmode; 477 setttymode(tname, 1); 478} 479 480static void 481setttymode(const char *tname, int raw) 482{ 483 int off = 0; 484 485 gettable(tname, tabent); 486 if (OPset || EPset || APset) 487 APset++, OPset++, EPset++; 488 setdefaults(); 489 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */ 490 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */ 491 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */ 492 493 if (IS) 494 cfsetispeed(&tmode, speed(IS)); 495 else if (SP) 496 cfsetispeed(&tmode, speed(SP)); 497 if (OS) 498 cfsetospeed(&tmode, speed(OS)); 499 else if (SP) 500 cfsetospeed(&tmode, speed(SP)); 501 set_flags(0); 502 setchars(); 503 if (raw) 504 cfmakeraw(&tmode); 505 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 506 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 507 exit(1); 508 } 509} 510 511 512static int 513getname(void) 514{ 515 int c; 516 char *np; 517 unsigned char cs; 518 int ppp_state = 0; 519 int ppp_connection = 0; 520 521 /* 522 * Interrupt may happen if we use CBREAK mode 523 */ 524 if (setjmp(intrupt)) { 525 signal(SIGINT, SIG_IGN); 526 return (0); 527 } 528 signal(SIGINT, interrupt); 529 set_flags(1); 530 prompt(); 531 oflush(); 532 if (PF > 0) { 533 sleep(PF); 534 PF = 0; 535 } 536 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 537 syslog(LOG_ERR, "%s: %m", ttyn); 538 exit(1); 539 } 540 crmod = digit = lower = upper = 0; 541 np = name; 542 for (;;) { 543 oflush(); 544 if (read(STDIN_FILENO, &cs, 1) <= 0) 545 exit(0); 546 if ((c = cs&0177) == 0) 547 return (0); 548 549 /* PPP detection state machine.. 550 Look for sequences: 551 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or 552 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) 553 See RFC1662. 554 Derived from code from Michael Hancock, <michaelh@cet.co.jp> 555 and Erik 'PPP' Olson, <eriko@wrq.com> 556 */ 557 558 if (PP && (cs == PPP_FRAME)) { 559 ppp_state = 1; 560 } else if (ppp_state == 1 && cs == PPP_STATION) { 561 ppp_state = 2; 562 } else if (ppp_state == 2 && cs == PPP_ESCAPE) { 563 ppp_state = 3; 564 } else if ((ppp_state == 2 && cs == PPP_CONTROL) 565 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { 566 ppp_state = 4; 567 } else if (ppp_state == 4 && cs == PPP_LCP_HI) { 568 ppp_state = 5; 569 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { 570 ppp_connection = 1; 571 break; 572 } else { 573 ppp_state = 0; 574 } 575 576 if (c == EOT || c == CTRL('d')) 577 exit(0); 578 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) { 579 putf("\r\n"); 580 break; 581 } 582 if (islower(c)) 583 lower = 1; 584 else if (isupper(c)) 585 upper = 1; 586 else if (c == ERASE || c == '\b' || c == 0177) { 587 if (np > name) { 588 np--; 589 if (cfgetospeed(&tmode) >= 1200) 590 puts("\b \b"); 591 else 592 putchr(cs); 593 } 594 continue; 595 } else if (c == KILL || c == CTRL('u')) { 596 putchr('\r'); 597 if (cfgetospeed(&tmode) < 1200) 598 putchr('\n'); 599 /* this is the way they do it down under ... */ 600 else if (np > name) 601 puts(" \r"); 602 prompt(); 603 digit = lower = upper = 0; 604 np = name; 605 continue; 606 } else if (isdigit(c)) 607 digit++; 608 if (IG && (c <= ' ' || c > 0176)) 609 continue; 610 *np++ = c; 611 putchr(cs); 612 } 613 signal(SIGINT, SIG_IGN); 614 *np = 0; 615 if (c == '\r') 616 crmod = 1; 617 if ((upper && !lower && !LC) || UC) 618 for (np = name; *np; np++) 619 if (isupper(*np)) 620 *np = tolower(*np); 621 return (1 + ppp_connection); 622} 623 624static void 625putpad(const char *s) 626{ 627 int pad = 0; 628 speed_t ospeed = cfgetospeed(&tmode); 629 630 if (isdigit(*s)) { 631 while (isdigit(*s)) { 632 pad *= 10; 633 pad += *s++ - '0'; 634 } 635 pad *= 10; 636 if (*s == '.' && isdigit(s[1])) { 637 pad += s[1] - '0'; 638 s += 2; 639 } 640 } 641 642 puts(s); 643 /* 644 * If no delay needed, or output speed is 645 * not comprehensible, then don't try to delay. 646 */ 647 if (pad == 0 || ospeed <= 0) 648 return; 649 650 /* 651 * Round up by a half a character frame, and then do the delay. 652 * Too bad there are no user program accessible programmed delays. 653 * Transmitting pad characters slows many terminals down and also 654 * loads the system. 655 */ 656 pad = (pad * ospeed + 50000) / 100000; 657 while (pad--) 658 putchr(*PC); 659} 660 661static void 662puts(const char *s) 663{ 664 while (*s) 665 putchr(*s++); 666} 667 668char outbuf[OBUFSIZ]; 669int obufcnt = 0; 670 671static void 672putchr(int cc) 673{ 674 char c; 675 676 c = cc; 677 if (!NP) { 678 c |= partab[c&0177] & 0200; 679 if (OP) 680 c ^= 0200; 681 } 682 if (!UB) { 683 outbuf[obufcnt++] = c; 684 if (obufcnt >= OBUFSIZ) 685 oflush(); 686 } else 687 write(STDOUT_FILENO, &c, 1); 688} 689 690static void 691oflush(void) 692{ 693 if (obufcnt) 694 write(STDOUT_FILENO, outbuf, obufcnt); 695 obufcnt = 0; 696} 697 698static void 699prompt(void) 700{ 701 702 putf(LM); 703 if (CO) 704 putchr('\n'); 705} 706 707 708static char * 709getline(int fd) 710{ 711 int i = 0; 712 static char linebuf[512]; 713 714 /* 715 * This is certainly slow, but it avoids having to include 716 * stdio.h unnecessarily. Issue files should be small anyway. 717 */ 718 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) { 719 if (linebuf[i] == '\n') { 720 /* Don't rely on newline mode, assume raw */ 721 linebuf[i++] = '\r'; 722 linebuf[i++] = '\n'; 723 linebuf[i] = '\0'; 724 return linebuf; 725 } 726 ++i; 727 } 728 linebuf[i] = '\0'; 729 return i ? linebuf : 0; 730} 731 732static void 733putf(const char *cp) 734{ 735 extern char editedhost[]; 736 time_t t; 737 char *slash, db[100]; 738 739 static struct utsname kerninfo; 740 741 if (!*kerninfo.sysname) 742 uname(&kerninfo); 743 744 while (*cp) { 745 if (*cp != '%') { 746 putchr(*cp++); 747 continue; 748 } 749 switch (*++cp) { 750 751 case 't': 752 slash = strrchr(ttyn, '/'); 753 if (slash == (char *) 0) 754 puts(ttyn); 755 else 756 puts(&slash[1]); 757 break; 758 759 case 'h': 760 puts(editedhost); 761 break; 762 763 case 'd': { 764 t = (time_t)0; 765 (void)time(&t); 766 if (Lo) 767 (void)setlocale(LC_TIME, Lo); 768 (void)strftime(db, sizeof(db), DF, localtime(&t)); 769 puts(db); 770 break; 771 772 case 's': 773 puts(kerninfo.sysname); 774 break; 775 776 case 'm': 777 puts(kerninfo.machine); 778 break; 779 780 case 'r': 781 puts(kerninfo.release); 782 break; 783 784 case 'v': 785 puts(kerninfo.version); 786 break; 787 } 788 789 case '%': 790 putchr('%'); 791 break; 792 } 793 cp++; 794 } 795} 796