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