예제 #1
0
/*
 * This function is code executed in the child process immediately after fork
 * to set things up and call exec().
 *
 * All of the code in this function must only use async-signal-safe functions,
 * listed at `man 7 signal` or
 * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html.
 *
 * This restriction is documented at
 * http://www.opengroup.org/onlinepubs/009695399/functions/fork.html.
 */
static void
child_exec(char *const exec_array[],
           char *const argv[],
           char *const envp[],
           const char *cwd,
           int p2cread, int p2cwrite,
           int c2pread, int c2pwrite,
           int errread, int errwrite,
           int errpipe_read, int errpipe_write,
           int close_fds, int restore_signals,
           int call_setsid,
           PyObject *py_fds_to_keep,
           PyObject *preexec_fn,
           PyObject *preexec_fn_args_tuple)
{
    int i, saved_errno, unused, reached_preexec = 0;
    PyObject *result;
    const char* err_msg = "";
    /* Buffer large enough to hold a hex integer.  We can't malloc. */
    char hex_errno[sizeof(saved_errno)*2+1];

    /* Close parent's pipe ends. */
    if (p2cwrite != -1) {
        POSIX_CALL(close(p2cwrite));
    }
    if (c2pread != -1) {
        POSIX_CALL(close(c2pread));
    }
    if (errread != -1) {
        POSIX_CALL(close(errread));
    }
    POSIX_CALL(close(errpipe_read));

    /* When duping fds, if there arises a situation where one of the fds is
       either 0, 1 or 2, it is possible that it is overwritten (#12607). */
    if (c2pwrite == 0)
        POSIX_CALL(c2pwrite = dup(c2pwrite));
    if (errwrite == 0 || errwrite == 1)
        POSIX_CALL(errwrite = dup(errwrite));

    /* Dup fds for child.
       dup2() removes the CLOEXEC flag but we must do it ourselves if dup2()
       would be a no-op (issue #10806). */
    if (p2cread == 0) {
        int old = fcntl(p2cread, F_GETFD);
        if (old != -1)
            fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC);
    } else if (p2cread != -1) {
        POSIX_CALL(dup2(p2cread, 0));  /* stdin */
    }
    if (c2pwrite == 1) {
        int old = fcntl(c2pwrite, F_GETFD);
        if (old != -1)
            fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC);
    } else if (c2pwrite != -1) {
        POSIX_CALL(dup2(c2pwrite, 1));  /* stdout */
    }
    if (errwrite == 2) {
        int old = fcntl(errwrite, F_GETFD);
        if (old != -1)
            fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC);
    } else if (errwrite != -1) {
        POSIX_CALL(dup2(errwrite, 2));  /* stderr */
    }

    /* Close pipe fds.  Make sure we don't close the same fd more than */
    /* once, or standard fds. */
    if (p2cread > 2) {
        POSIX_CALL(close(p2cread));
    }
    if (c2pwrite > 2 && c2pwrite != p2cread) {
        POSIX_CALL(close(c2pwrite));
    }
    if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) {
        POSIX_CALL(close(errwrite));
    }

    if (close_fds) {
        int local_max_fd = max_fd;
#if defined(__NetBSD__)
        local_max_fd = fcntl(0, F_MAXFD);
        if (local_max_fd < 0)
            local_max_fd = max_fd;
#endif
        /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
        _close_open_fd_range(3, local_max_fd, py_fds_to_keep);
    }

    if (cwd)
        POSIX_CALL(chdir(cwd));

    if (restore_signals)
        _Py_RestoreSignals();

#ifdef HAVE_SETSID
    if (call_setsid)
        POSIX_CALL(setsid());
#endif

    reached_preexec = 1;
    if (preexec_fn != Py_None && preexec_fn_args_tuple) {
        /* This is where the user has asked us to deadlock their program. */
        result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);
        if (result == NULL) {
            /* Stringifying the exception or traceback would involve
             * memory allocation and thus potential for deadlock.
             * We've already faced potential deadlock by calling back
             * into Python in the first place, so it probably doesn't
             * matter but we avoid it to minimize the possibility. */
            err_msg = "Exception occurred in preexec_fn.";
            errno = 0;  /* We don't want to report an OSError. */
            goto error;
        }
        /* Py_DECREF(result); - We're about to exec so why bother? */
    }

    /* This loop matches the Lib/os.py _execvpe()'s PATH search when */
    /* given the executable_list generated by Lib/subprocess.py.     */
    saved_errno = 0;
    for (i = 0; exec_array[i] != NULL; ++i) {
        const char *executable = exec_array[i];
        if (envp) {
            execve(executable, argv, envp);
        } else {
            execv(executable, argv);
        }
        if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) {
            saved_errno = errno;
        }
    }
    /* Report the first exec error, not the last. */
    if (saved_errno)
        errno = saved_errno;

error:
    saved_errno = errno;
    /* Report the posix error to our parent process. */
    /* We ignore all write() return values as the total size of our writes is
     * less than PIPEBUF and we cannot do anything about an error anyways. */
    if (saved_errno) {
        char *cur;
        unused = write(errpipe_write, "OSError:", 8);
        cur = hex_errno + sizeof(hex_errno);
        while (saved_errno != 0 && cur > hex_errno) {
            *--cur = "0123456789ABCDEF"[saved_errno % 16];
            saved_errno /= 16;
        }
        unused = write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur);
        unused = write(errpipe_write, ":", 1);
        if (!reached_preexec) {
            /* Indicate to the parent that the error happened before exec(). */
            unused = write(errpipe_write, "noexec", 6);
        }
        /* We can't call strerror(saved_errno).  It is not async signal safe.
         * The parent process will look the error message up. */
    } else {
        unused = write(errpipe_write, "RuntimeError:0:", 15);
        unused = write(errpipe_write, err_msg, strlen(err_msg));
    }
    if (unused) return;  /* silly? yes! avoids gcc compiler warning. */
}
예제 #2
0
/*
 * This function is code executed in the child process immediately after fork
 * to set things up and call exec().
 *
 * All of the code in this function must only use async-signal-safe functions,
 * listed at `man 7 signal` or
 * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html.
 *
 * This restriction is documented at
 * http://www.opengroup.org/onlinepubs/009695399/functions/fork.html.
 */
static void child_exec(char *const exec_array[],
                       char *const argv[],
                       char *const envp[],
                       const char *cwd,
                       int p2cread, int p2cwrite,
                       int c2pread, int c2pwrite,
                       int errread, int errwrite,
                       int errpipe_read, int errpipe_write,
                       int close_fds, int restore_signals,
                       int call_setsid, Py_ssize_t num_fds_to_keep,
                       PyObject *py_fds_to_keep,
                       PyObject *preexec_fn,
                       PyObject *preexec_fn_args_tuple)
{
    int i, saved_errno, fd_num;
    PyObject *result;
    const char* err_msg = "";
    /* Buffer large enough to hold a hex integer.  We can't malloc. */
    char hex_errno[sizeof(saved_errno)*2+1];

    /* Close parent's pipe ends. */
    if (p2cwrite != -1) {
        POSIX_CALL(close(p2cwrite));
    }
    if (c2pread != -1) {
        POSIX_CALL(close(c2pread));
    }
    if (errread != -1) {
        POSIX_CALL(close(errread));
    }
    POSIX_CALL(close(errpipe_read));

    /* Dup fds for child.
       dup2() removes the CLOEXEC flag but we must do it ourselves if dup2()
       would be a no-op (issue #10806). */
    if (p2cread == 0) {
        int old = fcntl(p2cread, F_GETFD);
        if (old != -1)
            fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC);
    } else if (p2cread != -1) {
        POSIX_CALL(dup2(p2cread, 0));  /* stdin */
    }
    if (c2pwrite == 1) {
        int old = fcntl(c2pwrite, F_GETFD);
        if (old != -1)
            fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC);
    } else if (c2pwrite != -1) {
        POSIX_CALL(dup2(c2pwrite, 1));  /* stdout */
    }
    if (errwrite == 2) {
        int old = fcntl(errwrite, F_GETFD);
        if (old != -1)
            fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC);
    } else if (errwrite != -1) {
        POSIX_CALL(dup2(errwrite, 2));  /* stderr */
    }

    /* Close pipe fds.  Make sure we don't close the same fd more than */
    /* once, or standard fds. */
    if (p2cread > 2) {
        POSIX_CALL(close(p2cread));
    }
    if (c2pwrite > 2) {
        POSIX_CALL(close(c2pwrite));
    }
    if (errwrite != c2pwrite && errwrite > 2) {
        POSIX_CALL(close(errwrite));
    }

    /* close() is intentionally not checked for errors here as we are closing */
    /* a large range of fds, some of which may be invalid. */
    if (close_fds) {
        Py_ssize_t keep_seq_idx;
        int start_fd = 3;
        for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) {
            PyObject* py_keep_fd = PySequence_Fast_GET_ITEM(py_fds_to_keep,
                                                            keep_seq_idx);
            int keep_fd = PyLong_AsLong(py_keep_fd);
            if (keep_fd < 0) {  /* Negative number, overflow or not a Long. */
                err_msg = "bad value in fds_to_keep.";
                errno = 0;  /* We don't want to report an OSError. */
                goto error;
            }
            if (keep_fd < start_fd)
                continue;
            for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) {
                close(fd_num);
            }
            start_fd = keep_fd + 1;
        }
        if (start_fd <= max_fd) {
            for (fd_num = start_fd; fd_num < max_fd; ++fd_num) {
                close(fd_num);
            }
        }
    }

    if (cwd)
        POSIX_CALL(chdir(cwd));

    if (restore_signals)
        _Py_RestoreSignals();

#ifdef HAVE_SETSID
    if (call_setsid)
        POSIX_CALL(setsid());
#endif

    if (preexec_fn != Py_None && preexec_fn_args_tuple) {
        /* This is where the user has asked us to deadlock their program. */
        result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);
        if (result == NULL) {
            /* Stringifying the exception or traceback would involve
             * memory allocation and thus potential for deadlock.
             * We've already faced potential deadlock by calling back
             * into Python in the first place, so it probably doesn't
             * matter but we avoid it to minimize the possibility. */
            err_msg = "Exception occurred in preexec_fn.";
            errno = 0;  /* We don't want to report an OSError. */
            goto error;
        }
        /* Py_DECREF(result); - We're about to exec so why bother? */
    }

    /* This loop matches the Lib/os.py _execvpe()'s PATH search when */
    /* given the executable_list generated by Lib/subprocess.py.     */
    saved_errno = 0;
    for (i = 0; exec_array[i] != NULL; ++i) {
        const char *executable = exec_array[i];
        if (envp) {
            execve(executable, argv, envp);
        } else {
            execv(executable, argv);
        }
        if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) {
            saved_errno = errno;
        }
    }
    /* Report the first exec error, not the last. */
    if (saved_errno)
        errno = saved_errno;

error:
    saved_errno = errno;
    /* Report the posix error to our parent process. */
    if (saved_errno) {
        char *cur;
        write(errpipe_write, "OSError:", 8);
        cur = hex_errno + sizeof(hex_errno);
        while (saved_errno != 0 && cur > hex_errno) {
            *--cur = "0123456789ABCDEF"[saved_errno % 16];
            saved_errno /= 16;
        }
        write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur);
        write(errpipe_write, ":", 1);
        /* We can't call strerror(saved_errno).  It is not async signal safe.
         * The parent process will look the error message up. */
    } else {
        write(errpipe_write, "RuntimeError:0:", 15);
        write(errpipe_write, err_msg, strlen(err_msg));
    }
}