1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #include <ctype.h> 30 #include <stdio_ext.h> 31 #include <stdlib.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <poll.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/stropts.h> 40 #include <sys/resource.h> 41 #include <sys/termios.h> 42 #include <pwd.h> 43 #include <grp.h> 44 #include <unistd.h> 45 #include <ulimit.h> 46 #include <libdevinfo.h> 47 48 #include "sac.h" 49 #include "ttymon.h" 50 #include "tmstruct.h" 51 #include "tmextern.h" 52 53 static int Initialized; 54 55 static void initialize(void); 56 static void open_all(void); 57 static int set_poll(struct pollfd *); 58 static int check_spawnlimit(struct pmtab *); 59 static int mod_ttydefs(void); 60 61 void open_device(struct pmtab *); 62 void set_softcar(struct pmtab *); 63 64 /* 65 * ttymon - a port monitor under SAC 66 * - monitor ports, set terminal modes, baud rate 67 * and line discipline for the port 68 * - invoke service on port if connection request received 69 * - Usage: ttymon 70 * ttymon -g [options] 71 * Valid options are 72 * -h 73 * -d device 74 * -l ttylabel 75 * -t timeout 76 * -m modules 77 * -p prompt 78 * 79 * - ttymon without args is invoked by SAC 80 * - ttymon -g is invoked by process that needs to 81 * have login service on the fly 82 */ 83 84 int 85 main(int argc, char *argv[]) 86 { 87 int nfds; 88 89 /* 90 * Only the superuser should execute this command. 91 */ 92 if (getuid() != 0) 93 return (1); 94 95 /* remember original signal mask and dispositions */ 96 (void) sigprocmask(SIG_SETMASK, NULL, &Origmask); 97 (void) sigaction(SIGINT, NULL, &Sigint); 98 (void) sigaction(SIGALRM, NULL, &Sigalrm); 99 (void) sigaction(SIGPOLL, NULL, &Sigpoll); 100 (void) sigaction(SIGQUIT, NULL, &Sigquit); 101 (void) sigaction(SIGCLD, NULL, &Sigcld); 102 (void) sigaction(SIGTERM, NULL, &Sigterm); 103 #ifdef DEBUG 104 (void) sigaction(SIGUSR1, NULL, &Sigusr1); 105 (void) sigaction(SIGUSR2, NULL, &Sigusr2); 106 #endif 107 108 /* 109 * SIGQUIT needs to be ignored. Otherwise, hitting ^\ from 110 * console kills ttymon. 111 */ 112 (void) signal(SIGQUIT, SIG_IGN); 113 114 if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) { 115 ttymon_express(argc, argv); 116 return (1); /*NOTREACHED*/ 117 } 118 119 initialize(); 120 121 for (;;) { 122 nfds = set_poll(Pollp); 123 if (!Reread_flag) { 124 if (nfds > 0) 125 do_poll(Pollp, nfds); 126 else 127 (void) pause(); 128 } 129 /* 130 * READDB messages may arrive during poll or pause. 131 * So the flag needs to be checked again. 132 */ 133 if (Reread_flag) { 134 Reread_flag = FALSE; 135 re_read(); 136 } 137 while (Retry) { 138 Retry = FALSE; 139 open_all(); 140 } 141 } 142 } 143 144 static void 145 initialize(void) 146 { 147 struct pmtab *tp; 148 struct passwd *pwdp; 149 struct group *gp; 150 struct rlimit rlimit; 151 152 Initialized = FALSE; 153 /* 154 * get_environ() must be called first, 155 * otherwise we don't know where the log file is 156 */ 157 get_environ(); 158 openttymonlog(); 159 openpid(); 160 openpipes(); 161 setup_PCpipe(); 162 163 log("PMTAG: %s", Tag); 164 log("Starting state: %s", 165 (State == PM_ENABLED) ? "enabled" : "disabled"); 166 167 #ifdef DEBUG 168 opendebug(FALSE); 169 debug("***** ttymon in initialize *****"); 170 log("debug mode is \t on"); 171 #endif 172 173 catch_signals(); 174 175 /* register to receive SIGPOLL when data comes to pmpipe */ 176 if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0) 177 fatal("I_SETSIG on pmpipe failed: %s", strerror(errno)); 178 179 sacpoll(); /* this is needed because there may be data already */ 180 181 Maxfiles = (int)ulimit(4, 0L); /* get max number of open files */ 182 if (Maxfiles < 0) 183 fatal("ulimit(4,0L) failed: %s", strerror(errno)); 184 185 if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1) 186 fatal("getrlimit failed: %s", strerror(errno)); 187 188 rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max; 189 if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1) 190 fatal("setrlimit failed: %s", strerror(errno)); 191 192 (void) enable_extended_FILE_stdio(-1, -1); 193 194 Maxfiles = rlimit.rlim_cur; 195 Maxfds = Maxfiles - FILE_RESERVED; 196 197 log("max open files = %d", Maxfiles); 198 log("max ports ttymon can monitor = %d", Maxfds); 199 200 read_pmtab(); 201 202 /* 203 * setup poll array 204 * - we allocate 10 extra pollfd so that 205 * we do not have to re-malloc when there is 206 * minor fluctuation in Nentries 207 */ 208 Npollfd = Nentries + 10; 209 if (Npollfd > Maxfds) 210 Npollfd = Maxfds; 211 if ((Pollp = (struct pollfd *) 212 malloc((unsigned)(Npollfd * sizeof (struct pollfd)))) 213 == (struct pollfd *)NULL) 214 fatal("malloc for Pollp failed"); 215 216 (void) mod_ttydefs(); /* just to initialize Mtime */ 217 if (check_version(TTYDEFS_VERS, TTYDEFS) != 0) 218 fatal("check /etc/ttydefs version failed"); 219 220 read_ttydefs(NULL, FALSE); 221 222 /* initialize global variables, Uucp_uid & Tty_gid */ 223 if ((pwdp = getpwnam(UUCP)) != NULL) 224 Uucp_uid = pwdp->pw_uid; 225 if ((gp = getgrnam(TTY)) == NULL) 226 log("no group entry for <tty>, default is used"); 227 else 228 Tty_gid = gp->gr_gid; 229 endgrent(); 230 endpwent(); 231 #ifdef DEBUG 232 debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid); 233 #endif 234 235 log("Initialization Completed"); 236 237 /* open the devices ttymon monitors */ 238 Retry = TRUE; 239 while (Retry) { 240 Retry = FALSE; 241 for (tp = PMtab; tp; tp = tp->p_next) { 242 if ((tp->p_status > 0) && (tp->p_fd == 0) && 243 (tp->p_childpid == 0) && 244 !(tp->p_ttyflags & I_FLAG) && 245 (!((State == PM_DISABLED) && 246 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { 247 open_device(tp); 248 if (tp->p_fd > 0) 249 got_carrier(tp); 250 } 251 } 252 } 253 Initialized = TRUE; 254 } 255 256 static void free_defs(void); 257 258 /* 259 * open_all - open devices in pmtab if the entry is 260 * - valid, fd = 0, and pid = 0 261 */ 262 static void 263 open_all(void) 264 { 265 struct pmtab *tp; 266 int check_modtime; 267 sigset_t cset; 268 sigset_t tset; 269 270 #ifdef DEBUG 271 debug("in open_all"); 272 #endif 273 check_modtime = TRUE; 274 275 for (tp = PMtab; tp; tp = tp->p_next) { 276 if ((tp->p_status > 0) && (tp->p_fd == 0) && 277 (tp->p_childpid == 0) && 278 !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) && 279 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { 280 /* 281 * if we have not check modification time and 282 * /etc/ttydefs was modified, need to re-read it 283 */ 284 if (check_modtime && mod_ttydefs()) { 285 check_modtime = FALSE; 286 (void) sigprocmask(SIG_SETMASK, NULL, &cset); 287 tset = cset; 288 (void) sigaddset(&tset, SIGCLD); 289 (void) sigprocmask(SIG_SETMASK, &tset, NULL); 290 free_defs(); 291 #ifdef DEBUG 292 debug("/etc/ttydefs is modified, re-read it"); 293 #endif 294 read_ttydefs(NULL, FALSE); 295 (void) sigprocmask(SIG_SETMASK, &cset, NULL); 296 } 297 open_device(tp); 298 if (tp->p_fd > 0) 299 got_carrier(tp); 300 } else if (((tp->p_status == LOCKED) || 301 (tp->p_status == SESSION) || 302 (tp->p_status == UNACCESS)) && 303 (tp->p_fd > 0) && 304 (!((State == PM_DISABLED) && 305 ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) { 306 if (check_modtime && mod_ttydefs()) { 307 check_modtime = FALSE; 308 (void) sigprocmask(SIG_SETMASK, NULL, &cset); 309 tset = cset; 310 (void) sigaddset(&tset, SIGCLD); 311 (void) sigprocmask(SIG_SETMASK, &tset, NULL); 312 free_defs(); 313 #ifdef DEBUG 314 debug("/etc/ttydefs is modified, re-read it"); 315 #endif 316 read_ttydefs(NULL, FALSE); 317 (void) sigprocmask(SIG_SETMASK, &cset, NULL); 318 } 319 tp->p_status = VALID; 320 open_device(tp); 321 if (tp->p_fd > 0) 322 got_carrier(tp); 323 } 324 } 325 } 326 327 void 328 set_softcar(struct pmtab *pmptr) 329 { 330 331 int fd, val = 0; 332 333 #ifdef DEBUG 334 debug("in set_softcar"); 335 #endif 336 /* 337 * If soft carrier is not set one way or 338 * the other, leave it alone. 339 */ 340 if (*pmptr->p_softcar == '\0') 341 return; 342 343 if (*pmptr->p_softcar == 'y') 344 val = 1; 345 346 if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) { 347 log("open (%s) failed: %s", pmptr->p_device, strerror(errno)); 348 return; 349 } 350 351 if (ioctl(fd, TIOCSSOFTCAR, &val) < 0) 352 log("set soft-carrier (%s) failed: %s", pmptr->p_device, 353 strerror(errno)); 354 355 (void) close(fd); 356 } 357 358 359 /* 360 * open_device(pmptr) - open the device 361 * - check device lock 362 * - change owner of device 363 * - push line disciplines 364 * - set termio 365 */ 366 367 void 368 open_device(struct pmtab *pmptr) 369 { 370 int fd, tmpfd; 371 struct sigaction sigact; 372 373 #ifdef DEBUG 374 debug("in open_device"); 375 #endif 376 377 if (pmptr->p_status == GETTY) { 378 revokedevaccess(pmptr->p_device, 0, 0, 0); 379 380 if ((fd = open(pmptr->p_device, O_RDWR)) == -1) 381 fatal("open (%s) failed: %s", pmptr->p_device, 382 strerror(errno)); 383 384 } else { 385 if (check_spawnlimit(pmptr) == -1) { 386 pmptr->p_status = NOTVALID; 387 log("service <%s> is respawning too rapidly", 388 pmptr->p_tag); 389 return; 390 } 391 if (pmptr->p_fd > 0) { /* file already open */ 392 fd = pmptr->p_fd; 393 pmptr->p_fd = 0; 394 } else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) 395 == -1) { 396 log("open (%s) failed: %s", pmptr->p_device, 397 strerror(errno)); 398 if ((errno == ENODEV) || (errno == EBUSY)) { 399 pmptr->p_status = UNACCESS; 400 Nlocked++; 401 if (Nlocked == 1) { 402 sigact.sa_flags = 0; 403 sigact.sa_handler = sigalarm; 404 (void) sigemptyset(&sigact.sa_mask); 405 (void) sigaction(SIGALRM, &sigact, 406 NULL); 407 (void) alarm(ALARMTIME); 408 } 409 } else 410 Retry = TRUE; 411 return; 412 } 413 /* set close-on-exec flag */ 414 if (fcntl(fd, F_SETFD, 1) == -1) 415 fatal("F_SETFD fcntl failed: %s", strerror(errno)); 416 417 if (tm_checklock(fd) != 0) { 418 pmptr->p_status = LOCKED; 419 (void) close(fd); 420 Nlocked++; 421 if (Nlocked == 1) { 422 sigact.sa_flags = 0; 423 sigact.sa_handler = sigalarm; 424 (void) sigemptyset(&sigact.sa_mask); 425 (void) sigaction(SIGALRM, &sigact, NULL); 426 (void) alarm(ALARMTIME); 427 } 428 return; 429 } 430 if (check_session(fd) != 0) { 431 if ((Initialized) && (pmptr->p_inservice != SESSION)) { 432 log("Warning -- active session exists on <%s>", 433 pmptr->p_device); 434 } else { 435 /* 436 * this may happen if a service is running 437 * and ttymon dies and is restarted, 438 * or another process is running on the 439 * port. 440 */ 441 pmptr->p_status = SESSION; 442 pmptr->p_inservice = 0; 443 (void) close(fd); 444 Nlocked++; 445 if (Nlocked == 1) { 446 sigact.sa_flags = 0; 447 sigact.sa_handler = sigalarm; 448 (void) sigemptyset(&sigact.sa_mask); 449 (void) sigaction(SIGALRM, &sigact, 450 NULL); 451 (void) alarm(ALARMTIME); 452 } 453 return; 454 } 455 } 456 pmptr->p_inservice = 0; 457 } 458 459 if (pmptr->p_ttyflags & H_FLAG) { 460 /* drop DTR */ 461 (void) hang_up_line(fd); 462 /* 463 * After hang_up_line, the stream is in STRHUP state. 464 * We need to do another open to reinitialize streams 465 * then we can close one fd 466 */ 467 if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) { 468 log("open (%s) failed: %s", pmptr->p_device, 469 strerror(errno)); 470 Retry = TRUE; 471 (void) close(fd); 472 return; 473 } 474 (void) close(tmpfd); 475 } 476 477 #ifdef DEBUG 478 debug("open_device (%s), fd = %d", pmptr->p_device, fd); 479 #endif 480 481 /* Change ownership of the tty line to root/uucp and */ 482 /* set protections to only allow root/uucp to read the line. */ 483 484 if (pmptr->p_ttyflags & (B_FLAG|C_FLAG)) 485 (void) fchown(fd, Uucp_uid, Tty_gid); 486 else 487 (void) fchown(fd, ROOTUID, Tty_gid); 488 (void) fchmod(fd, 0620); 489 490 if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) { 491 if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device) 492 == -1) { 493 Retry = TRUE; 494 (void) close(fd); 495 return; 496 } 497 } 498 499 if (initial_termio(fd, pmptr) == -1) { 500 Retry = TRUE; 501 (void) close(fd); 502 return; 503 } 504 505 (void) di_devperm_logout((const char *)pmptr->p_device); 506 pmptr->p_fd = fd; 507 } 508 509 /* 510 * set_poll(fdp) - put all fd's in a pollfd array 511 * - set poll event to POLLIN and POLLMSG 512 * - return number of fd to be polled 513 */ 514 515 static int 516 set_poll(struct pollfd *fdp) 517 { 518 struct pmtab *tp; 519 int nfd = 0; 520 521 for (tp = PMtab; tp; tp = tp->p_next) { 522 if (tp->p_fd > 0) { 523 fdp->fd = tp->p_fd; 524 fdp->events = POLLIN; 525 fdp++; 526 nfd++; 527 } 528 } 529 return (nfd); 530 } 531 532 /* 533 * check_spawnlimit - return 0 if spawnlimit is not reached 534 * - otherwise return -1 535 */ 536 static int 537 check_spawnlimit(struct pmtab *pmptr) 538 { 539 time_t now; 540 541 (void) time(&now); 542 if (pmptr->p_time == 0L) 543 pmptr->p_time = now; 544 if (pmptr->p_respawn >= SPAWN_LIMIT) { 545 if ((now - pmptr->p_time) < SPAWN_INTERVAL) { 546 pmptr->p_time = now; 547 pmptr->p_respawn = 0; 548 return (-1); 549 } 550 pmptr->p_time = now; 551 pmptr->p_respawn = 0; 552 } 553 pmptr->p_respawn++; 554 return (0); 555 } 556 557 /* 558 * mod_ttydefs - to check if /etc/ttydefs has been modified 559 * - return TRUE if file modified 560 * - otherwise, return FALSE 561 */ 562 static int 563 mod_ttydefs(void) 564 { 565 struct stat statbuf; 566 567 if (stat(TTYDEFS, &statbuf) == -1) { 568 /* if stat failed, don't bother reread ttydefs */ 569 return (FALSE); 570 } 571 if ((long)statbuf.st_mtime != Mtime) { 572 Mtime = (long)statbuf.st_mtime; 573 return (TRUE); 574 } 575 return (FALSE); 576 } 577 578 /* 579 * free_defs - free the Gdef table 580 */ 581 static void 582 free_defs(void) 583 { 584 int i; 585 struct Gdef *tp; 586 tp = &Gdef[0]; 587 for (i = 0; i < Ndefs; i++, tp++) { 588 free(tp->g_id); 589 free(tp->g_iflags); 590 free(tp->g_fflags); 591 free(tp->g_nextid); 592 tp->g_id = NULL; 593 tp->g_iflags = NULL; 594 tp->g_fflags = NULL; 595 tp->g_nextid = NULL; 596 } 597 Ndefs = 0; 598 } 599 600 /* 601 * rebuild flags entry using speed from ttymode. 602 */ 603 static char * 604 merge_flags(char *src, char *ttymode) 605 { 606 char *data, *ptr, *flags; 607 608 /* copy speed entry */ 609 data = strsave(src); 610 flags = strsave(ttymode); 611 ptr = strchr(flags, ','); 612 if (ptr == NULL) { /* ttymode is corrupted */ 613 free(flags); 614 return (data); 615 } 616 *ptr = '\0'; 617 ptr = flags; 618 flags = strsave(flags); 619 free(ptr); 620 621 /* 622 * The flags line is supposed to have stty keywords separated by space. 623 * We need to split up the keywords, replace the speed and 624 * reconstruct the flags line. 625 */ 626 627 ptr = strtok(data, " \t"); 628 if (ptr == NULL) { 629 free(data); 630 return (flags); 631 } 632 633 do { 634 char *tmp; 635 636 /* skip speed */ 637 if (isdigit(*ptr)) 638 continue; 639 640 if (asprintf(&tmp, "%s %s", flags, ptr) <= 0) { 641 /* should we complain? */ 642 break; 643 } 644 free(flags); 645 flags = tmp; 646 } while ((ptr = strtok(NULL, " \t")) != NULL); 647 648 free(data); 649 return (flags); 650 } 651 652 /* 653 * struct Gdef *get_speed(ttylabel) 654 * - search "/etc/ttydefs" for speed and term. specification 655 * using "ttylabel". If "ttylabel" is NULL, default 656 * to DEFAULT 657 * - for /dev/console, if we are in fact using serial console, 658 * use ttyX-mode value to get speed. This allows us to use 659 * the value set for serial console either from firmware (or BMC sol), 660 * or boot loader default. 661 * arg: ttylabel - label/id of speed settings. 662 */ 663 664 struct Gdef * 665 get_speed(struct pmtab *pmptr) 666 { 667 static struct Gdef serial = { 0 }; 668 struct Gdef *sp; 669 char *ttylabel = pmptr->p_ttylabel; 670 671 if ((ttylabel != NULL) && (*ttylabel != '\0')) { 672 if ((sp = find_def(ttylabel)) == NULL) { 673 log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS); 674 sp = &DEFAULT; /* use default */ 675 } 676 } else { 677 sp = &DEFAULT; /* use default */ 678 } 679 680 /* 681 * if this is not /dev/console or /dev/console is not using serial, 682 * we are done. 683 */ 684 if (pmptr->p_ttymode == NULL || 685 strcmp(pmptr->p_device, "/dev/console") != 0) 686 return (sp); 687 688 /* is entry for serial set up? */ 689 if (serial.g_id == NULL) { 690 /* 691 * Copy data from sp, except we need to update inital and 692 * final flags. 693 */ 694 serial.g_id = strsave(sp->g_id); 695 serial.g_iflags = merge_flags(sp->g_iflags, pmptr->p_ttymode); 696 serial.g_fflags = merge_flags(sp->g_fflags, pmptr->p_ttymode); 697 serial.g_autobaud = sp->g_autobaud; 698 serial.g_nextid = strsave(sp->g_nextid); 699 } 700 return (&serial); 701 } 702 703 /* 704 * setup_PCpipe() - setup the pipe between Parent and Children 705 * - the pipe is used for a tmchild to send its 706 * pid to inform ttymon that it is about to 707 * invoke service 708 * - the pipe also serves as a mean for tmchild 709 * to detect failure of ttymon 710 */ 711 void 712 setup_PCpipe(void) 713 { 714 int flag = 0; 715 716 if (pipe(PCpipe) == -1) 717 fatal("pipe() failed: %s", strerror(errno)); 718 719 /* set close-on-exec flag */ 720 if (fcntl(PCpipe[0], F_SETFD, 1) == -1) 721 fatal("F_SETFD fcntl failed: %s", strerror(errno)); 722 723 if (fcntl(PCpipe[1], F_SETFD, 1) == -1) 724 fatal("F_SETFD fcntl failed: %s", strerror(errno)); 725 726 /* set O_NONBLOCK flag */ 727 if (fcntl(PCpipe[0], F_GETFL, flag) == -1) 728 fatal("F_GETFL failed: %s", strerror(errno)); 729 730 flag |= O_NONBLOCK; 731 if (fcntl(PCpipe[0], F_SETFL, flag) == -1) 732 fatal("F_SETFL failed: %s", strerror(errno)); 733 734 /* set message discard mode */ 735 if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1) 736 fatal("I_SRDOPT RMSGD failed: %s", strerror(errno)); 737 738 /* register to receive SIGPOLL when data come */ 739 if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1) 740 fatal("I_SETSIG S_INPUT failed: %s", strerror(errno)); 741 742 #ifdef DEBUG 743 log("PCpipe[0]\t = %d", PCpipe[0]); 744 log("PCpipe[1]\t = %d", PCpipe[1]); 745 #endif 746 } 747