[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.11

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