+
+ 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,
+ int in, int out, int errdes,
+ int toclose, const char **errmsg, int *err)
+{
+ pid_t pid = -1;
+ /* Tuple to communicate error from child to parent. We can safely
+ transfer string literal pointers as both run with identical
+ address mappings. */
+ struct fn_err
+ {
+ const char *fn;
+ int err;
+ };
+ volatile int do_pipe = 0;
+ volatile int pipes[2]; /* [0]:reader,[1]:writer. */
+#ifdef O_CLOEXEC
+ do_pipe = 1;
+#endif
+ if (do_pipe)
+ {
+#ifdef HAVE_PIPE2
+ if (pipe2 ((int *)pipes, O_CLOEXEC))
+ do_pipe = 0;
+#else
+ if (pipe ((int *)pipes))
+ do_pipe = 0;
+ else
+ {
+ if (fcntl (pipes[1], F_SETFD, FD_CLOEXEC) == -1)
+ {
+ close (pipes[0]);
+ close (pipes[1]);
+ do_pipe = 0;
+ }
+ }
+#endif
+ }
+