diff options
author | Richard Henderson <rth@redhat.com> | 2010-11-04 23:41:21 +0000 |
---|---|---|
committer | Richard Henderson <rth@redhat.com> | 2010-11-04 23:41:21 +0000 |
commit | 9c55e0647658a618fb8fbaf8feb5c5737e28bf20 (patch) | |
tree | b9817c1e178eed6cb0f9f800eb29852102ea59bd /libiberty/pex-unix.c | |
parent | f445451a1f3478f43f5a08cf2487ee66cec3bcf3 (diff) | |
download | gdb-9c55e0647658a618fb8fbaf8feb5c5737e28bf20.zip gdb-9c55e0647658a618fb8fbaf8feb5c5737e28bf20.tar.gz gdb-9c55e0647658a618fb8fbaf8feb5c5737e28bf20.tar.bz2 |
Use spawnve on cygwin.
* configure.ac (AC_CHECK_HEADERS): Add process.h.
(checkfuncs): Add dup3, spawnve, spawnvpe; sort the list.
(AC_CHECK_FUNCS): Add dup3, spawnve, spawnvpe.
* configure, config.in: Rebuild.
* pex-unix.c [HAVE_SPAWNVE] (pex_unix_exec_child): New function.
[HAVE_SPAWNVE] (save_and_install_fd, restore_fd): New functions.
Diffstat (limited to 'libiberty/pex-unix.c')
-rw-r--r-- | libiberty/pex-unix.c | 201 |
1 files changed, 200 insertions, 1 deletions
diff --git a/libiberty/pex-unix.c b/libiberty/pex-unix.c index 85733a6..c4a69ad 100644 --- a/libiberty/pex-unix.c +++ b/libiberty/pex-unix.c @@ -55,7 +55,9 @@ extern int errno; #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif - +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif #ifdef vfork /* Autoconf may define this to fork for us. */ # define VFORK_STRING "fork" @@ -387,6 +389,202 @@ pex_child_error (struct pex_obj *obj, const char *executable, extern char **environ; +#if defined(HAVE_SPAWNVE) && defined(HAVE_SPAWNVPE) +/* Implementation of pex->exec_child using the Cygwin spawn operation. */ + +/* Subroutine of pex_unix_exec_child. Move OLD_FD to a new file descriptor + to be stored in *PNEW_FD, save the flags in *PFLAGS, and arrange for the + saved copy to be close-on-exec. Move CHILD_FD into OLD_FD. If CHILD_FD + is -1, OLD_FD is to be closed. Return -1 on error. */ + +static int +save_and_install_fd(int *pnew_fd, int *pflags, int old_fd, int child_fd) +{ + int new_fd, flags; + + flags = fcntl (old_fd, F_GETFD); + + /* If we could not retrieve the flags, then OLD_FD was not open. */ + if (flags < 0) + { + new_fd = -1, flags = 0; + if (child_fd >= 0 && dup2 (child_fd, old_fd) < 0) + return -1; + } + /* If we wish to close OLD_FD, just mark it CLOEXEC. */ + else if (child_fd == -1) + { + new_fd = old_fd; + if ((flags & FD_CLOEXEC) == 0 && fcntl (old_fd, F_SETFD, FD_CLOEXEC) < 0) + return -1; + } + /* Otherwise we need to save a copy of OLD_FD before installing CHILD_FD. */ + else + { +#ifdef F_DUPFD_CLOEXEC + new_fd = fcntl (old_fd, F_DUPFD_CLOEXEC, 3); + if (new_fd < 0) + return -1; +#else + /* Prefer F_DUPFD over dup in order to avoid getting a new fd + in the range 0-2, right where a new stderr fd might get put. */ + new_fd = fcntl (old_fd, F_DUPFD, 3); + if (new_fd < 0) + return -1; + if (fcntl (new_fd, F_SETFD, FD_CLOEXEC) < 0) + return -1; +#endif + if (dup2 (child_fd, old_fd) < 0) + return -1; + } + + *pflags = flags; + if (pnew_fd) + *pnew_fd = new_fd; + else if (new_fd != old_fd) + abort (); + + return 0; +} + +/* Subroutine of pex_unix_exec_child. Move SAVE_FD back to OLD_FD + restoring FLAGS. If SAVE_FD < 0, OLD_FD is to be closed. */ + +static int +restore_fd(int old_fd, int save_fd, int flags) +{ + /* For SAVE_FD < 0, all we have to do is restore the + "closed-ness" of the original. */ + if (save_fd < 0) + return close (old_fd); + + /* For SAVE_FD == OLD_FD, all we have to do is restore the + original setting of the CLOEXEC flag. */ + if (save_fd == old_fd) + { + if (flags & FD_CLOEXEC) + return 0; + return fcntl (old_fd, F_SETFD, flags); + } + + /* Otherwise we have to move the descriptor back, restore the flags, + and close the saved copy. */ +#ifdef HAVE_DUP3 + if (flags == FD_CLOEXEC) + { + if (dup3 (save_fd, old_fd, O_CLOEXEC) < 0) + return -1; + } + else +#endif + { + if (dup2 (save_fd, old_fd) < 0) + return -1; + if (flags != 0 && fcntl (old_fd, F_SETFD, flags) < 0) + return -1; + } + return close (save_fd); +} + +static pid_t +pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, + int flags, const char *executable, + char * const * argv, char * const * env, + int in, int out, int errdes, int toclose, + const char **errmsg, int *err) +{ + int fl_in = 0, fl_out = 0, fl_err = 0, fl_tc = 0; + int save_in = -1, save_out = -1, save_err = -1; + int max, retries; + pid_t pid; + + if (flags & PEX_STDERR_TO_STDOUT) + errdes = out; + + /* We need the three standard file descriptors to be set up as for + the child before we perform the spawn. The file descriptors for + the parent need to be moved and marked for close-on-exec. */ + if (in != STDIN_FILE_NO + && save_and_install_fd (&save_in, &fl_in, STDIN_FILE_NO, in) < 0) + goto error_dup2; + if (out != STDOUT_FILE_NO + && save_and_install_fd (&save_out, &fl_out, STDOUT_FILE_NO, out) < 0) + goto error_dup2; + if (errdes != STDERR_FILE_NO + && save_and_install_fd (&save_err, &fl_err, STDERR_FILE_NO, errdes) < 0) + goto error_dup2; + if (toclose >= 0 + && save_and_install_fd (NULL, &fl_tc, toclose, -1) < 0) + goto error_dup2; + + /* Now that we've moved the file descriptors for the child into place, + close the originals. Be careful not to close any of the standard + file descriptors that we just set up. */ + max = -1; + if (errdes >= 0) + max = STDERR_FILE_NO; + else if (out >= 0) + max = STDOUT_FILE_NO; + else if (in >= 0) + max = STDIN_FILE_NO; + if (in > max) + close (in); + if (out > max) + close (out); + if (errdes > max && errdes != out) + close (errdes); + + /* If we were not given an environment, use the global environment. */ + if (env == NULL) + env = environ; + + /* Launch the program. If we get EAGAIN (normally out of pid's), try + again a few times with increasing backoff times. */ + retries = 0; + while (1) + { + typedef const char * const *cc_cp; + + if (flags & PEX_SEARCH) + pid = spawnvpe (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env); + else + pid = spawnve (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env); + + if (pid > 0) + break; + + *err = errno; + *errmsg = "spawn"; + if (errno != EAGAIN || ++retries == 4) + return (pid_t) -1; + sleep (1 << retries); + } + + /* Success. Restore the parent's file descriptors that we saved above. */ + if (toclose >= 0 + && restore_fd (toclose, toclose, fl_tc) < 0) + goto error_dup2; + if (in != STDIN_FILE_NO + && restore_fd (STDIN_FILE_NO, save_in, fl_in) < 0) + goto error_dup2; + if (out != STDOUT_FILE_NO + && restore_fd (STDOUT_FILE_NO, save_out, fl_out) < 0) + goto error_dup2; + if (errdes != STDERR_FILE_NO + && restore_fd (STDERR_FILE_NO, save_err, fl_err) < 0) + goto error_dup2; + + return pid; + + error_dup2: + *err = errno; + *errmsg = "dup2"; + return (pid_t) -1; +} + +#else +/* Implementation of pex->exec_child using standard vfork + exec. */ + static pid_t pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable, char * const * argv, char * const * env, @@ -521,6 +719,7 @@ pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable, return pid; } } +#endif /* SPAWN */ /* Wait for a child process to complete. */ |