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