1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #ifndef lint 33 static const char copyright[] = 34 "@(#) Copyright (c) 1980, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"; 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)from: main.c 8.1 (Berkeley) 6/20/93"; 41 #endif 42 #endif /* not lint */ 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 #include <sys/param.h> 47 #include <sys/ioctl.h> 48 #include <sys/time.h> 49 #include <sys/resource.h> 50 #include <sys/stat.h> 51 #include <sys/ttydefaults.h> 52 #include <sys/utsname.h> 53 54 #include <ctype.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <locale.h> 58 #include <libutil.h> 59 #include <setjmp.h> 60 #include <signal.h> 61 #include <stdio.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 "extern.h" 71 #include "pathnames.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 /* original mode; flags've been reset using values from <sys/ttydefaults.h> */ 93 struct termios omode; 94 /* current mode */ 95 struct termios tmode; 96 97 static int crmod, digit, lower, upper; 98 99 char hostname[MAXHOSTNAMELEN]; 100 static char name[MAXLOGNAME*3]; 101 static char ttyn[32]; 102 103 #define OBUFSIZ 128 104 105 static const char *tname; 106 107 static char *env[128]; 108 109 static 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 defttymode(void); 135 static void dingdong(int); 136 static void dogettytab(void); 137 static int getname(void); 138 static void interrupt(int); 139 static void oflush(void); 140 static void prompt(void); 141 static void putchr(int); 142 static void putf(const char *); 143 static void putpad(const char *); 144 static void puts(const char *); 145 static void timeoverrun(int); 146 static char *get_line(int); 147 static void setttymode(int); 148 static int opentty(const char *, int); 149 150 static jmp_buf timeout; 151 152 static void 153 dingdong(int signo __unused) 154 { 155 alarm(0); 156 longjmp(timeout, 1); 157 } 158 159 static jmp_buf intrupt; 160 161 static void 162 interrupt(int signo __unused) 163 { 164 longjmp(intrupt, 1); 165 } 166 167 /* 168 * Action to take when getty is running too long. 169 */ 170 static void 171 timeoverrun(int signo __unused) 172 { 173 174 syslog(LOG_ERR, "getty exiting due to excessive running time"); 175 exit(1); 176 } 177 178 int 179 main(int argc, char *argv[]) 180 { 181 int first_sleep = 1, first_time = 1; 182 struct rlimit limit; 183 int rval; 184 185 signal(SIGINT, SIG_IGN); 186 signal(SIGQUIT, SIG_IGN); 187 188 openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH); 189 gethostname(hostname, sizeof(hostname) - 1); 190 hostname[sizeof(hostname) - 1] = '\0'; 191 if (hostname[0] == '\0') 192 snprintf(hostname, sizeof(hostname), "Amnesiac"); 193 194 /* 195 * Limit running time to deal with broken or dead lines. 196 */ 197 (void)signal(SIGXCPU, timeoverrun); 198 limit.rlim_max = RLIM_INFINITY; 199 limit.rlim_cur = GETTY_TIMEOUT; 200 (void)setrlimit(RLIMIT_CPU, &limit); 201 202 gettable("default"); 203 gendefaults(); 204 tname = "default"; 205 if (argc > 1) 206 tname = argv[1]; 207 208 /* 209 * The following is a work around for vhangup interactions 210 * which cause great problems getting window systems started. 211 * If the tty line is "-", we do the old style getty presuming 212 * that the file descriptors are already set up for us. 213 * J. Gettys - MIT Project Athena. 214 */ 215 if (argc <= 2 || strcmp(argv[2], "-") == 0) { 216 char *n = ttyname(STDIN_FILENO); 217 if (n == NULL) { 218 syslog(LOG_ERR, "ttyname: %m"); 219 exit(1); 220 } 221 snprintf(ttyn, sizeof(ttyn), "%s", n); 222 } else { 223 snprintf(ttyn, sizeof(ttyn), "%s%s", _PATH_DEV, argv[2]); 224 if (strcmp(argv[0], "+") != 0) { 225 chown(ttyn, 0, 0); 226 chmod(ttyn, 0600); 227 revoke(ttyn); 228 229 /* 230 * Do the first scan through gettytab. 231 * Terminal mode parameters will be wrong until 232 * defttymode() called, but they're irrelevant for 233 * the initial setup of the terminal device. 234 */ 235 dogettytab(); 236 237 /* 238 * Init or answer modem sequence has been specified. 239 */ 240 if (IC || AC) { 241 if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) 242 exit(1); 243 defttymode(); 244 setttymode(1); 245 } 246 247 if (IC) { 248 if (getty_chat(IC, CT, DC) > 0) { 249 syslog(LOG_ERR, "modem init problem on %s", ttyn); 250 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 251 exit(1); 252 } 253 } 254 255 if (AC) { 256 fd_set rfds; 257 struct timeval to; 258 int i; 259 260 FD_ZERO(&rfds); 261 FD_SET(0, &rfds); 262 to.tv_sec = RT; 263 to.tv_usec = 0; 264 i = select(32, &rfds, NULL, NULL, RT ? &to : NULL); 265 if (i < 0) { 266 syslog(LOG_ERR, "select %s: %m", ttyn); 267 } else if (i == 0) { 268 syslog(LOG_NOTICE, "recycle tty %s", ttyn); 269 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 270 exit(0); /* recycle for init */ 271 } 272 i = getty_chat(AC, CT, DC); 273 if (i > 0) { 274 syslog(LOG_ERR, "modem answer problem on %s", ttyn); 275 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 276 exit(1); 277 } 278 } else { /* maybe blocking open */ 279 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 ))) 280 exit(1); 281 } 282 } 283 } 284 285 defttymode(); 286 for (;;) { 287 288 /* 289 * if a delay was specified then sleep for that 290 * number of seconds before writing the initial prompt 291 */ 292 if (first_sleep && DE) { 293 sleep(DE); 294 /* remove any noise */ 295 (void)tcflush(STDIN_FILENO, TCIOFLUSH); 296 } 297 first_sleep = 0; 298 299 setttymode(0); 300 if (AB) { 301 tname = autobaud(); 302 dogettytab(); 303 continue; 304 } 305 if (PS) { 306 tname = portselector(); 307 dogettytab(); 308 continue; 309 } 310 if (CL && *CL) 311 putpad(CL); 312 edithost(HE); 313 314 /* if this is the first time through this, and an 315 issue file has been given, then send it */ 316 if (first_time && IF) { 317 int fd; 318 319 if ((fd = open(IF, O_RDONLY)) != -1) { 320 char * cp; 321 322 while ((cp = get_line(fd)) != NULL) { 323 putf(cp); 324 } 325 close(fd); 326 } 327 } 328 first_time = 0; 329 330 if (IMP && *IMP && !(PL && PP)) 331 system(IMP); 332 if (IM && *IM && !(PL && PP)) 333 putf(IM); 334 if (setjmp(timeout)) { 335 cfsetispeed(&tmode, B0); 336 cfsetospeed(&tmode, B0); 337 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode); 338 exit(1); 339 } 340 if (TO) { 341 signal(SIGALRM, dingdong); 342 alarm(TO); 343 } 344 345 rval = 0; 346 if (AL) { 347 const char *p = AL; 348 char *q = name; 349 350 while (*p && q < &name[sizeof name - 1]) { 351 if (isupper(*p)) 352 upper = 1; 353 else if (islower(*p)) 354 lower = 1; 355 else if (isdigit(*p)) 356 digit = 1; 357 *q++ = *p++; 358 } 359 } else if (!(PL && PP)) 360 rval = getname(); 361 if (rval == 2 || (PL && PP)) { 362 oflush(); 363 alarm(0); 364 limit.rlim_max = RLIM_INFINITY; 365 limit.rlim_cur = RLIM_INFINITY; 366 (void)setrlimit(RLIMIT_CPU, &limit); 367 execle(PP, "ppplogin", ttyn, (char *) 0, env); 368 syslog(LOG_ERR, "%s: %m", PP); 369 exit(1); 370 } else if (rval || AL) { 371 int i; 372 373 oflush(); 374 alarm(0); 375 signal(SIGALRM, SIG_DFL); 376 if (name[0] == '\0') 377 continue; 378 if (name[0] == '-') { 379 puts("user names may not start with '-'."); 380 continue; 381 } 382 if (!(upper || lower || digit)) { 383 if (AL) { 384 syslog(LOG_ERR, 385 "invalid auto-login name: %s", AL); 386 exit(1); 387 } else 388 continue; 389 } 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 dogettytab(); 424 } 425 } 426 } 427 428 static int 429 opentty(const char *tty, int flags) 430 { 431 int failopenlogged = 0, i, saved_errno; 432 433 while ((i = open(tty, flags)) == -1) 434 { 435 saved_errno = errno; 436 if (!failopenlogged) { 437 syslog(LOG_ERR, "open %s: %m", tty); 438 failopenlogged = 1; 439 } 440 if (saved_errno == ENOENT) 441 return 0; 442 sleep(60); 443 } 444 if (login_tty(i) < 0) { 445 if (daemon(0,0) < 0) { 446 syslog(LOG_ERR,"daemon: %m"); 447 close(i); 448 return 0; 449 } 450 if (login_tty(i) < 0) { 451 syslog(LOG_ERR, "login_tty %s: %m", tty); 452 close(i); 453 return 0; 454 } 455 } 456 return 1; 457 } 458 459 static void 460 defttymode(void) 461 { 462 struct termios def; 463 464 /* Start with default tty settings. */ 465 if (tcgetattr(STDIN_FILENO, &tmode) < 0) { 466 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); 467 exit(1); 468 } 469 omode = tmode; /* fill c_cc for dogettytab() */ 470 dogettytab(); 471 /* 472 * Don't rely on the driver too much, and initialize crucial 473 * things according to <sys/ttydefaults.h>. Avoid clobbering 474 * the c_cc[] settings however, the console drivers might wish 475 * to leave their idea of the preferred VERASE key value 476 * there. 477 */ 478 cfmakesane(&def); 479 tmode.c_iflag = def.c_iflag; 480 tmode.c_oflag = def.c_oflag; 481 tmode.c_lflag = def.c_lflag; 482 tmode.c_cflag = def.c_cflag; 483 if (NC) 484 tmode.c_cflag |= CLOCAL; 485 omode = tmode; 486 } 487 488 static void 489 setttymode(int raw) 490 { 491 int off = 0; 492 493 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */ 494 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */ 495 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */ 496 497 if (IS) 498 cfsetispeed(&tmode, speed(IS)); 499 else if (SP) 500 cfsetispeed(&tmode, speed(SP)); 501 if (OS) 502 cfsetospeed(&tmode, speed(OS)); 503 else if (SP) 504 cfsetospeed(&tmode, speed(SP)); 505 set_flags(0); 506 setchars(); 507 if (raw) 508 cfmakeraw(&tmode); 509 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 510 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn); 511 exit(1); 512 } 513 } 514 515 516 static int 517 getname(void) 518 { 519 int c; 520 char *np; 521 unsigned char cs; 522 int ppp_state = 0; 523 int ppp_connection = 0; 524 525 /* 526 * Interrupt may happen if we use CBREAK mode 527 */ 528 if (setjmp(intrupt)) { 529 signal(SIGINT, SIG_IGN); 530 return (0); 531 } 532 signal(SIGINT, interrupt); 533 set_flags(1); 534 prompt(); 535 oflush(); 536 if (PF > 0) { 537 sleep(PF); 538 PF = 0; 539 } 540 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) { 541 syslog(LOG_ERR, "%s: %m", ttyn); 542 exit(1); 543 } 544 crmod = digit = lower = upper = 0; 545 np = name; 546 for (;;) { 547 oflush(); 548 if (read(STDIN_FILENO, &cs, 1) <= 0) 549 exit(0); 550 if ((c = cs&0177) == 0) 551 return (0); 552 553 /* PPP detection state machine.. 554 Look for sequences: 555 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or 556 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) 557 See RFC1662. 558 Derived from code from Michael Hancock, <michaelh@cet.co.jp> 559 and Erik 'PPP' Olson, <eriko@wrq.com> 560 */ 561 562 if (PP && (cs == PPP_FRAME)) { 563 ppp_state = 1; 564 } else if (ppp_state == 1 && cs == PPP_STATION) { 565 ppp_state = 2; 566 } else if (ppp_state == 2 && cs == PPP_ESCAPE) { 567 ppp_state = 3; 568 } else if ((ppp_state == 2 && cs == PPP_CONTROL) 569 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { 570 ppp_state = 4; 571 } else if (ppp_state == 4 && cs == PPP_LCP_HI) { 572 ppp_state = 5; 573 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { 574 ppp_connection = 1; 575 break; 576 } else { 577 ppp_state = 0; 578 } 579 580 if (c == EOT || c == CTRL('d')) 581 exit(0); 582 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) { 583 putf("\r\n"); 584 break; 585 } 586 if (islower(c)) 587 lower = 1; 588 else if (isupper(c)) 589 upper = 1; 590 else if (c == ERASE || c == '\b' || c == 0177) { 591 if (np > name) { 592 np--; 593 if (cfgetospeed(&tmode) >= 1200) 594 puts("\b \b"); 595 else 596 putchr(cs); 597 } 598 continue; 599 } else if (c == KILL || c == CTRL('u')) { 600 putchr('\r'); 601 if (cfgetospeed(&tmode) < 1200) 602 putchr('\n'); 603 /* this is the way they do it down under ... */ 604 else if (np > name) 605 puts(" \r"); 606 prompt(); 607 digit = lower = upper = 0; 608 np = name; 609 continue; 610 } else if (isdigit(c)) 611 digit = 1; 612 if (IG && (c <= ' ' || c > 0176)) 613 continue; 614 *np++ = c; 615 putchr(cs); 616 } 617 signal(SIGINT, SIG_IGN); 618 *np = 0; 619 if (c == '\r') 620 crmod = 1; 621 if ((upper && !lower && !LC) || UC) 622 for (np = name; *np; np++) 623 if (isupper(*np)) 624 *np = tolower(*np); 625 return (1 + ppp_connection); 626 } 627 628 static void 629 putpad(const char *s) 630 { 631 int pad = 0; 632 speed_t ospeed = cfgetospeed(&tmode); 633 634 if (isdigit(*s)) { 635 while (isdigit(*s)) { 636 pad *= 10; 637 pad += *s++ - '0'; 638 } 639 pad *= 10; 640 if (*s == '.' && isdigit(s[1])) { 641 pad += s[1] - '0'; 642 s += 2; 643 } 644 } 645 646 puts(s); 647 /* 648 * If no delay needed, or output speed is 649 * not comprehensible, then don't try to delay. 650 */ 651 if (pad == 0 || ospeed <= 0) 652 return; 653 654 /* 655 * Round up by a half a character frame, and then do the delay. 656 * Too bad there are no user program accessible programmed delays. 657 * Transmitting pad characters slows many terminals down and also 658 * loads the system. 659 */ 660 pad = (pad * ospeed + 50000) / 100000; 661 while (pad--) 662 putchr(*PC); 663 } 664 665 static void 666 puts(const char *s) 667 { 668 while (*s) 669 putchr(*s++); 670 } 671 672 static char outbuf[OBUFSIZ]; 673 static int obufcnt = 0; 674 675 static void 676 putchr(int cc) 677 { 678 char c; 679 680 c = cc; 681 if (!NP) { 682 c |= partab[c&0177] & 0200; 683 if (OP) 684 c ^= 0200; 685 } 686 if (!UB) { 687 outbuf[obufcnt++] = c; 688 if (obufcnt >= OBUFSIZ) 689 oflush(); 690 } else 691 write(STDOUT_FILENO, &c, 1); 692 } 693 694 static void 695 oflush(void) 696 { 697 if (obufcnt) 698 write(STDOUT_FILENO, outbuf, obufcnt); 699 obufcnt = 0; 700 } 701 702 static void 703 prompt(void) 704 { 705 706 putf(LM); 707 if (CO) 708 putchr('\n'); 709 } 710 711 712 static char * 713 get_line(int fd) 714 { 715 size_t i = 0; 716 static char linebuf[512]; 717 718 /* 719 * This is certainly slow, but it avoids having to include 720 * stdio.h unnecessarily. Issue files should be small anyway. 721 */ 722 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) { 723 if (linebuf[i] == '\n') { 724 /* Don't rely on newline mode, assume raw */ 725 linebuf[i++] = '\r'; 726 linebuf[i++] = '\n'; 727 linebuf[i] = '\0'; 728 return linebuf; 729 } 730 ++i; 731 } 732 linebuf[i] = '\0'; 733 return i ? linebuf : 0; 734 } 735 736 static void 737 putf(const char *cp) 738 { 739 time_t t; 740 char db[100]; 741 const char *slash; 742 743 static struct utsname kerninfo; 744 745 if (!*kerninfo.sysname) 746 uname(&kerninfo); 747 748 while (*cp) { 749 if (*cp != '%') { 750 putchr(*cp++); 751 continue; 752 } 753 switch (*++cp) { 754 755 case 't': 756 slash = strrchr(ttyn, '/'); 757 if (slash == (char *) 0) 758 puts(ttyn); 759 else 760 puts(&slash[1]); 761 break; 762 763 case 'h': 764 puts(editedhost); 765 break; 766 767 case 'd': 768 t = (time_t)0; 769 (void)time(&t); 770 if (Lo) 771 (void)setlocale(LC_TIME, Lo); 772 (void)strftime(db, sizeof(db), DF, localtime(&t)); 773 puts(db); 774 break; 775 776 case 's': 777 puts(kerninfo.sysname); 778 break; 779 780 case 'm': 781 puts(kerninfo.machine); 782 break; 783 784 case 'r': 785 puts(kerninfo.release); 786 break; 787 788 case 'v': 789 puts(kerninfo.version); 790 break; 791 792 case '%': 793 putchr('%'); 794 break; 795 } 796 cp++; 797 } 798 } 799 800 /* 801 * Read a gettytab database entry and perform necessary quirks. 802 */ 803 static void 804 dogettytab(void) 805 { 806 807 /* Read the database entry. */ 808 gettable(tname); 809 810 /* 811 * Avoid inheriting the parity values from the default entry 812 * if any of them is set in the current entry. 813 * Mixing different parity settings is unreasonable. 814 */ 815 if (OPset || EPset || APset || NPset) 816 OPset = EPset = APset = NPset = 1; 817 818 /* Fill in default values for unset capabilities. */ 819 setdefaults(); 820 } 821