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

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