1 /* $NetBSD: main.c,v 1.38 2001/02/19 22:46:14 cgd Exp $ */ 2 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 38 #ifndef lint 39 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"); 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "from: @(#)main.c 8.1 (Berkeley) 6/20/93"; 46 #else 47 __RCSID("$NetBSD: main.c,v 1.38 2001/02/19 22:46:14 cgd Exp $"); 48 #endif 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/stat.h> 53 #include <termios.h> 54 #include <sys/ioctl.h> 55 #include <sys/resource.h> 56 #include <sys/utsname.h> 57 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <time.h> 61 #include <ctype.h> 62 #include <fcntl.h> 63 #include <pwd.h> 64 #include <setjmp.h> 65 #include <signal.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <syslog.h> 69 #include <time.h> 70 #include <unistd.h> 71 #include <util.h> 72 #include <limits.h> 73 #include <ttyent.h> 74 #include <termcap.h> 75 76 #include "gettytab.h" 77 #include "pathnames.h" 78 #include "extern.h" 79 80 extern char **environ; 81 extern char editedhost[]; 82 83 /* 84 * Set the amount of running time that getty should accumulate 85 * before deciding that something is wrong and exit. 86 */ 87 #define GETTY_TIMEOUT 60 /* seconds */ 88 89 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */ 90 91 #define PPP_FRAME 0x7e /* PPP Framing character */ 92 #define PPP_STATION 0xff /* "All Station" character */ 93 #define PPP_ESCAPE 0x7d /* Escape Character */ 94 #define PPP_CONTROL 0x03 /* PPP Control Field */ 95 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */ 96 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */ 97 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */ 98 99 struct termios tmode, omode; 100 101 int crmod, digit, lower, upper; 102 103 char hostname[MAXHOSTNAMELEN + 1]; 104 struct utsname kerninfo; 105 char name[LOGIN_NAME_MAX]; 106 char dev[] = _PATH_DEV; 107 char ttyn[32]; 108 char lockfile[512]; 109 uid_t ttyowner; 110 char *rawttyn; 111 112 #define OBUFSIZ 128 113 #define TABBUFSIZ 512 114 115 char defent[TABBUFSIZ]; 116 char tabent[TABBUFSIZ]; 117 118 char *env[128]; 119 120 char partab[] = { 121 0001,0201,0201,0001,0201,0001,0001,0201, 122 0202,0004,0003,0205,0005,0206,0201,0001, 123 0201,0001,0001,0201,0001,0201,0201,0001, 124 0001,0201,0201,0001,0201,0001,0001,0201, 125 0200,0000,0000,0200,0000,0200,0200,0000, 126 0000,0200,0200,0000,0200,0000,0000,0200, 127 0000,0200,0200,0000,0200,0000,0000,0200, 128 0200,0000,0000,0200,0000,0200,0200,0000, 129 0200,0000,0000,0200,0000,0200,0200,0000, 130 0000,0200,0200,0000,0200,0000,0000,0200, 131 0000,0200,0200,0000,0200,0000,0000,0200, 132 0200,0000,0000,0200,0000,0200,0200,0000, 133 0000,0200,0200,0000,0200,0000,0000,0200, 134 0200,0000,0000,0200,0000,0200,0200,0000, 135 0200,0000,0000,0200,0000,0200,0200,0000, 136 0000,0200,0200,0000,0200,0000,0000,0201 137 }; 138 139 #define ERASE tmode.c_cc[VERASE] 140 #define KILL tmode.c_cc[VKILL] 141 #define EOT tmode.c_cc[VEOF] 142 143 static void dingdong __P((int)); 144 static void interrupt __P((int)); 145 static void clearscreen __P((void)); 146 void timeoverrun __P((int)); 147 148 jmp_buf timeout; 149 150 static void 151 dingdong(signo) 152 int signo; 153 { 154 155 alarm(0); 156 signal(SIGALRM, SIG_DFL); 157 longjmp(timeout, 1); 158 } 159 160 jmp_buf intrupt; 161 162 static void 163 interrupt(signo) 164 int signo; 165 { 166 167 signal(SIGINT, interrupt); 168 longjmp(intrupt, 1); 169 } 170 171 /* 172 * Action to take when getty is running too long. 173 */ 174 void 175 timeoverrun(signo) 176 int signo; 177 { 178 179 syslog(LOG_ERR, "getty exiting due to excessive running time"); 180 exit(1); 181 } 182 183 int main __P((int, char **)); 184 static int getname __P((void)); 185 static void oflush __P((void)); 186 static void prompt __P((void)); 187 static void putchr __P((int)); 188 static void putf __P((const char *)); 189 static void putpad __P((const char *)); 190 static void xputs __P((const char *)); 191 192 int 193 main(argc, argv) 194 int argc; 195 char *argv[]; 196 { 197 const char *progname; 198 char *tname; 199 int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1; 200 struct rlimit limit; 201 struct passwd *pw; 202 int rval; 203 204 #ifdef __GNUC__ 205 (void)&tname; /* XXX gcc -Wall */ 206 #endif 207 208 signal(SIGINT, SIG_IGN); 209 /* 210 signal(SIGQUIT, SIG_DFL); 211 */ 212 openlog("getty", LOG_PID, LOG_AUTH); 213 gethostname(hostname, sizeof(hostname)); 214 hostname[sizeof(hostname) - 1] = '\0'; 215 if (hostname[0] == '\0') 216 strcpy(hostname, "Amnesiac"); 217 uname(&kerninfo); 218 219 progname = getprogname(); 220 if (progname[0] == 'u' && progname[1] == 'u') 221 uugetty = 1; 222 223 /* 224 * Find id of uucp login (if present) so we can chown tty properly. 225 */ 226 if (uugetty && (pw = getpwnam("uucp"))) 227 ttyowner = pw->pw_uid; 228 else 229 ttyowner = 0; 230 231 /* 232 * Limit running time to deal with broken or dead lines. 233 */ 234 (void)signal(SIGXCPU, timeoverrun); 235 limit.rlim_max = RLIM_INFINITY; 236 limit.rlim_cur = GETTY_TIMEOUT; 237 (void)setrlimit(RLIMIT_CPU, &limit); 238 239 /* 240 * The following is a work around for vhangup interactions 241 * which cause great problems getting window systems started. 242 * If the tty line is "-", we do the old style getty presuming 243 * that the file descriptors are already set up for us. 244 * J. Gettys - MIT Project Athena. 245 */ 246 if (argc <= 2 || strcmp(argv[2], "-") == 0) { 247 strlcpy(ttyn, ttyname(0), sizeof(ttyn)); 248 } 249 else { 250 int i; 251 252 rawttyn = argv[2]; 253 strlcpy(ttyn, dev, sizeof(ttyn)); 254 strlcat(ttyn, argv[2], sizeof(ttyn)); 255 256 if (uugetty) { 257 chown(ttyn, ttyowner, 0); 258 strcpy(lockfile, _PATH_LOCK); 259 strlcat(lockfile, argv[2], sizeof(lockfile)); 260 /* wait for lockfiles to go away before we try to open */ 261 if ( pidlock(lockfile, 0, 0, 0) != 0 ) { 262 syslog(LOG_ERR, "%s: can't create lockfile", ttyn); 263 exit(1); 264 } 265 unlink(lockfile); 266 } 267 if (strcmp(argv[0], "+") != 0) { 268 chown(ttyn, ttyowner, 0); 269 chmod(ttyn, 0600); 270 revoke(ttyn); 271 if (ttyaction(ttyn, "getty", "root")) 272 syslog(LOG_WARNING, "%s: ttyaction failed", ttyn); 273 /* 274 * Delay the open so DTR stays down long enough to be detected. 275 */ 276 sleep(2); 277 while ((i = open(ttyn, O_RDWR)) == -1) { 278 if ((repcnt % 10 == 0) && 279 (errno != ENXIO || !failopenlogged)) { 280 syslog(LOG_WARNING, "%s: %m", ttyn); 281 closelog(); 282 failopenlogged = 1; 283 } 284 repcnt++; 285 sleep(60); 286 } 287 if (uugetty && pidlock(lockfile, 0, 0, 0) != 0) { 288 syslog(LOG_ERR, "%s: can't create lockfile", ttyn); 289 exit(1); 290 } 291 (void) chown(lockfile, ttyowner, 0); 292 login_tty(i); 293 } 294 } 295 296 /* Start with default tty settings */ 297 if (tcgetattr(0, &tmode) < 0) { 298 syslog(LOG_ERR, "%s: %m", ttyn); 299 exit(1); 300 } 301 omode = tmode; 302 303 gettable("default", defent); 304 gendefaults(); 305 tname = "default"; 306 if (argc > 1) 307 tname = argv[1]; 308 for (;;) { 309 int off; 310 311 gettable(tname, tabent); 312 if (OPset || EPset || APset) 313 APset++, OPset++, EPset++; 314 setdefaults(); 315 off = 0; 316 (void)tcflush(0, TCIOFLUSH); /* clear out the crap */ 317 ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ 318 ioctl(0, FIOASYNC, &off); /* ditto for async mode */ 319 320 if (IS) 321 cfsetispeed(&tmode, IS); 322 else if (SP) 323 cfsetispeed(&tmode, SP); 324 if (OS) 325 cfsetospeed(&tmode, OS); 326 else if (SP) 327 cfsetospeed(&tmode, SP); 328 setflags(0); 329 setchars(); 330 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 331 syslog(LOG_ERR, "%s: %m", ttyn); 332 exit(1); 333 } 334 if (AB) { 335 tname = autobaud(); 336 continue; 337 } 338 if (PS) { 339 tname = portselector(); 340 continue; 341 } 342 if (CS) 343 clearscreen(); 344 if (CL && *CL) 345 putpad(CL); 346 edithost(HE); 347 348 /* 349 * If this is the first time through this, and an 350 * issue file has been given, then send it. 351 */ 352 if (first_time != 0 && IF != NULL) { 353 char buf[_POSIX2_LINE_MAX]; 354 FILE *fd; 355 356 if ((fd = fopen(IF, "r")) != NULL) { 357 while (fgets(buf, sizeof(buf) - 1, fd) != NULL) 358 putf(buf); 359 fclose(fd); 360 } 361 } 362 first_time = 0; 363 364 if (IM && *IM) 365 putf(IM); 366 oflush(); 367 if (setjmp(timeout)) { 368 tmode.c_ispeed = tmode.c_ospeed = 0; 369 (void)tcsetattr(0, TCSANOW, &tmode); 370 exit(1); 371 } 372 if (TO) { 373 signal(SIGALRM, dingdong); 374 alarm(TO); 375 } 376 if (AL) { 377 const char *p = AL; 378 char *q = name; 379 380 while (*p && q < &name[sizeof name - 1]) { 381 if (isupper(*p)) 382 upper = 1; 383 else if (islower(*p)) 384 lower = 1; 385 else if (isdigit(*p)) 386 digit++; 387 *q++ = *p++; 388 } 389 } else if ((rval = getname()) == 2) { 390 execle(PP, "ppplogin", ttyn, (char *) 0, env); 391 syslog(LOG_ERR, "%s: %m", PP); 392 exit(1); 393 } 394 395 if (rval || AL) { 396 int i; 397 398 oflush(); 399 alarm(0); 400 signal(SIGALRM, SIG_DFL); 401 if (name[0] == '-') { 402 xputs("user names may not start with '-'."); 403 continue; 404 } 405 if (!(upper || lower || digit)) 406 continue; 407 setflags(2); 408 if (crmod) { 409 tmode.c_iflag |= ICRNL; 410 tmode.c_oflag |= ONLCR; 411 } 412 #if XXX 413 if (upper || UC) 414 tmode.sg_flags |= LCASE; 415 if (lower || LC) 416 tmode.sg_flags &= ~LCASE; 417 #endif 418 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 419 syslog(LOG_ERR, "%s: %m", ttyn); 420 exit(1); 421 } 422 signal(SIGINT, SIG_DFL); 423 for (i = 0; environ[i] != (char *)0; i++) 424 env[i] = environ[i]; 425 makeenv(&env[i]); 426 427 limit.rlim_max = RLIM_INFINITY; 428 limit.rlim_cur = RLIM_INFINITY; 429 (void)setrlimit(RLIMIT_CPU, &limit); 430 execle(LO, "login", AL ? "-fp" : "-p", "--", name, 431 (char *)0, env); 432 syslog(LOG_ERR, "%s: %m", LO); 433 exit(1); 434 } 435 alarm(0); 436 signal(SIGALRM, SIG_DFL); 437 signal(SIGINT, SIG_IGN); 438 if (NX && *NX) 439 tname = NX; 440 unlink(lockfile); 441 } 442 } 443 444 static int 445 getname() 446 { 447 int c; 448 char *np; 449 unsigned char cs; 450 int ppp_state, ppp_connection; 451 452 /* 453 * Interrupt may happen if we use CBREAK mode 454 */ 455 if (setjmp(intrupt)) { 456 signal(SIGINT, SIG_IGN); 457 return (0); 458 } 459 signal(SIGINT, interrupt); 460 setflags(1); 461 prompt(); 462 if (PF > 0) { 463 oflush(); 464 sleep(PF); 465 PF = 0; 466 } 467 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 468 syslog(LOG_ERR, "%s: %m", ttyn); 469 exit(1); 470 } 471 crmod = digit = lower = upper = 0; 472 ppp_state = ppp_connection = 0; 473 np = name; 474 for (;;) { 475 oflush(); 476 if (read(STDIN_FILENO, &cs, 1) <= 0) 477 exit(0); 478 if ((c = cs&0177) == 0) 479 return (0); 480 481 /* 482 * PPP detection state machine.. 483 * Look for sequences: 484 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or 485 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) 486 * See RFC1662. 487 * Derived from code from Michael Hancock <michaelh@cet.co.jp> 488 * and Erik 'PPP' Olson <eriko@wrq.com> 489 */ 490 if (PP && cs == PPP_FRAME) { 491 ppp_state = 1; 492 } else if (ppp_state == 1 && cs == PPP_STATION) { 493 ppp_state = 2; 494 } else if (ppp_state == 2 && cs == PPP_ESCAPE) { 495 ppp_state = 3; 496 } else if ((ppp_state == 2 && cs == PPP_CONTROL) || 497 (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { 498 ppp_state = 4; 499 } else if (ppp_state == 4 && cs == PPP_LCP_HI) { 500 ppp_state = 5; 501 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { 502 ppp_connection = 1; 503 break; 504 } else { 505 ppp_state = 0; 506 } 507 508 if (c == EOT) 509 exit(1); 510 if (c == '\r' || c == '\n' || 511 np >= &name[LOGIN_NAME_MAX - 1]) { 512 *np = '\0'; 513 putf("\r\n"); 514 break; 515 } 516 if (islower(c)) 517 lower = 1; 518 else if (isupper(c)) 519 upper = 1; 520 else if (c == ERASE || c == '#' || c == '\b') { 521 if (np > name) { 522 np--; 523 if (cfgetospeed(&tmode) >= 1200) 524 xputs("\b \b"); 525 else 526 putchr(cs); 527 } 528 continue; 529 } else if (c == KILL || c == '@') { 530 putchr(cs); 531 putchr('\r'); 532 if (cfgetospeed(&tmode) < 1200) 533 putchr('\n'); 534 /* this is the way they do it down under ... */ 535 else if (np > name) 536 xputs( 537 " \r"); 538 prompt(); 539 np = name; 540 continue; 541 } else if (isdigit(c)) 542 digit++; 543 if (IG && (c <= ' ' || c > 0176)) 544 continue; 545 *np++ = c; 546 putchr(cs); 547 } 548 signal(SIGINT, SIG_IGN); 549 *np = 0; 550 if (c == '\r') 551 crmod = 1; 552 if ((upper && !lower && !LC) || UC) 553 for (np = name; *np; np++) 554 if (isupper(*np)) 555 *np = tolower(*np); 556 return (1 + ppp_connection); 557 } 558 559 static void 560 putpad(s) 561 const char *s; 562 { 563 int pad = 0; 564 speed_t ospeed = cfgetospeed(&tmode); 565 566 if (isdigit(*s)) { 567 while (isdigit(*s)) { 568 pad *= 10; 569 pad += *s++ - '0'; 570 } 571 pad *= 10; 572 if (*s == '.' && isdigit(s[1])) { 573 pad += s[1] - '0'; 574 s += 2; 575 } 576 } 577 578 xputs(s); 579 /* 580 * If no delay needed, or output speed is 581 * not comprehensible, then don't try to delay. 582 */ 583 if (pad == 0 || ospeed <= 0) 584 return; 585 586 /* 587 * Round up by a half a character frame, and then do the delay. 588 * Too bad there are no user program accessible programmed delays. 589 * Transmitting pad characters slows many terminals down and also 590 * loads the system. 591 */ 592 pad = (pad * ospeed + 50000) / 100000; 593 while (pad--) 594 putchr(*PC); 595 } 596 597 static void 598 xputs(s) 599 const char *s; 600 { 601 while (*s) 602 putchr(*s++); 603 } 604 605 char outbuf[OBUFSIZ]; 606 int obufcnt = 0; 607 608 static void 609 putchr(cc) 610 int cc; 611 { 612 char c; 613 614 c = cc; 615 if (!NP) { 616 c |= partab[c&0177] & 0200; 617 if (OP) 618 c ^= 0200; 619 } 620 if (!UB) { 621 outbuf[obufcnt++] = c; 622 if (obufcnt >= OBUFSIZ) 623 oflush(); 624 } else 625 write(STDOUT_FILENO, &c, 1); 626 } 627 628 static void 629 oflush() 630 { 631 if (obufcnt) 632 write(STDOUT_FILENO, outbuf, obufcnt); 633 obufcnt = 0; 634 } 635 636 static void 637 prompt() 638 { 639 640 putf(LM); 641 if (CO) 642 putchr('\n'); 643 } 644 645 static void 646 putf(cp) 647 const char *cp; 648 { 649 time_t t; 650 char *slash, db[100]; 651 652 while (*cp) { 653 if (*cp != '%') { 654 putchr(*cp++); 655 continue; 656 } 657 switch (*++cp) { 658 659 case 't': 660 slash = strrchr(ttyn, '/'); 661 if (slash == NULL) 662 xputs(ttyn); 663 else 664 xputs(&slash[1]); 665 break; 666 667 case 'h': 668 xputs(editedhost); 669 break; 670 671 case 'd': 672 (void)time(&t); 673 (void)strftime(db, sizeof(db), 674 /* SCCS eats %M% */ 675 "%l:%M" "%p on %A, %d %B %Y", localtime(&t)); 676 xputs(db); 677 break; 678 679 case 's': 680 xputs(kerninfo.sysname); 681 break; 682 683 case 'm': 684 xputs(kerninfo.machine); 685 break; 686 687 case 'r': 688 xputs(kerninfo.release); 689 break; 690 691 case 'v': 692 xputs(kerninfo.version); 693 break; 694 695 case '%': 696 putchr('%'); 697 break; 698 } 699 if (*cp) 700 cp++; 701 } 702 } 703 704 static void 705 clearscreen() 706 { 707 struct ttyent *typ; 708 struct tinfo *tinfo; 709 char *buffer = NULL; 710 char *area = NULL; 711 char *cs; 712 713 if (rawttyn == NULL) 714 return; 715 716 typ = getttynam(rawttyn); 717 718 if ((typ == NULL) || (typ->ty_type == NULL) || 719 (typ->ty_type[0] == 0)) 720 return; 721 722 if (t_getent(&tinfo, typ->ty_type) <= 0) 723 return; 724 725 cs = t_agetstr(tinfo, "cl", &buffer, &area); 726 if (cs == NULL) 727 return; 728 729 putpad(cs); 730 free(buffer); 731 } 732