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 }