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

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