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