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.h> 30 #include <stdlib.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 #include <sys/types.h> 34 #include <termio.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <poll.h> 38 #include <unistd.h> 39 #include "sys/stropts.h" 40 #include <sys/resource.h> 41 #include "sac.h" 42 #include "ttymon.h" 43 #include "tmstruct.h" 44 #include "tmextern.h" 45 #ifdef SYS_NAME 46 #include <sys/utsname.h> 47 #endif 48 49 static void openline(); 50 static void invoke_service(); 51 static char *do_autobaud(); 52 static struct Gdef *next_speed(); 53 static int check_hup(); 54 55 extern struct Gdef *get_speed(); 56 extern struct strbuf *peek_ptr, *do_peek(); 57 58 /* 59 * tmchild - process that handles peeking data, determine baud rate 60 * and invoke service on each individual port. 61 * 62 */ 63 void 64 tmchild(pmtab) 65 struct pmtab *pmtab; 66 { 67 register struct Gdef *speedef; 68 char *auto_speed = ""; 69 struct sigaction sigact; 70 71 #ifdef DEBUG 72 debug("in tmchild"); 73 #endif 74 peek_ptr = NULL; 75 if (pmtab->p_status != GETTY) { 76 child_sigcatch(); 77 (void) close(PCpipe[0]); /* close parent end of the pipe */ 78 if (ioctl(PCpipe[1], I_SETSIG, S_HANGUP) == -1) { 79 log("I_SETSIG failed: %s", strerror(errno)); 80 exit(1); 81 } 82 /* 83 * the following check is to make sure no hangup 84 * happens before registering for SIGPOLL 85 */ 86 if (check_hup(PCpipe[1])) { 87 #ifdef DEBUG 88 debug("PCpipe hungup, tmchild exiting"); 89 #endif 90 exit(1); 91 } 92 93 if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) { 94 if (pmtab->p_fd > 0) { 95 (void) close(pmtab->p_fd); 96 pmtab->p_fd = 0; 97 } 98 } 99 100 /* 101 * become the session leader so that a controlling tty 102 * will be allocated. 103 */ 104 (void) setsid(); 105 } 106 speedef = get_speed(pmtab->p_ttylabel); 107 openline(pmtab, speedef); 108 if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) { 109 if (pmtab->p_fd >= 0) { 110 if ((pmtab->p_modules != NULL)&&(*(pmtab->p_modules) != '\0')) { 111 if (push_linedisc(pmtab->p_fd, pmtab->p_modules, pmtab->p_device) == -1) { 112 (void) close(pmtab->p_fd); 113 return; 114 } 115 } 116 } 117 } 118 if ((pmtab->p_ttyflags & C_FLAG) && 119 (State != PM_DISABLED) && 120 (!(pmtab->p_flags & X_FLAG))) { 121 /* 122 * if "c" flag is set, and the port is not disabled 123 * invoke service immediately 124 */ 125 if (set_termio(0, speedef->g_fflags, NULL,FALSE,CANON) == -1) { 126 log("set final termio failed"); 127 exit(1); 128 } 129 invoke_service(pmtab); 130 exit(1); /*NOTREACHED*/ 131 } 132 if (speedef->g_autobaud & A_FLAG) { 133 auto_speed = do_autobaud(pmtab, speedef); 134 } 135 if (set_termio(0, speedef->g_fflags, NULL, FALSE, CANON) == -1) { 136 log("set final termio failed"); 137 exit(1); 138 } 139 if ((pmtab->p_ttyflags & (R_FLAG|A_FLAG)) || 140 (pmtab->p_status == GETTY) || (pmtab->p_timeout > 0)) { 141 write_prompt(1, pmtab, TRUE, TRUE); 142 if (pmtab->p_timeout) { 143 sigact.sa_flags = 0; 144 sigact.sa_handler = timedout; 145 (void) sigemptyset(&sigact.sa_mask); 146 (void) sigaction(SIGALRM, &sigact, NULL); 147 (void) alarm((unsigned)pmtab->p_timeout); 148 } 149 } 150 else if ((pmtab->p_ttyflags & (B_FLAG))) 151 write_prompt(pmtab->p_fd, pmtab, TRUE, TRUE); 152 153 154 /* Loop until user is successful in invoking service. */ 155 for (;;) { 156 157 /* Peek the user's typed response and respond appropriately. */ 158 switch (poll_data()) { 159 case GOODNAME: 160 #ifdef DEBUG 161 debug("got GOODNAME"); 162 #endif 163 if (pmtab->p_timeout) { 164 (void) alarm((unsigned)0); 165 sigact.sa_flags = 0; 166 sigact.sa_handler = SIG_DFL; 167 (void) sigemptyset(&sigact.sa_mask); 168 (void) sigaction(SIGALRM, &sigact, NULL); 169 } 170 if ((State == PM_DISABLED)||(pmtab->p_flags & X_FLAG)){ 171 write_prompt(1, pmtab, TRUE, FALSE); 172 break; 173 } 174 if (set_termio(0, speedef->g_fflags, auto_speed, 175 FALSE, CANON) == -1) { 176 log("set final termio failed"); 177 exit(1); 178 } 179 invoke_service(pmtab); 180 exit(1); /*NOTREACHED*/ 181 182 case BADSPEED: 183 /* wrong speed! try next speed in the list. */ 184 speedef = next_speed(speedef); 185 #ifdef DEBUG 186 debug("BADSPEED: setup next speed"); 187 #endif 188 if (speedef->g_autobaud & A_FLAG) { 189 if (auto_termio(0) == -1) { 190 exit(1); 191 } 192 auto_speed = do_autobaud(pmtab, speedef); 193 } 194 else { 195 auto_speed = NULL; 196 /* 197 * this reset may fail if the speed is not 198 * supported by the system 199 * we just cycle through it to the next one 200 */ 201 if (set_termio(0, speedef->g_iflags, NULL, 202 FALSE, CANON) != 0) { 203 log("Warning -- speed of <%s> may " 204 "be not supported by the system", 205 speedef->g_id); 206 } 207 } 208 write_prompt(1, pmtab, TRUE, TRUE); 209 break; 210 211 case NONAME: 212 #ifdef DEBUG 213 debug("got NONAME"); 214 #endif 215 write_prompt(1, pmtab, FALSE, FALSE); 216 break; 217 218 } /* end switch */ 219 220 peek_ptr = NULL; 221 if (pmtab->p_timeout) { 222 sigact.sa_flags = 0; 223 sigact.sa_handler = timedout; 224 (void) sigemptyset(&sigact.sa_mask); 225 (void) sigaction(SIGALRM, &sigact, NULL); 226 (void) alarm((unsigned)pmtab->p_timeout); 227 } 228 } /* end for loop */ 229 } 230 231 static void 232 openline(pmtab, speedef) 233 struct pmtab *pmtab; 234 struct Gdef *speedef; 235 { 236 char buffer[5]; 237 int rtn = 0; 238 int line_count; 239 240 #ifdef DEBUG 241 debug("in openline"); 242 #endif 243 if (pmtab->p_status != GETTY) { 244 (void) close(0); 245 /* open should return fd 0, if not, then close it */ 246 if ((pmtab->p_fd = open(pmtab->p_device, O_RDWR)) != 0) { 247 log("open \"%s\" failed: %s", pmtab->p_device, 248 strerror(errno)); 249 exit(1); 250 } 251 } 252 (void) close(1); 253 (void) close(2); 254 (void) dup(0); 255 (void) dup(0); 256 257 if (pmtab->p_ttyflags & R_FLAG) { /* wait_read is needed */ 258 if (pmtab->p_count) { 259 if (peek_ptr != NULL) 260 if ((peek_ptr->buf[0]&0x7F) == '\n' || 261 (peek_ptr->buf[0]&0x7F) == '\r') 262 pmtab->p_count--; 263 264 /* 265 * - wait for "p_count" lines 266 * - datakit switch does not 267 * know you are a host or a terminal 268 * - so it send you several lines of msg 269 * - we need to swallow that msg 270 * - we assume the baud rate is correct 271 * - if it is not, '\n' will not look like '\n' 272 * and we will wait forever here 273 */ 274 if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) { 275 log("set final termio failed"); 276 exit(1); 277 } 278 for (line_count = 0; line_count < pmtab->p_count; ) { 279 if (read(0, buffer, 1) < 0 280 || *buffer == '\0' 281 || *buffer == '\004') { 282 (void) close(0); 283 exit(0); 284 } 285 if (*buffer == '\n') 286 line_count++; 287 } 288 } 289 else { /* wait for 1 char */ 290 if (peek_ptr == NULL) { 291 if (set_termio(0, NULL, NULL,TRUE,RAW) == -1) { 292 log("set termio RAW failed"); 293 exit(1); 294 } 295 rtn = read(0, buffer, 1); 296 } else 297 *buffer = (peek_ptr->buf[0]&0x7F); 298 299 /* 300 * NOTE: Cu on a direct line when ~. is encountered will 301 * send EOTs to the other side. EOT=\004 302 */ 303 if (rtn < 0 || *buffer == '\004') { 304 (void) close(0); 305 exit(0); 306 } 307 } 308 peek_ptr = NULL; 309 if (!(pmtab->p_ttyflags & A_FLAG)) { /* autobaud not enabled */ 310 if (set_termio(0, speedef->g_fflags, NULL, TRUE, CANON) == -1) { 311 log("set final termio failed"); 312 exit(1); 313 } 314 } 315 } 316 if (pmtab->p_ttyflags & B_FLAG) { /* port is bi-directional */ 317 /* set advisory lock on the line */ 318 if (tm_lock(0) != 0) { 319 /* 320 * device is locked 321 * child exits and let the parent wait for 322 * the lock to go away 323 */ 324 exit(0); 325 } 326 /* change ownership back to root */ 327 (void) fchown(0, ROOTUID, Tty_gid); 328 (void) fchmod(0, 0620); 329 } 330 return; 331 } 332 333 /* 334 * write_prompt - write the msg to fd 335 * - if flush is set, flush input queue 336 * - if clear is set, write a new line 337 */ 338 void 339 write_prompt(fd, pmtab, flush, clear) 340 int fd; 341 struct pmtab *pmtab; 342 int flush, clear; 343 { 344 345 #ifdef DEBUG 346 debug("in write_prompt"); 347 #endif 348 if (flush) 349 flush_input(fd); 350 if (clear) { 351 (void) write(fd, "\r\n", 2); 352 } 353 #ifdef SYS_NAME 354 sys_name(fd); 355 #endif 356 /* Print prompt/disable message. */ 357 if ((State == PM_DISABLED) || (pmtab->p_flags & X_FLAG)) 358 (void)write(fd, pmtab->p_dmsg, (unsigned)strlen(pmtab->p_dmsg)); 359 else 360 (void) write(fd, pmtab->p_prompt, 361 (unsigned)strlen(pmtab->p_prompt)); 362 } 363 364 /* 365 * timedout - input period timed out 366 */ 367 void 368 timedout() 369 { 370 exit(1); 371 } 372 373 #ifdef SYS_NAME 374 /* 375 * void sys_name() - generate a msg with system id 376 * - print out /etc/issue file if it exists 377 */ 378 void 379 sys_name(fd) 380 int fd; 381 { 382 char *ptr, buffer[BUFSIZ]; 383 FILE *fp; 384 385 #if 0 /* 1111333 - don't print node name, we already do this elsewhere */ 386 struct utsname utsname; 387 388 if (uname(&utsname) != FAILURE) { 389 (void) sprintf(buffer, "%.9s\r\n", utsname.nodename); 390 (void) write(fd, buffer, strlen(buffer)); 391 } 392 #endif 393 394 if ((fp = fopen(ISSUEFILE, "r")) != NULL) { 395 while ((ptr = fgets(buffer, sizeof (buffer), fp)) != NULL) { 396 (void) write(fd, ptr, strlen(ptr)); 397 } 398 (void) fclose(fp); 399 } 400 } 401 #endif 402 403 404 /* 405 * do_autobaud - do autobaud 406 * - if it succeed, set the new speed and return 407 * - if it failed, it will get the nextlabel 408 * - if next entry is also autobaud, 409 * it will loop back to do autobaud again 410 * - otherwise, it will set new termio and return 411 */ 412 static char * 413 do_autobaud(pmtab, speedef) 414 struct pmtab *pmtab; 415 struct Gdef *speedef; 416 { 417 int done = FALSE; 418 char *auto_speed; 419 #ifdef DEBUG 420 debug("in do_autobaud"); 421 #endif 422 while (!done) { 423 if ((auto_speed = autobaud(0, pmtab->p_timeout)) == NULL) { 424 speedef = next_speed(speedef); 425 if (speedef->g_autobaud & A_FLAG) { 426 continue; 427 } 428 else { 429 if (set_termio(0, speedef->g_iflags, NULL, 430 TRUE, CANON) != 0) { 431 exit(1); 432 } 433 done = TRUE; 434 } 435 } 436 else { 437 if (set_termio(0, speedef->g_fflags, auto_speed, 438 TRUE, CANON) != 0) { 439 exit(1); 440 } 441 done = TRUE; 442 } 443 } 444 #ifdef DEBUG 445 debug("autobaud done"); 446 #endif 447 return (auto_speed); 448 } 449 450 /* 451 * next_speed(speedef) 452 * - find the next entry according to nextlabel. If "nextlabel" 453 * is not valid, go back to the old ttylabel. 454 */ 455 456 static struct Gdef * 457 next_speed(speedef) 458 struct Gdef *speedef; 459 { 460 struct Gdef *sp; 461 462 if (strcmp(speedef->g_nextid, speedef->g_id) == 0) 463 return (speedef); 464 if ((sp = find_def(speedef->g_nextid)) == NULL) { 465 log("%s's next speed-label (%s) is bad.", speedef->g_id, 466 speedef->g_nextid); 467 468 /* go back to the original entry. */ 469 if ((sp = find_def(speedef->g_id)) == NULL) { 470 /* if failed, complain and quit. */ 471 log("unable to find (%s) again", speedef->g_id); 472 exit(1); 473 } 474 } 475 return (sp); 476 } 477 478 /* 479 * inform_parent() - inform ttymon that tmchild is going to exec service 480 */ 481 static void 482 inform_parent(fd) 483 int fd; 484 { 485 pid_t pid; 486 487 pid = getpid(); 488 (void) write(fd, &pid, sizeof (pid)); 489 } 490 491 static char pbuf[BUFSIZ]; /* static buf for TTYPROMPT */ 492 static char hbuf[BUFSIZ]; /* static buf for HOME */ 493 static char tbuf[BUFSIZ]; /* static buf for TERM */ 494 495 /* 496 * void invoke_service - invoke the service 497 */ 498 499 static void 500 invoke_service(pmtab) 501 struct pmtab *pmtab; 502 { 503 char *argvp[MAXARGS]; /* service cmd args */ 504 int cnt = 0; /* arg counter */ 505 int i, fd; 506 struct sigaction sigact; 507 extern struct rlimit Rlimit; 508 509 #ifdef DEBUG 510 debug("in invoke_service"); 511 #endif 512 513 if (tcgetsid(0) != getsid(getpid())) { 514 cons_printf("Warning -- ttymon cannot allocate controlling " 515 "tty on \"%s\",\n", pmtab->p_device); 516 cons_printf("\tThere may be another session active on this " 517 "port.\n"); 518 519 if (strcmp("/dev/console", pmtab->p_device) != 0) { 520 /* 521 * if not on console, write to stderr to warn the user 522 * also. 523 */ 524 (void) fprintf(stderr, "Warning -- ttymon cannot " 525 "allocate controlling tty on \"%s\",\n", 526 pmtab->p_device); 527 (void) fprintf(stderr, "\tthere may be another session " 528 "active on this port.\n"); 529 } 530 } 531 532 if (pmtab->p_status != GETTY) { 533 inform_parent(PCpipe[1]); 534 sigact.sa_flags = 0; 535 sigact.sa_handler = SIG_DFL; 536 (void) sigemptyset(&sigact.sa_mask); 537 (void) sigaction(SIGPOLL, &sigact, NULL); 538 } 539 540 if (pmtab->p_flags & U_FLAG) { 541 if (account(pmtab->p_device) != 0) { 542 log("invoke_service: account failed"); 543 exit(1); 544 } 545 } 546 547 /* parse command line */ 548 mkargv(pmtab->p_server, &argvp[0], &cnt, MAXARGS-1); 549 550 if (!(pmtab->p_ttyflags & C_FLAG)) { 551 (void) sprintf(pbuf, "TTYPROMPT=%s", pmtab->p_prompt); 552 if (putenv(pbuf)) { 553 log("cannot expand service <%s> environment", argvp[0]); 554 exit(1); 555 } 556 } 557 if (pmtab->p_status != GETTY) { 558 (void) sprintf(hbuf, "HOME=%s", pmtab->p_dir); 559 if (putenv(hbuf)) { 560 log("cannot expand service <%s> environment", argvp[0]); 561 exit(1); 562 } 563 #ifdef DEBUG 564 debug("about to run config script"); 565 #endif 566 if ((i = doconfig(0, pmtab->p_tag, 0)) != 0) { 567 if (i < 0) { 568 log("doconfig failed, system error"); 569 } 570 else { 571 log("doconfig failed on line %d of script %s", 572 i, pmtab->p_tag); 573 } 574 exit(1); 575 } 576 } 577 578 if (setgid(pmtab->p_gid)) { 579 log("cannot set group id to %ld: %s", pmtab->p_gid, 580 strerror(errno)); 581 exit(1); 582 } 583 584 if (setuid(pmtab->p_uid)) { 585 log("cannot set user id to %ld: %s", pmtab->p_uid, 586 strerror(errno)); 587 exit(1); 588 } 589 590 if (chdir(pmtab->p_dir)) { 591 log("cannot chdir to %s: %s", pmtab->p_dir, strerror(errno)); 592 exit(1); 593 } 594 595 if (pmtab->p_uid != ROOTUID) { 596 /* change ownership and mode of device */ 597 (void) fchown(0, pmtab->p_uid, Tty_gid); 598 (void) fchmod(0, 0620); 599 } 600 601 602 if (pmtab->p_status != GETTY) { 603 sigact.sa_flags = 0; 604 sigact.sa_handler = SIG_DFL; 605 (void) sigemptyset(&sigact.sa_mask); 606 (void) sigaction(SIGINT, &sigact, NULL); 607 if (setrlimit(RLIMIT_NOFILE, &Rlimit) == -1) { 608 log("setrlimit failed: %s", strerror(errno)); 609 exit(1); 610 } 611 /* invoke the service */ 612 log("Starting service (%s) on %s", argvp[0], pmtab->p_device); 613 } 614 615 if (pmtab->p_termtype != (char *)NULL) { 616 (void) sprintf(tbuf, "TERM=%s", pmtab->p_termtype); 617 if (putenv(tbuf)) { 618 log("cannot expand service <%s> environment", argvp[0]); 619 exit(1); 620 } 621 } 622 /* restore signal handlers and mask */ 623 (void) sigaction(SIGINT, &Sigint, NULL); 624 (void) sigaction(SIGALRM, &Sigalrm, NULL); 625 (void) sigaction(SIGPOLL, &Sigpoll, NULL); 626 (void) sigaction(SIGQUIT, &Sigquit, NULL); 627 (void) sigaction(SIGCLD, &Sigcld, NULL); 628 (void) sigaction(SIGTERM, &Sigterm, NULL); 629 #ifdef DEBUG 630 (void) sigaction(SIGUSR1, &Sigusr1, NULL); 631 (void) sigaction(SIGUSR2, &Sigusr2, NULL); 632 #endif 633 (void) sigprocmask(SIG_SETMASK, &Origmask, NULL); 634 (void) execve(argvp[0], argvp, environ); 635 636 /* exec returns only on failure! */ 637 log("tmchild: exec service failed: %s", strerror(errno)); 638 exit(1); 639 } 640 641 /* 642 * check_hup(fd) - do a poll on fd to check if it is in hangup state 643 * - return 1 if hangup, otherwise return 0 644 */ 645 646 static int 647 check_hup(fd) 648 int fd; 649 { 650 int ret; 651 struct pollfd pfd[1]; 652 653 pfd[0].fd = fd; 654 pfd[0].events = POLLHUP; 655 for (;;) { 656 ret = poll(pfd, 1, 0); 657 if (ret < 0) { 658 if (errno == EINTR) 659 continue; 660 log("check_hup: poll failed: %s", strerror(errno)); 661 exit(1); 662 } 663 else if (ret > 0) { 664 if (pfd[0].revents & POLLHUP) { 665 return (1); 666 } 667 } 668 return (0); 669 } 670 } 671 672 /* 673 * sigpoll() - SIGPOLL handle for tmchild 674 * - when SIGPOLL is received by tmchild, 675 * the pipe between ttymon and tmchild is broken. 676 * Something must happen to ttymon. 677 */ 678 void 679 sigpoll() 680 { 681 #ifdef DEBUG 682 debug("tmchild got SIGPOLL, exiting"); 683 #endif 684 exit(1); 685 } 686