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

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 */