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