Annotation of embedaddon/sudo/src/exec.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
        !             3:  *
        !             4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
        !             7:  *
        !             8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            15:  */
        !            16: 
        !            17: #include <config.h>
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/param.h>
        !            21: #ifdef HAVE_SYS_SYSMACROS_H
        !            22: # include <sys/sysmacros.h>
        !            23: #endif
        !            24: #include <sys/socket.h>
        !            25: #include <sys/stat.h>
        !            26: #include <sys/time.h>
        !            27: #include <sys/wait.h>
        !            28: #include <sys/ioctl.h>
        !            29: #ifdef HAVE_SYS_SELECT_H
        !            30: # include <sys/select.h>
        !            31: #endif /* HAVE_SYS_SELECT_H */
        !            32: #include <stdio.h>
        !            33: #ifdef STDC_HEADERS
        !            34: # include <stdlib.h>
        !            35: # include <stddef.h>
        !            36: #else
        !            37: # ifdef HAVE_STDLIB_H
        !            38: #  include <stdlib.h>
        !            39: # endif
        !            40: #endif /* STDC_HEADERS */
        !            41: #ifdef HAVE_STRING_H
        !            42: # include <string.h>
        !            43: #endif /* HAVE_STRING_H */
        !            44: #ifdef HAVE_STRINGS_H
        !            45: # include <strings.h>
        !            46: #endif /* HAVE_STRINGS_H */
        !            47: #ifdef HAVE_UNISTD_H
        !            48: # include <unistd.h>
        !            49: #endif /* HAVE_UNISTD_H */
        !            50: #if TIME_WITH_SYS_TIME
        !            51: # include <time.h>
        !            52: #endif
        !            53: #ifdef HAVE_SETLOCALE
        !            54: # include <locale.h>
        !            55: #endif
        !            56: #include <errno.h>
        !            57: #include <fcntl.h>
        !            58: #include <signal.h>
        !            59: #include <termios.h>
        !            60: 
        !            61: #include "sudo.h"
        !            62: #include "sudo_exec.h"
        !            63: #include "sudo_plugin.h"
        !            64: #include "sudo_plugin_int.h"
        !            65: 
        !            66: /* Shared with exec_pty.c for use with handler(). */
        !            67: int signal_pipe[2];
        !            68: 
        !            69: /* We keep a tailq of signals to forward to child. */
        !            70: struct sigforward {
        !            71:     struct sigforward *prev, *next;
        !            72:     int signo;
        !            73: };
        !            74: TQ_DECLARE(sigforward)
        !            75: static struct sigforward_list sigfwd_list;
        !            76: 
        !            77: static int handle_signals(int fd, pid_t child, int log_io,
        !            78:     struct command_status *cstat);
        !            79: static void forward_signals(int fd);
        !            80: static void schedule_signal(int signo);
        !            81: 
        !            82: /*
        !            83:  * Like execve(2) but falls back to running through /bin/sh
        !            84:  * ala execvp(3) if we get ENOEXEC.
        !            85:  */
        !            86: int
        !            87: my_execve(const char *path, char *const argv[], char *const envp[])
        !            88: {
        !            89:     execve(path, argv, envp);
        !            90:     if (errno == ENOEXEC) {
        !            91:        int argc;
        !            92:        char **nargv;
        !            93: 
        !            94:        for (argc = 0; argv[argc] != NULL; argc++)
        !            95:            continue;
        !            96:        nargv = emalloc2(argc + 2, sizeof(char *));
        !            97:        nargv[0] = "sh";
        !            98:        nargv[1] = (char *)path;
        !            99:        memcpy(nargv + 2, argv + 1, argc * sizeof(char *));
        !           100:        execve(_PATH_BSHELL, nargv, envp);
        !           101:        efree(nargv);
        !           102:     }
        !           103:     return -1;
        !           104: }
        !           105: 
        !           106: /*
        !           107:  * Fork and execute a command, returns the child's pid.
        !           108:  * Sends errno back on sv[1] if execve() fails.
        !           109:  */
        !           110: static int fork_cmnd(struct command_details *details, int sv[2])
        !           111: {
        !           112:     struct command_status cstat;
        !           113:     sigaction_t sa;
        !           114:     pid_t child;
        !           115: 
        !           116:     zero_bytes(&sa, sizeof(sa));
        !           117:     sigemptyset(&sa.sa_mask);
        !           118:     sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
        !           119:     sa.sa_handler = handler;
        !           120:     sigaction(SIGCONT, &sa, NULL);
        !           121: 
        !           122:     child = fork();
        !           123:     switch (child) {
        !           124:     case -1:
        !           125:        error(1, _("unable to fork"));
        !           126:        break;
        !           127:     case 0:
        !           128:        /* child */
        !           129:        close(sv[0]);
        !           130:        close(signal_pipe[0]);
        !           131:        close(signal_pipe[1]);
        !           132:        fcntl(sv[1], F_SETFD, FD_CLOEXEC);
        !           133:        restore_signals();
        !           134:        if (exec_setup(details, NULL, -1) == TRUE) {
        !           135:            /* headed for execve() */
        !           136:            if (details->closefrom >= 0)
        !           137:                closefrom(details->closefrom);
        !           138: #ifdef HAVE_SELINUX
        !           139:            if (ISSET(details->flags, CD_RBAC_ENABLED))
        !           140:                selinux_execve(details->command, details->argv, details->envp);
        !           141:            else
        !           142: #endif
        !           143:                my_execve(details->command, details->argv, details->envp);
        !           144:        }
        !           145:        cstat.type = CMD_ERRNO;
        !           146:        cstat.val = errno;
        !           147:        send(sv[1], &cstat, sizeof(cstat), 0);
        !           148:        _exit(1);
        !           149:     }
        !           150:     return child;
        !           151: }
        !           152: 
        !           153: static struct signal_state {
        !           154:     int signo;
        !           155:     sigaction_t sa;
        !           156: } saved_signals[] = {
        !           157:     { SIGALRM },
        !           158:     { SIGCHLD },
        !           159:     { SIGCONT },
        !           160:     { SIGHUP },
        !           161:     { SIGINT },
        !           162:     { SIGPIPE },
        !           163:     { SIGQUIT },
        !           164:     { SIGTERM },
        !           165:     { SIGTSTP },
        !           166:     { SIGTTIN },
        !           167:     { SIGTTOU },
        !           168:     { SIGUSR1 },
        !           169:     { SIGUSR2 },
        !           170:     { -1 }
        !           171: };
        !           172: 
        !           173: /*
        !           174:  * Save signal handler state so it can be restored before exec.
        !           175:  */
        !           176: void
        !           177: save_signals(void)
        !           178: {
        !           179:     struct signal_state *ss;
        !           180: 
        !           181:     for (ss = saved_signals; ss->signo != -1; ss++)
        !           182:        sigaction(ss->signo, NULL, &ss->sa);
        !           183: }
        !           184: 
        !           185: /*
        !           186:  * Restore signal handlers to initial state.
        !           187:  */
        !           188: void
        !           189: restore_signals(void)
        !           190: {
        !           191:     struct signal_state *ss;
        !           192: 
        !           193:     for (ss = saved_signals; ss->signo != -1; ss++)
        !           194:        sigaction(ss->signo, &ss->sa, NULL);
        !           195: }
        !           196: 
        !           197: /*
        !           198:  * Execute a command, potentially in a pty with I/O loggging.
        !           199:  * This is a little bit tricky due to how POSIX job control works and
        !           200:  * we fact that we have two different controlling terminals to deal with.
        !           201:  */
        !           202: int
        !           203: sudo_execve(struct command_details *details, struct command_status *cstat)
        !           204: {
        !           205:     int maxfd, n, nready, sv[2], log_io = FALSE;
        !           206:     const char *utmp_user = NULL;
        !           207:     fd_set *fdsr, *fdsw;
        !           208:     sigaction_t sa;
        !           209:     pid_t child;
        !           210: 
        !           211:     /* If running in background mode, fork and exit. */
        !           212:     if (ISSET(details->flags, CD_BACKGROUND)) {
        !           213:        switch (fork()) {
        !           214:            case -1:
        !           215:                cstat->type = CMD_ERRNO;
        !           216:                cstat->val = errno;
        !           217:                return -1;
        !           218:            case 0:
        !           219:                /* child continues without controlling terminal */
        !           220:                (void)setpgid(0, 0);
        !           221:                break;
        !           222:            default:
        !           223:                /* parent exits (but does not flush buffers) */
        !           224:                _exit(0);
        !           225:        }
        !           226:     }
        !           227: 
        !           228:     /*
        !           229:      * If we have an I/O plugin or the policy plugin has requested one, we
        !           230:      * need to allocate a pty.  It is OK to set log_io in the pty-only case
        !           231:      * as the io plugin tailqueue will be empty and no I/O logging will occur.
        !           232:      */
        !           233:     if (!tq_empty(&io_plugins) || ISSET(details->flags, CD_USE_PTY)) {
        !           234:        log_io = TRUE;
        !           235:        if (ISSET(details->flags, CD_SET_UTMP))
        !           236:            utmp_user = details->utmp_user ? details->utmp_user : user_details.username;
        !           237:        sudo_debug(8, "allocate pty for I/O logging");
        !           238:        pty_setup(details->euid, user_details.tty, utmp_user);
        !           239:     }
        !           240: 
        !           241:     /*
        !           242:      * We communicate with the child over a bi-directional pair of sockets.
        !           243:      * Parent sends signal info to child and child sends back wait status.
        !           244:      */
        !           245:     if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1)
        !           246:        error(1, _("unable to create sockets"));
        !           247: 
        !           248:     /*
        !           249:      * We use a pipe to atomically handle signal notification within
        !           250:      * the select() loop.
        !           251:      */
        !           252:     if (pipe_nonblock(signal_pipe) != 0)
        !           253:        error(1, _("unable to create pipe"));
        !           254: 
        !           255:     zero_bytes(&sa, sizeof(sa));
        !           256:     sigemptyset(&sa.sa_mask);
        !           257: 
        !           258:     /*
        !           259:      * Signals for forward to the child process (excluding SIGALRM and SIGCHLD).
        !           260:      * Note: HP-UX select() will not be interrupted if SA_RESTART set.
        !           261:      */
        !           262:     sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
        !           263:     sa.sa_handler = handler;
        !           264:     sigaction(SIGALRM, &sa, NULL);
        !           265:     sigaction(SIGCHLD, &sa, NULL);
        !           266:     sigaction(SIGHUP, &sa, NULL);
        !           267:     sigaction(SIGINT, &sa, NULL);
        !           268:     sigaction(SIGPIPE, &sa, NULL);
        !           269:     sigaction(SIGQUIT, &sa, NULL);
        !           270:     sigaction(SIGTERM, &sa, NULL);
        !           271:     sigaction(SIGUSR1, &sa, NULL);
        !           272:     sigaction(SIGUSR2, &sa, NULL);
        !           273: 
        !           274:     /* Max fd we will be selecting on. */
        !           275:     maxfd = MAX(sv[0], signal_pipe[0]);
        !           276: 
        !           277:     /*
        !           278:      * Child will run the command in the pty, parent will pass data
        !           279:      * to and from pty.  Adjusts maxfd as needed.
        !           280:      */
        !           281:     if (log_io)
        !           282:        child = fork_pty(details, sv, &maxfd);
        !           283:     else
        !           284:        child = fork_cmnd(details, sv);
        !           285:     close(sv[1]);
        !           286: 
        !           287:     /* Set command timeout if specified. */
        !           288:     if (ISSET(details->flags, CD_SET_TIMEOUT))
        !           289:        alarm(details->timeout);
        !           290: 
        !           291: #ifdef HAVE_SETLOCALE
        !           292:     /*
        !           293:      * I/O logging must be in the C locale for floating point numbers
        !           294:      * to be logged consistently.
        !           295:      */
        !           296:     setlocale(LC_ALL, "C");
        !           297: #endif
        !           298: 
        !           299:     /*
        !           300:      * In the event loop we pass input from user tty to master
        !           301:      * and pass output from master to stdout and IO plugin.
        !           302:      */
        !           303:     fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
        !           304:     fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
        !           305:     for (;;) {
        !           306:        zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
        !           307:        zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
        !           308: 
        !           309:        FD_SET(signal_pipe[0], fdsr);
        !           310:        FD_SET(sv[0], fdsr);
        !           311:        if (!tq_empty(&sigfwd_list))
        !           312:            FD_SET(sv[0], fdsw);
        !           313:        if (log_io)
        !           314:            fd_set_iobs(fdsr, fdsw); /* XXX - better name */
        !           315:        nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
        !           316:        if (nready == -1) {
        !           317:            if (errno == EINTR)
        !           318:                continue;
        !           319:            error(1, _("select failed"));
        !           320:        }
        !           321:        if (FD_ISSET(sv[0], fdsw)) {
        !           322:            forward_signals(sv[0]);
        !           323:        }
        !           324:        if (FD_ISSET(signal_pipe[0], fdsr)) {
        !           325:            n = handle_signals(signal_pipe[0], child, log_io, cstat);
        !           326:            if (n == 0) {
        !           327:                /* Child has exited, cstat is set, we are done. */
        !           328:                goto done;
        !           329:            }
        !           330:            if (n == -1) {
        !           331:                /* Error reading signal_pipe[0], should not happen. */
        !           332:                break;
        !           333:            }
        !           334:            /* Restart event loop so signals get sent to child immediately. */
        !           335:            continue;
        !           336:        }
        !           337:        if (FD_ISSET(sv[0], fdsr)) {
        !           338:            /* read child status */
        !           339:            n = recv(sv[0], cstat, sizeof(*cstat), 0);
        !           340:            if (n == -1) {
        !           341:                if (errno == EINTR)
        !           342:                    continue;
        !           343:                /*
        !           344:                 * If not logging I/O we will receive ECONNRESET when
        !           345:                 * the command is executed.  It is safe to ignore this.
        !           346:                 */
        !           347:                if (log_io && errno != EAGAIN) {
        !           348:                    cstat->type = CMD_ERRNO;
        !           349:                    cstat->val = errno;
        !           350:                    break;
        !           351:                }
        !           352:            }
        !           353:            if (cstat->type == CMD_WSTATUS) {
        !           354:                if (WIFSTOPPED(cstat->val)) {
        !           355:                    /* Suspend parent and tell child how to resume on return. */
        !           356:                    sudo_debug(8, "child stopped, suspending parent");
        !           357:                    n = suspend_parent(WSTOPSIG(cstat->val));
        !           358:                    schedule_signal(n);
        !           359:                    continue;
        !           360:                } else {
        !           361:                    /* Child exited or was killed, either way we are done. */
        !           362:                    break;
        !           363:                }
        !           364:            } else if (cstat->type == CMD_ERRNO) {
        !           365:                /* Child was unable to execute command or broken pipe. */
        !           366:                break;
        !           367:            }
        !           368:        }
        !           369: 
        !           370:        if (perform_io(fdsr, fdsw, cstat) != 0) {
        !           371:            /* I/O error, kill child if still alive and finish. */
        !           372:            schedule_signal(SIGKILL);
        !           373:            forward_signals(sv[0]);
        !           374:            break;
        !           375:        }
        !           376:     }
        !           377: 
        !           378:     if (log_io) {
        !           379:        /* Flush any remaining output and free pty-related memory. */
        !           380:        pty_close(cstat);
        !           381:    }
        !           382: 
        !           383: #ifdef HAVE_SELINUX
        !           384:     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
        !           385:        /* This is probably not needed in log_io mode. */
        !           386:        if (selinux_restore_tty() != 0)
        !           387:            warningx(_("unable to restore tty label"));
        !           388:     }
        !           389: #endif
        !           390: 
        !           391: done:
        !           392:     efree(fdsr);
        !           393:     efree(fdsw);
        !           394:     while (!tq_empty(&sigfwd_list)) {
        !           395:        struct sigforward *sigfwd = tq_first(&sigfwd_list);
        !           396:        tq_remove(&sigfwd_list, sigfwd);
        !           397:        efree(sigfwd);
        !           398:     }
        !           399: 
        !           400:     return cstat->type == CMD_ERRNO ? -1 : 0;
        !           401: }
        !           402: 
        !           403: /*
        !           404:  * Read signals on fd written to by handler().
        !           405:  * Returns -1 on error, 0 on child exit, else 1.
        !           406:  */
        !           407: static int
        !           408: handle_signals(int fd, pid_t child, int log_io, struct command_status *cstat)
        !           409: {
        !           410:     unsigned char signo;
        !           411:     ssize_t nread;
        !           412:     int status;
        !           413:     pid_t pid;
        !           414: 
        !           415:     for (;;) {
        !           416:        /* read signal pipe */
        !           417:        nread = read(signal_pipe[0], &signo, sizeof(signo));
        !           418:        if (nread <= 0) {
        !           419:            /* It should not be possible to get EOF but just in case. */
        !           420:            if (nread == 0)
        !           421:                errno = ECONNRESET;
        !           422:            /* Restart if interrupted by signal so the pipe doesn't fill. */
        !           423:            if (errno == EINTR)
        !           424:                continue;
        !           425:            /* If pipe is empty, we are done. */
        !           426:            if (errno == EAGAIN)
        !           427:                break;
        !           428:            sudo_debug(9, "error reading signal pipe %s", strerror(errno));
        !           429:            cstat->type = CMD_ERRNO;
        !           430:            cstat->val = errno;
        !           431:            return -1;
        !           432:        }
        !           433:        sudo_debug(9, "received signal %d", signo);
        !           434:        if (signo == SIGCHLD) {
        !           435:            /*
        !           436:             * If logging I/O, child is the intermediate process,
        !           437:             * otherwise it is the command itself.
        !           438:             */
        !           439:            do {
        !           440:                pid = waitpid(child, &status, WUNTRACED|WNOHANG);
        !           441:            } while (pid == -1 && errno == EINTR);
        !           442:            if (pid == child) {
        !           443:                /* If not logging I/O and child has exited we are done. */
        !           444:                if (!log_io) {
        !           445:                    if (WIFSTOPPED(status)) {
        !           446:                        /*
        !           447:                         * Save the controlling terminal's process group
        !           448:                         * so we can restore it after we resume.
        !           449:                         */
        !           450:                        pid_t saved_pgrp = (pid_t)-1;
        !           451:                        int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
        !           452:                        if (fd != -1)
        !           453:                            saved_pgrp = tcgetpgrp(fd);
        !           454:                        if (kill(getpid(), WSTOPSIG(status)) != 0) {
        !           455:                            warning("kill(%d, %d)", (int)getpid(),
        !           456:                                WSTOPSIG(status));
        !           457:                        }
        !           458:                        if (fd != -1) {
        !           459:                            if (saved_pgrp != (pid_t)-1)
        !           460:                                (void)tcsetpgrp(fd, saved_pgrp);
        !           461:                            close(fd);
        !           462:                        }
        !           463:                    } else {
        !           464:                        /* Child has exited, we are done. */
        !           465:                        cstat->type = CMD_WSTATUS;
        !           466:                        cstat->val = status;
        !           467:                        return 0;
        !           468:                    }
        !           469:                }
        !           470:                /* Else we get ECONNRESET on sv[0] if child dies. */
        !           471:            }
        !           472:        } else {
        !           473:            if (log_io) {
        !           474:                /* Schedule signo to be forwared to the child. */
        !           475:                schedule_signal(signo);
        !           476:            } else {
        !           477:                /* Nothing listening on sv[0], send directly. */
        !           478:                if (signo == SIGALRM)
        !           479:                    terminate_child(child, FALSE);
        !           480:                else if (kill(child, signo) != 0)
        !           481:                    warning("kill(%d, %d)", (int)child, signo);
        !           482:            }
        !           483:        }
        !           484:     }
        !           485:     return 1;
        !           486: }
        !           487: 
        !           488: /*
        !           489:  * Forward signals in sigfwd_list to child listening on fd.
        !           490:  */
        !           491: static void
        !           492: forward_signals(int sock)
        !           493: {
        !           494:     struct sigforward *sigfwd;
        !           495:     struct command_status cstat;
        !           496:     ssize_t nsent;
        !           497: 
        !           498:     while (!tq_empty(&sigfwd_list)) {
        !           499:        sigfwd = tq_first(&sigfwd_list);
        !           500:        sudo_debug(9, "sending signal %d to child over backchannel",
        !           501:            sigfwd->signo);
        !           502:        cstat.type = CMD_SIGNO;
        !           503:        cstat.val = sigfwd->signo;
        !           504:        do {
        !           505:            nsent = send(sock, &cstat, sizeof(cstat), 0);
        !           506:        } while (nsent == -1 && errno == EINTR);
        !           507:        tq_remove(&sigfwd_list, sigfwd);
        !           508:        efree(sigfwd);
        !           509:        if (nsent != sizeof(cstat)) {
        !           510:            if (errno == EPIPE) {
        !           511:                /* Other end of socket gone, empty out sigfwd_list. */
        !           512:                while (!tq_empty(&sigfwd_list)) {
        !           513:                    sigfwd = tq_first(&sigfwd_list);
        !           514:                    tq_remove(&sigfwd_list, sigfwd);
        !           515:                    efree(sigfwd);
        !           516:                }
        !           517:            }
        !           518:            break;
        !           519:        }
        !           520:     }
        !           521: }
        !           522: 
        !           523: /*
        !           524:  * Schedule a signal to be forwared.
        !           525:  */
        !           526: static void
        !           527: schedule_signal(int signo)
        !           528: {
        !           529:     struct sigforward *sigfwd;
        !           530: 
        !           531:     sigfwd = emalloc(sizeof(*sigfwd));
        !           532:     sigfwd->prev = sigfwd;
        !           533:     sigfwd->next = NULL;
        !           534:     sigfwd->signo = signo;
        !           535:     tq_append(&sigfwd_list, sigfwd);
        !           536: }
        !           537: 
        !           538: /*
        !           539:  * Generic handler for signals passed from parent -> child.
        !           540:  * The other end of signal_pipe is checked in the main event loop.
        !           541:  */
        !           542: void
        !           543: handler(int s)
        !           544: {
        !           545:     unsigned char signo = (unsigned char)s;
        !           546: 
        !           547:     /*
        !           548:      * The pipe is non-blocking, if we overflow the kernel's pipe
        !           549:      * buffer we drop the signal.  This is not a problem in practice.
        !           550:      */
        !           551:     if (write(signal_pipe[1], &signo, sizeof(signo)) == -1)
        !           552:        /* shut up glibc */;
        !           553: }
        !           554: 
        !           555: /*
        !           556:  * Open a pipe and make both ends non-blocking.
        !           557:  * Returns 0 on success and -1 on error.
        !           558:  */
        !           559: int
        !           560: pipe_nonblock(int fds[2])
        !           561: {
        !           562:     int flags, rval;
        !           563: 
        !           564:     rval = pipe(fds);
        !           565:     if (rval != -1) {
        !           566:        flags = fcntl(fds[0], F_GETFL, 0);
        !           567:        if (flags != -1 && !ISSET(flags, O_NONBLOCK))
        !           568:            rval = fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
        !           569:        if (rval != -1) {
        !           570:            flags = fcntl(fds[1], F_GETFL, 0);
        !           571:            if (flags != -1 && !ISSET(flags, O_NONBLOCK))
        !           572:                rval = fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
        !           573:        }
        !           574:        if (rval == -1) {
        !           575:            close(fds[0]);
        !           576:            close(fds[1]);
        !           577:        }
        !           578:     }
        !           579: 
        !           580:     return rval;
        !           581: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>