aboutsummaryrefslogtreecommitdiff
path: root/libiberty/pex-unix.c
diff options
context:
space:
mode:
authorRichard Henderson <rth@redhat.com>2010-11-04 23:41:21 +0000
committerRichard Henderson <rth@redhat.com>2010-11-04 23:41:21 +0000
commit9c55e0647658a618fb8fbaf8feb5c5737e28bf20 (patch)
treeb9817c1e178eed6cb0f9f800eb29852102ea59bd /libiberty/pex-unix.c
parentf445451a1f3478f43f5a08cf2487ee66cec3bcf3 (diff)
downloadgdb-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.c201
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. */