[BACK]Return to child.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rdist

Annotation of src/usr.bin/rdist/child.c, Revision 1.2

1.1       dm          1: /*
                      2:  * Copyright (c) 1983 Regents of the University of California.
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  * 3. All advertising materials mentioning features or use of this software
                     14:  *    must display the following acknowledgement:
                     15:  *     This product includes software developed by the University of
                     16:  *     California, Berkeley and its contributors.
                     17:  * 4. Neither the name of the University nor the names of its contributors
                     18:  *    may be used to endorse or promote products derived from this software
                     19:  *    without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  */
                     33:
                     34: #ifndef lint
                     35: static char RCSid[] =
                     36: "$Id: child.c,v 6.27 1995/12/13 00:54:18 mcooper Exp $";
                     37:
                     38: static char sccsid[] = "@(#)docmd.c    5.1 (Berkeley) 6/6/85";
                     39:
                     40: static char copyright[] =
                     41: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
                     42:  All rights reserved.\n";
                     43: #endif /* not lint */
                     44:
                     45: /*
                     46:  * Functions for rdist related to children
                     47:  */
                     48:
                     49: #include "defs.h"
                     50: #include <sys/types.h>
                     51: #include <sys/wait.h>
                     52: #if    defined(NEED_SYS_SELECT_H)
                     53: #include <sys/select.h>
                     54: #endif /* NEED_SYS_SELECT_H */
                     55:
                     56: typedef enum _PROCSTATE {
                     57:     PSrunning,
                     58:     PSdead
                     59: } PROCSTATE;
                     60:
                     61: /*
                     62:  * Structure for child rdist processes mainted by the parent
                     63:  */
                     64: struct _child {
                     65:        char           *c_name;                 /* Name of child */
                     66:        int             c_readfd;               /* Read file descriptor */
                     67:        pid_t           c_pid;                  /* Process ID */
                     68:        PROCSTATE       c_state;                /* Running? */
                     69:        struct _child  *c_next;                 /* Next entry */
                     70: };
                     71: typedef struct _child CHILD;
                     72:
                     73: static CHILD          *childlist = NULL;       /* List of children */
                     74: int                    activechildren = 0;     /* Number of active children */
                     75: extern int             maxchildren;            /* Max active children */
                     76: static int             needscan = FALSE;       /* Need to scan children */
                     77:
                     78: /*
                     79:  * Remove a child that has died (exited)
                     80:  * from the list of active children
                     81:  */
                     82: static void removechild(child)
                     83:        CHILD *child;
                     84: {
                     85:        register CHILD *pc, *prevpc;
                     86:
                     87:        debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
                     88:                 child->c_name, child->c_pid, child->c_readfd);
                     89:
                     90:        /*
                     91:         * Find the child in the list
                     92:         */
                     93:        for (pc = childlist, prevpc = NULL; pc != NULL;
                     94:             prevpc = pc, pc = pc->c_next)
                     95:                if (pc == child)
                     96:                        break;
                     97:
                     98:        if (pc == NULL)
                     99:                error("RemoveChild called with bad child %s %d %d",
                    100:                      child->c_name, child->c_pid, child->c_readfd);
                    101:        else {
                    102:                /*
                    103:                 * Remove the child
                    104:                 */
                    105: #if    defined(POSIX_SIGNALS)
                    106:                sigset_t set;
                    107:
                    108:                sigemptyset(&set);
                    109:                sigaddset(&set, SIGCHLD);
                    110:                sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
                    111: #else  /* !POSIX_SIGNALS */
                    112:                int oldmask;
                    113:
                    114:                oldmask = sigblock(sigmask(SIGCHLD));
                    115: #endif /* POSIX_SIGNALS */
                    116:
                    117:                if (prevpc != NULL)
                    118:                        prevpc->c_next = pc->c_next;
                    119:                else
                    120:                        childlist = pc->c_next;
                    121:
                    122: #if    defined(POSIX_SIGNALS)
                    123:                sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)NULL);
                    124: #else
                    125:                sigsetmask(oldmask);
                    126: #endif /* POSIX_SIGNALS */
                    127:
                    128:                (void) free(child->c_name);
                    129:                --activechildren;
                    130:                (void) close(child->c_readfd);
                    131:                (void) free(pc);
                    132:        }
                    133:
                    134:        debugmsg(DM_CALL, "removechild() end");
                    135: }
                    136:
                    137: /*
                    138:  * Create a totally new copy of a child.
                    139:  */
                    140: static CHILD *copychild(child)
                    141:        CHILD *child;
                    142: {
                    143:        register CHILD *newc;
                    144:
                    145:        newc = (CHILD *) xmalloc(sizeof(CHILD));
                    146:
                    147:        newc->c_name = strdup(child->c_name);
                    148:        newc->c_readfd = child->c_readfd;
                    149:        newc->c_pid = child->c_pid;
                    150:        newc->c_state = child->c_state;
                    151:        newc->c_next = NULL;
                    152:
                    153:        return(newc);
                    154: }
                    155:
                    156: /*
                    157:  * Add a child to the list of children.
                    158:  */
                    159: static void addchild(child)
                    160:        CHILD *child;
                    161: {
                    162:        register CHILD *pc;
                    163:
                    164:        debugmsg(DM_CALL, "addchild() start\n");
                    165:
                    166:        pc = copychild(child);
                    167:        pc->c_next = childlist;
                    168:        childlist = pc;
                    169:
                    170:        ++activechildren;
                    171:
                    172:        debugmsg(DM_MISC,
                    173:                 "addchild() created '%s' pid %d fd %d (active=%d)\n",
                    174:                 child->c_name, child->c_pid, child->c_readfd, activechildren);
                    175: }
                    176:
                    177: /*
                    178:  * Read input from a child process.
                    179:  */
                    180: static void readchild(child)
                    181:        CHILD *child;
                    182: {
                    183:        char rbuf[BUFSIZ];
                    184:        int amt;
                    185:
                    186:        debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]",
                    187:                 child->c_name, child->c_pid, child->c_readfd);
                    188:
                    189:        /*
                    190:         * Check that this is a valid child.
                    191:         */
                    192:        if (child->c_name == NULL || child->c_readfd <= 0) {
                    193:                debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
                    194:                         child->c_name, child->c_pid, child->c_readfd);
                    195:                return;
                    196:        }
                    197:
                    198:        /*
                    199:         * Read from child and display the result.
                    200:         */
                    201:        while ((amt = read(child->c_readfd, rbuf, sizeof(rbuf))) > 0) {
                    202:                /* XXX remove these debug calls */
                    203:                debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %d bytes]",
                    204:                         child->c_name, child->c_pid, child->c_readfd, amt);
                    205:
                    206:                (void) xwrite(fileno(stdout), rbuf, amt);
                    207:
                    208:                debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
                    209:                         child->c_name, child->c_pid, child->c_readfd);
                    210:        }
                    211:
                    212:        debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %d errno = %d\n",
                    213:                 child->c_name, child->c_pid, child->c_readfd, amt, errno);
                    214:
                    215:        /*
                    216:         * See if we've reached EOF
                    217:         */
                    218:        if (amt == 0)
                    219:                debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
                    220:                         child->c_name, child->c_pid, child->c_readfd);
                    221: }
                    222:
                    223: /*
                    224:  * Wait for processes to exit.  If "block" is true, then we block
                    225:  * until a process exits.  Otherwise, we return right away.  If
                    226:  * a process does exit, then the pointer "statval" is set to the
                    227:  * exit status of the exiting process, if statval is not NULL.
                    228:  */
                    229: static int waitproc(statval, block)
                    230:        int *statval;
                    231:        int block;
                    232: {
                    233:        WAIT_ARG_TYPE status;
                    234:        int pid, exitval;
                    235:
                    236:        debugmsg(DM_CALL, "waitproc() %s, active children = %d...\n",
                    237:                 (block) ? "blocking" : "nonblocking", activechildren);
                    238:
                    239: #if    WAIT_TYPE == WAIT_WAITPID
                    240:        pid = waitpid(-1, &status, (block) ? 0 : WNOHANG);
                    241: #else
                    242: #if    WAIT_TYPE == WAIT_WAIT3
                    243:        pid = wait3(&status, (block) ? 0 : WNOHANG, NULL);
                    244: #endif /* WAIT_WAIT3 */
                    245: #endif /* WAIT_WAITPID */
                    246:
                    247: #if    defined(WEXITSTATUS)
                    248:        exitval = WEXITSTATUS(status);
                    249: #else
                    250:        exitval = status.w_retcode;
                    251: #endif /* defined(WEXITSTATUS) */
                    252:
                    253:        if (pid > 0 && exitval != 0) {
                    254:                nerrs++;
                    255:                debugmsg(DM_MISC,
                    256:                         "Child process %d exited with status %d.\n",
                    257:                         pid, exitval);
                    258:        }
                    259:
                    260:        if (statval)
                    261:                *statval = exitval;
                    262:
                    263:        debugmsg(DM_CALL, "waitproc() done (activechildren = %d)\n",
                    264:                 activechildren);
                    265:
                    266:        return(pid);
                    267: }
                    268:
                    269: /*
                    270:  * Check to see if any children have exited, and if so, read any unread
                    271:  * input and then remove the child from the list of children.
                    272:  */
                    273: static void reap()
                    274: {
                    275:        register CHILD *pc;
                    276:        int status = 0;
                    277:        pid_t pid;
                    278:
                    279:        debugmsg(DM_CALL, "reap() called\n");
                    280:
                    281:        /*
                    282:         * Reap every child that has exited.  Break out of the
                    283:         * loop as soon as we run out of children that have
                    284:         * exited so far.
                    285:         */
                    286:        for ( ; ; ) {
                    287:                /*
                    288:                 * Do a non-blocking check for exiting processes
                    289:                 */
                    290:                pid = waitproc(&status, FALSE);
                    291:                debugmsg(DM_MISC,
                    292:                         "reap() pid = %d status = %d activechildren=%d\n",
                    293:                         pid, status, activechildren);
                    294:
                    295:                /*
                    296:                 * See if a child really exited
                    297:                 */
                    298:                if (pid == 0)
                    299:                        break;
                    300:                if (pid < 0) {
                    301:                        if (errno != ECHILD)
                    302:                                error("Wait failed: %s", SYSERR);
                    303:                        break;
                    304:                }
                    305:
                    306:                /*
                    307:                 * Find the process (pid) and mark it as dead.
                    308:                 */
                    309:                for (pc = childlist; pc; pc = pc->c_next)
                    310:                        if (pc->c_pid == pid) {
                    311:                                needscan = TRUE;
                    312:                                pc->c_state = PSdead;
                    313:                        }
                    314:
                    315:        }
                    316:
                    317:        /*
                    318:         * Reset signals
                    319:         */
                    320:        (void) signal(SIGCHLD, reap);
                    321:
                    322:        debugmsg(DM_CALL, "reap() done\n");
                    323: }
                    324:
                    325: /*
                    326:  * Scan the children list to find the child that just exited,
                    327:  * read any unread input, then remove it from the list of active children.
                    328:  */
                    329: static void childscan()
                    330: {
                    331:        register CHILD *pc, *nextpc;
                    332:
                    333:        debugmsg(DM_CALL, "childscan() start");
                    334:
                    335:        for (pc = childlist; pc; pc = nextpc) {
                    336:                nextpc = pc->c_next;
                    337:                if (pc->c_state == PSdead) {
                    338:                        readchild(pc);
                    339:                        removechild(pc);
                    340:                }
                    341:        }
                    342:
                    343:        needscan = FALSE;
                    344:        debugmsg(DM_CALL, "childscan() end");
                    345: }
                    346:
                    347: /*
                    348: #if    defined HAVE_SELECT
                    349:  *
                    350:  * Wait for children to send output for us to read.
                    351:  *
                    352: #else  !HAVE_SELECT
                    353:  *
                    354:  * Wait up for children to exit.
                    355:  *
                    356: #endif
                    357:  */
                    358: extern void waitup()
                    359: {
                    360: #if    defined(HAVE_SELECT)
                    361:        register int count;
                    362:        register CHILD *pc;
                    363:        fd_set rchildfds;
                    364:
                    365:        debugmsg(DM_CALL, "waitup() start\n");
                    366:
                    367:        if (needscan)
                    368:                childscan();
                    369:
                    370:        if (activechildren <= 0)
                    371:                return;
                    372:
                    373:        /*
                    374:         * Settup which children we want to select() on.
                    375:         */
                    376:        FD_ZERO(&rchildfds);
                    377:        for (pc = childlist; pc; pc = pc->c_next)
                    378:                if (pc->c_readfd > 0) {
                    379:                        debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
                    380:                                 pc->c_readfd, pc->c_name);
                    381:                        FD_SET(pc->c_readfd, &rchildfds);
                    382:                }
                    383:
                    384:        /*
                    385:         * Actually call select()
                    386:         */
                    387:        /* XXX remove debugmsg() calls */
                    388:        debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n",
                    389:                 activechildren);
                    390:
                    391:        count = select(FD_SETSIZE, &rchildfds, (fd_set *) NULL,
                    392:                       (fd_set *) NULL, (struct timeval *) NULL);
                    393:
                    394:        debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n",
                    395:                 count, activechildren);
                    396:
                    397:        /*
                    398:         * select() will return count < 0 and errno == EINTR when
                    399:         * there are no active children left.
                    400:         */
                    401:        if (count < 0) {
                    402:                if (errno != EINTR)
                    403:                        error("Select failed reading children input: %s",
                    404:                              SYSERR);
                    405:                return;
                    406:        }
                    407:
                    408:        /*
                    409:         * This should never happen.
                    410:         */
                    411:        if (count == 0) {
                    412:                error("Select returned an unexpected count of 0.");
                    413:                return;
                    414:        }
                    415:
                    416:        /*
                    417:         * Go through the list of children and read from each child
                    418:         * which select() detected as ready for reading.
                    419:         */
                    420:        for (pc = childlist; pc && count > 0; pc = pc->c_next) {
                    421:                /*
                    422:                 * Make sure child still exists
                    423:                 */
                    424:                if (pc->c_name && kill(pc->c_pid, 0) < 0 &&
                    425:                    errno == ESRCH) {
                    426:                        debugmsg(DM_MISC,
                    427:                                 "waitup() proc %d (%s) died unexpectedly!",
                    428:                                 pc->c_pid, pc->c_name);
                    429:                        pc->c_state = PSdead;
                    430:                        needscan = TRUE;
                    431:                }
                    432:
                    433:                if (pc->c_name == NULL ||
                    434:                    !FD_ISSET(pc->c_readfd, &rchildfds))
                    435:                        continue;
                    436:
                    437:                readchild(pc);
                    438:                --count;
                    439:        }
                    440:
                    441: #else  /* !defined(HAVE_SELECT) */
                    442:
                    443:        /*
                    444:         * The non-select() version of waitproc()
                    445:         */
                    446:        debugmsg(DM_CALL, "waitup() start\n");
                    447:
                    448:        if (waitproc((int *) NULL, TRUE) > 0)
                    449:                --activechildren;
                    450:
                    451: #endif /* defined(HAVE_SELECT) */
                    452:        debugmsg(DM_CALL, "waitup() end\n");
                    453: }
                    454:
                    455: /*
                    456:  * Spawn (create) a new child process for "cmd".
                    457:  */
                    458: extern int spawn(cmd, cmdlist)
                    459:        struct cmd *cmd;
                    460:        struct cmd *cmdlist;
                    461: {
                    462:        pid_t pid;
                    463:        int fildes[2];
                    464:        char *childname = cmd->c_name;
                    465:
                    466:        if (pipe(fildes) < 0) {
                    467:                error("Cannot create pipe for %s: %s", childname, SYSERR);
                    468:                return(-1);
                    469:        }
                    470:
                    471:        pid = fork();
                    472:        if (pid == (pid_t)-1) {
                    473:                error("Cannot spawn child for %s: fork failed: %s",
                    474:                      childname, SYSERR);
                    475:                return(-1);
                    476:        } else if (pid > 0) {
                    477:                /*
                    478:                 * Parent
                    479:                 */
                    480:                static CHILD newchild;
                    481:
                    482: #if    defined(FORK_MISSES)
                    483:                /*
                    484:                 * XXX Some OS's have a bug where fork does not
                    485:                 * always return properly to the parent
                    486:                 * when a number of forks are done very quicky.
                    487:                 */
                    488:                sleep(2);
                    489: #endif /* FORK_MISSES */
                    490:
                    491:                /* Receive notification when the child exits */
                    492:                (void) signal(SIGCHLD, reap);
                    493:
                    494:                /* Settup the new child */
                    495:                newchild.c_next = NULL;
                    496:                newchild.c_name = childname;
                    497:                newchild.c_readfd = fildes[PIPE_READ];
                    498:                newchild.c_pid = pid;
                    499:                newchild.c_state = PSrunning;
                    500:
                    501:                /* We're not going to write to the child */
                    502:                (void) close(fildes[PIPE_WRITE]);
                    503:
                    504:                /* Set non-blocking I/O */
                    505:                if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
                    506:                        error("Set nonblocking I/O failed: %s", SYSERR);
                    507:                        return(-1);
                    508:                }
                    509:
                    510:                /* Add new child to child list */
                    511:                addchild(&newchild);
                    512:
                    513:                /* Mark all other entries for this host as assigned */
                    514:                markassigned(cmd, cmdlist);
                    515:
                    516:                debugmsg(DM_CALL,
                    517:                         "spawn() Forked child %d for host %s active = %d\n",
                    518:                         pid, childname, activechildren);
                    519:                return(pid);
                    520:        } else {
                    521:                /*
                    522:                 * Child
                    523:                 */
                    524:
                    525:                /* We're not going to read from our parent */
                    526:                (void) close(fildes[PIPE_READ]);
                    527:
                    528:                /* Make stdout and stderr go to PIPE_WRITE (our parent) */
                    529:                if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
                    530:                        error("Cannot duplicate stdout file descriptor: %s",
                    531:                              SYSERR);
                    532:                        return(-1);
                    533:                }
                    534:                if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
                    535:                        error("Cannot duplicate stderr file descriptor: %s",
                    536:                              SYSERR);
                    537:                        return(-1);
                    538:                }
                    539:
                    540:                return(0);
                    541:        }
                    542: }
                    543:
                    544:
                    545: /*
                    546:  * Enable or disable non-blocking I/O mode.
                    547:  *
                    548:  * Code is from INN by Rich Salz.
                    549:  */
                    550: #if    NBIO_TYPE == NBIO_IOCTL
                    551: #include <sys/ioctl.h>
                    552:
                    553: int setnonblocking(fd, flag)
                    554:        int fd;
                    555:        int flag;
                    556: {
                    557:        int state;
                    558:
                    559:        state = flag ? 1 : 0;
                    560:        return(ioctl(fd, FIONBIO, (char *)&state));
                    561: }
                    562:
                    563: #endif /* NBIO_IOCTL */
                    564:
                    565:
                    566: #if    NBIO_TYPE == NBIO_FCNTL
                    567: int setnonblocking(fd, flag)
                    568:        int fd;
                    569:        int flag;
                    570: {
                    571:        int     mode;
                    572:
                    573:        if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
                    574:                return(-1);
                    575:        if (flag)
                    576:                mode |= FNDELAY;
                    577:        else
                    578:                mode &= ~FNDELAY;
                    579:        return(fcntl(fd, F_SETFL, mode));
                    580: }
                    581: #endif /* NBIO_FCNTL */