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

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