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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 33 #include <stdlib.h> 34 #include <stdio.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #include <poll.h> 38 #include <string.h> 39 #include <termio.h> 40 #include <signal.h> 41 #include <sys/types.h> 42 #include <sys/stropts.h> 43 #include <unistd.h> 44 #include <sys/wait.h> 45 #include "ttymon.h" 46 #include "tmstruct.h" 47 #include "tmextern.h" 48 #include "sac.h" 49 50 extern int Retry; 51 static struct pmtab *find_pid(); 52 static void kill_children(); 53 54 static struct pmtab *find_fd(); 55 static void pcsync_close(); 56 extern void sigalarm(); 57 extern void tmchild(); 58 59 /* 60 * fork_tmchild - fork child on the device 61 */ 62 static void 63 fork_tmchild(pmptr) 64 struct pmtab *pmptr; 65 { 66 pid_t pid; 67 sigset_t cset; 68 sigset_t tset; 69 int pcpipe0[2], pcpipe1[2]; 70 int p0; 71 72 #ifdef DEBUG 73 debug("in fork_tmchild"); 74 #endif 75 pmptr->p_inservice = FALSE; 76 77 /* 78 * initialize pipe. 79 * Child has pcpipe[0] pipe fd for reading and writing 80 * and closes pcpipe[1]. Parent has pcpipe[1] pipe fd for 81 * reading and writing and closes pcpipe[0]. 82 * 83 * This way if the child process exits the parent's block 84 * read on pipe will return immediately as the other end of 85 * the pipe has closed. Similarly if the parent process exits 86 * child's blocking read on the pipe will return immediately. 87 */ 88 89 if (((p0 = pipe(pcpipe0)) == -1) || (pipe(pcpipe1) == -1)) { 90 if (p0 == 0) { 91 close(pcpipe0[0]); 92 close(pcpipe0[1]); 93 } 94 log("pipe() failed: %s", strerror(errno)); 95 pmptr->p_status = VALID; 96 pmptr->p_pid = 0; 97 Retry = TRUE; 98 } 99 100 /* protect following region from SIGCLD */ 101 (void)sigprocmask(SIG_SETMASK, NULL, &cset); 102 tset = cset; 103 (void)sigaddset(&tset, SIGCLD); 104 (void)sigprocmask(SIG_SETMASK, &tset, NULL); 105 if( (pid=fork()) == 0 ) { 106 /* 107 * Close all file descriptors except pmptr->p_fd 108 * Wait for the parent process to close its fd 109 */ 110 pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd); 111 /* The CHILD */ 112 tmchild(pmptr); 113 /* tmchild should never return */ 114 fatal("tmchild for <%s> returns unexpected", pmptr->p_device); 115 } 116 else if (pid < 0) { 117 log("fork failed: %s", strerror(errno)); 118 pmptr->p_status = VALID; 119 pmptr->p_pid = 0; 120 Retry = TRUE; 121 } 122 else { 123 /* 124 * The PARENT - store pid of child and close the device 125 */ 126 pmptr->p_pid = pid; 127 } 128 if (pmptr->p_fd > 0) { 129 (void)close(pmptr->p_fd); 130 pmptr->p_fd = 0; 131 } 132 (void)sigprocmask(SIG_SETMASK, &cset, NULL); 133 /* 134 * Wait for child to close file descriptors 135 */ 136 pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd); 137 } 138 139 /* 140 * got_carrier - carrier is detected on the stream 141 * - depends on the flags, different action is taken 142 * - R_FLAG - wait for data 143 * - C_FLAG - if port is not disabled, fork tmchild 144 * - A_FLAG - wait for data 145 * - otherwise - write out prompt, then wait for data 146 */ 147 void 148 got_carrier(pmptr) 149 struct pmtab *pmptr; 150 { 151 flush_input(pmptr->p_fd); 152 153 if (pmptr->p_ttyflags & R_FLAG) { 154 #ifdef DEBUG 155 debug("R_FLAG"); 156 #endif 157 return; 158 } 159 else if ((pmptr->p_ttyflags & (C_FLAG|B_FLAG)) && 160 (State != PM_DISABLED) && 161 (!(pmptr->p_flags & X_FLAG))) { 162 fork_tmchild(pmptr); 163 } 164 else if (pmptr->p_ttyflags & A_FLAG) { 165 #ifdef DEBUG 166 debug("A_FLAG"); 167 #endif 168 return; 169 } 170 else if (pmptr->p_timeout) { 171 fork_tmchild(pmptr); 172 } 173 else if ( ! (pmptr->p_ttyflags & X_FLAG) ) { 174 write_prompt(pmptr->p_fd,pmptr,TRUE,TRUE); 175 } 176 } 177 178 /* 179 * got_data - data is detected on the stream, fork tmchild 180 */ 181 static void 182 got_data(pmptr) 183 struct pmtab *pmptr; 184 { 185 struct sigaction sigact; 186 187 if (tm_checklock(pmptr->p_fd) != 0) { 188 pmptr->p_status = LOCKED; 189 (void)close(pmptr->p_fd); 190 pmptr->p_fd = 0; 191 Nlocked++; 192 if (Nlocked == 1) { 193 sigact.sa_flags = 0; 194 sigact.sa_handler = sigalarm; 195 (void)sigemptyset(&sigact.sa_mask); 196 (void)sigaction(SIGALRM, &sigact, NULL); 197 (void)alarm(ALARMTIME); 198 } 199 } 200 else 201 fork_tmchild(pmptr); 202 } 203 /* 204 * got_hup - stream hangup is detected, close the device 205 */ 206 static void 207 got_hup(pmptr) 208 struct pmtab *pmptr; 209 { 210 #ifdef DEBUG 211 debug("in got hup"); 212 #endif 213 (void)close(pmptr->p_fd); 214 pmptr->p_fd = 0; 215 pmptr->p_inservice = 0; 216 Retry = TRUE; 217 } 218 219 220 /* 221 * do_poll - poll device 222 * - if POLLHUP received, close the device 223 * - if POLLIN received, fork tmchild. 224 */ 225 void 226 do_poll(fdp,nfds) 227 struct pollfd *fdp; 228 int nfds; 229 { 230 int i,n; 231 struct pmtab *pmptr; 232 233 n = poll(fdp, (unsigned long)nfds, -1); /* blocked poll */ 234 #ifdef DEBUG 235 debug("poll return"); 236 #endif 237 if (n < 0) { 238 if (errno == EINTR) /* interrupt by signal */ 239 return; 240 fatal("do_poll: poll failed: %s", strerror(errno)); 241 } 242 for (i = 0; (i < nfds)&&(n); i++,fdp++) { 243 if (fdp->revents != 0) { 244 n--; 245 if ((pmptr = find_fd(fdp->fd)) == NULL) { 246 log("do_poll: cannot find fd %d in pmtab", 247 fdp->fd); 248 continue; 249 } 250 else if (fdp->revents & POLLHUP) { 251 got_hup(pmptr); 252 } 253 else if (fdp->revents & POLLIN) { 254 #ifdef DEBUG 255 debug("got POLLIN"); 256 #endif 257 got_data(pmptr); 258 } else if (fdp->revents & POLLERR) { 259 fatal("ttymon[%d]: do_poll: POLLERR on fd %d", 260 getpid(), fdp->fd); 261 } 262 } 263 } 264 } 265 266 /* 267 * sigchild - handler for SIGCLD 268 * - find the pid of dead child 269 * - clean utmp if U_FLAG is set 270 */ 271 void 272 /*ARGSUSED*/ 273 sigchild(n) 274 int n; /* this is declared to make cc happy, but it is not used */ 275 { 276 struct pmtab *pmptr; 277 struct sigaction sigact; 278 siginfo_t info; 279 int status; 280 pid_t pid; 281 int rcode; 282 283 #ifdef DEBUG 284 debug("in sigchild"); 285 #endif 286 287 /* find all processes that died */ 288 for (;;) { 289 rcode = waitid(P_ALL, 0, &info, WNOHANG|WEXITED); 290 if (rcode == -1 && errno == EINTR) 291 continue; 292 293 /* If no more children have exited, just return */ 294 if (rcode == -1 || (pid = info.si_pid) == 0) 295 break; 296 297 /* construct status as returned from waitid() */ 298 status = info.si_status & 0377; 299 switch (info.si_code) { 300 case CLD_EXITED: 301 status <<= 8; 302 break; 303 case CLD_DUMPED: 304 status |= WCOREFLG; 305 break; 306 case CLD_KILLED: 307 break; 308 } 309 310 if ((pmptr = find_pid(pid)) == NULL) { 311 #ifdef DEBUG 312 log("cannot find dead child (%ld) in pmtab", pid); 313 #endif 314 /* 315 * This may happen if the entry is deleted from pmtab 316 * before the service exits. 317 * We try to cleanup utmp entry 318 */ 319 cleanut(pid, status); 320 } else { 321 if (pmptr->p_flags & U_FLAG) 322 cleanut(pid, status); 323 pmptr->p_status = VALID; 324 pmptr->p_fd = 0; 325 pmptr->p_pid = 0; 326 pmptr->p_inservice = 0; 327 Retry = TRUE; 328 } 329 } 330 } 331 332 /* 333 * sigterm - handler for SIGTERM 334 */ 335 void 336 sigterm() 337 { 338 fatal("caught SIGTERM"); 339 } 340 341 /* 342 * state_change - this is called when ttymon changes 343 * its internal state between enabled and disabled 344 */ 345 void 346 state_change() 347 { 348 struct pmtab *pmptr; 349 350 #ifdef DEBUG 351 debug("in state_change"); 352 #endif 353 354 /* 355 * closing PCpipe will cause attached non-service children 356 * to get SIGPOLL and exit 357 */ 358 (void)close(PCpipe[0]); 359 (void)close(PCpipe[1]); 360 361 /* reopen PCpipe */ 362 setup_PCpipe(); 363 364 /* 365 * also close all open ports so ttymon can start over 366 * with new internal state 367 */ 368 for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { 369 if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) { 370 (void)close(pmptr->p_fd); 371 pmptr->p_fd = 0; 372 } 373 } 374 Retry = TRUE; 375 376 } 377 378 /* 379 * re_read - reread pmtab 380 * - kill tmchild if entry changed 381 */ 382 void 383 re_read() 384 { 385 extern struct pollfd *Pollp; 386 sigset_t cset; 387 sigset_t tset; 388 389 (void)sigprocmask(SIG_SETMASK, NULL, &cset); 390 tset = cset; 391 (void)sigaddset(&tset, SIGCLD); 392 (void)sigprocmask(SIG_SETMASK, &tset, NULL); 393 if (Nlocked > 0) { 394 alarm(0); 395 Nlocked = 0; 396 } 397 read_pmtab(); 398 kill_children(); 399 (void)sigprocmask(SIG_SETMASK, &cset, NULL); 400 purge(); 401 402 if (Nentries > Npollfd) { 403 #ifdef DEBUG 404 debug("Nentries > Npollfd, reallocating pollfds"); 405 #endif 406 /* need to malloc more pollfd structure */ 407 free((char *)Pollp); 408 Npollfd = Nentries + 10; 409 if (Npollfd > Maxfds) 410 Npollfd = Maxfds; 411 if ((Pollp = (struct pollfd *) 412 malloc((unsigned)(Npollfd * sizeof(struct pollfd)))) 413 == (struct pollfd *)NULL) 414 fatal("malloc for Pollp failed"); 415 } 416 Retry = TRUE; 417 } 418 419 /* 420 * find_pid(pid) - find the corresponding pmtab entry for the pid 421 */ 422 static struct pmtab * 423 find_pid(pid) 424 pid_t pid; 425 { 426 struct pmtab *pmptr; 427 428 for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { 429 if (pmptr->p_pid == pid) { 430 return(pmptr); 431 } 432 } 433 return((struct pmtab *)NULL); 434 } 435 436 /* 437 * find_fd(fd) - find the corresponding pmtab entry for the fd 438 */ 439 static struct pmtab * 440 find_fd(fd) 441 int fd; 442 { 443 struct pmtab *pmptr; 444 445 for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { 446 if (pmptr->p_fd == fd) { 447 return(pmptr); 448 } 449 } 450 return((struct pmtab *)NULL); 451 } 452 453 /* 454 * kill_children() - if the pmtab entry has been changed, 455 * kill tmchild if it is not in service. 456 * - close the device if there is no tmchild 457 */ 458 static void 459 kill_children() 460 { 461 struct pmtab *pmptr; 462 for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { 463 if (pmptr->p_status == VALID) 464 continue; 465 if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) { 466 (void)close(pmptr->p_fd); 467 pmptr->p_fd = 0; 468 } 469 else if ((pmptr->p_fd == 0) && (pmptr->p_pid > 0) 470 && (pmptr->p_inservice == FALSE)) { 471 (void)kill(pmptr->p_pid, SIGTERM); 472 } 473 } 474 } 475 476 static void 477 mark_service(pid) 478 pid_t pid; 479 { 480 struct pmtab *pmptr; 481 #ifdef DEBUG 482 debug("in mark_service"); 483 #endif 484 if ((pmptr = find_pid(pid)) == NULL) { 485 log("mark_service: cannot find child (%ld) in pmtab", pid); 486 return; 487 } 488 pmptr->p_inservice = TRUE; 489 return; 490 } 491 492 /* 493 * read_pid(fd) - read pid info from PCpipe 494 */ 495 static void 496 read_pid(fd) 497 int fd; 498 { 499 int ret; 500 pid_t pid; 501 502 for (;;) { 503 if ((ret = read(fd,&pid,sizeof(pid))) < 0) { 504 if (errno == EINTR) 505 continue; 506 if (errno == EAGAIN) 507 return; 508 fatal("read PCpipe failed: %s", strerror(errno)); 509 } 510 if (ret == 0) 511 return; 512 if (ret != sizeof(pid)) 513 fatal("read return size incorrect, ret = %d", ret); 514 515 mark_service(pid); 516 } 517 } 518 519 /* 520 * sipoll_catch() - signal handle of SIGPOLL for ttymon 521 * - it will check both PCpipe and pmpipe 522 */ 523 void 524 sigpoll_catch() 525 { 526 int ret; 527 struct pollfd pfd[2]; 528 529 #ifdef DEBUG 530 debug("in sigpoll_catch"); 531 #endif 532 533 pfd[0].fd = PCpipe[0]; 534 pfd[1].fd = Pfd; 535 pfd[0].events = POLLIN; 536 pfd[1].events = POLLIN; 537 if ((ret = poll(pfd, 2, 0)) < 0) 538 fatal("sigpoll_catch: poll failed: %s", strerror(errno)); 539 540 if (ret > 0) { 541 if (pfd[0].revents & POLLIN) 542 read_pid(pfd[0].fd); 543 if (pfd[1].revents & POLLIN) 544 sacpoll(); 545 } 546 } 547 548 /*ARGSUSED*/ 549 void 550 sigalarm(signo) 551 int signo; 552 { 553 struct pmtab *pmptr; 554 struct sigaction sigact; 555 int fd; 556 extern int check_session(); 557 558 #ifdef DEBUG 559 debug("in sigalarm, Nlocked = %d", Nlocked); 560 #endif 561 for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) { 562 if ((pmptr->p_status == LOCKED) && (pmptr->p_fd == 0)) { 563 if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){ 564 log("open (%s) failed: %s", pmptr->p_device, 565 strerror(errno)); 566 pmptr->p_status = VALID; 567 Nlocked--; 568 Retry = TRUE; 569 } 570 else { 571 if (tm_checklock(fd) == 0) { 572 Nlocked--; 573 pmptr->p_fd = fd; 574 Retry = TRUE; 575 } 576 else 577 (void)close(fd); 578 } 579 } 580 else if ((pmptr->p_status == SESSION) && (pmptr->p_fd == 0)) { 581 if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){ 582 log("open (%s) failed: %s", pmptr->p_device, 583 strerror(errno)); 584 pmptr->p_status = VALID; 585 Nlocked--; 586 Retry = TRUE; 587 } 588 else { 589 if (check_session(fd) == 0) { 590 Nlocked--; 591 pmptr->p_fd = fd; 592 Retry = TRUE; 593 } 594 else 595 (void)close(fd); 596 } 597 } 598 else if ((pmptr->p_status == UNACCESS) && (pmptr->p_fd == 0)) { 599 if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){ 600 log("open (%s) failed: %s", pmptr->p_device, 601 strerror(errno)); 602 pmptr->p_status = VALID; 603 Nlocked--; 604 Retry = TRUE; 605 } 606 else { 607 Nlocked--; 608 pmptr->p_fd = fd; 609 Retry = TRUE; 610 } 611 } 612 } 613 if (Nlocked > 0) { 614 sigact.sa_flags = 0; 615 sigact.sa_handler = sigalarm; 616 (void)sigemptyset(&sigact.sa_mask); 617 (void)sigaction(SIGALRM, &sigact, NULL); 618 (void)alarm(ALARMTIME); 619 } 620 else { 621 sigact.sa_flags = 0; 622 sigact.sa_handler = SIG_IGN; 623 (void)sigemptyset(&sigact.sa_mask); 624 (void)sigaction(SIGALRM, &sigact, NULL); 625 } 626 } 627 628 /* 629 * pcsync_close - For the child process close all open fd's except 630 * the one that is passed to the routine. Coordinate the reads and 631 * writes to the pipes by the parent and child process to ensure 632 * the parent and child processes have closed all the file descriptors 633 * that are not needed any more. 634 */ 635 static void 636 pcsync_close(p0, p1, pid, fd) 637 int *p0; 638 int *p1; 639 int pid; 640 { 641 char ch; 642 643 if (pid == 0) { /* Child */ 644 struct pmtab *tp; 645 for (tp = PMtab; tp; tp = tp->p_next) 646 if ((tp->p_fd > 0) && (tp->p_fd != fd)) 647 close(tp->p_fd); 648 close(p0[1]); close(p1[0]); 649 if (read(p0[0], &ch, 1) == 1) 650 write(p1[1], "a", 1); 651 close(p0[0]); close(p1[1]); 652 } else { /* Parent */ 653 close(p0[0]); close(p1[1]); 654 if (write(p0[1], "a", 1) == 1) 655 read(p1[0], &ch, 1); 656 close(p0[1]); close(p1[0]); 657 } 658 }