/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Generic Unix version
(also used for UWIN and VMS).
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
Free Software Foundation, Inc.
This file is part of the libiberty library.
#define waitpid(pid, status, flags) wait(status)
#endif
-extern int execv ();
-extern int execvp ();
+#ifdef vfork /* Autoconf may define this to fork for us. */
+# define VFORK_STRING "fork"
+#else
+# define VFORK_STRING "vfork"
+#endif
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+#ifdef VMS
+#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
+ lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
+#endif /* VMS */
+
+/* Execute a program, possibly setting up pipes to programs executed
+ via other calls to this function.
+
+ This version of the function uses vfork. In general vfork is
+ similar to setjmp/longmp, in that any variable which is modified by
+ the child process has an indeterminate value in the parent process.
+ We follow a safe approach here by not modifying any variables at
+ all in the child process (with the possible exception of variables
+ modified by xstrerror if exec fails, but this is unlikely to be
+ detectable).
+
+ We work a little bit harder to avoid gcc warnings. gcc will warn
+ about any automatic variable which is live at the time of the
+ vfork, which is non-volatile, and which is either set more than
+ once or is an argument to the function. This warning isn't quite
+ right, since what we really care about is whether the variable is
+ live at the time of the vfork and set afterward by the child
+ process, but gcc only checks whether the variable is set more than
+ once. To avoid this warning, we ensure that any variable which is
+ live at the time of the vfork (i.e., used after the vfork) is set
+ exactly once and is not an argument, or is marked volatile. */
int
-pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg,
+ flagsarg)
const char *program;
char * const *argv;
const char *this_pname;
const char *temp_base ATTRIBUTE_UNUSED;
char **errmsg_fmt, **errmsg_arg;
- int flags;
+ int flagsarg;
{
- int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv);
int pid;
int pdes[2];
+ int out;
int input_desc, output_desc;
- int retries, sleep_interval;
+ int flags;
+ /* We declare these to be volatile to avoid warnings from gcc about
+ them being clobbered by vfork. */
+ volatile int retries, sleep_interval;
/* Pipe waiting from last process, to be used as input for the next one.
Value is STDIN_FILE_NO if no pipe is waiting
(i.e. the next command is the first of a group). */
static int last_pipe_input;
+ flags = flagsarg;
+
/* If this is the first process, initialize. */
if (flags & PEXECUTE_FIRST)
last_pipe_input = STDIN_FILE_NO;
*errmsg_arg = NULL;
return -1;
}
- output_desc = pdes[WRITE_PORT];
+ out = pdes[WRITE_PORT];
last_pipe_input = pdes[READ_PORT];
}
else
{
/* Last process. */
- output_desc = STDOUT_FILE_NO;
+ out = STDOUT_FILE_NO;
last_pipe_input = STDIN_FILE_NO;
}
+ output_desc = out;
+
/* Fork a subprocess; wait and retry if it fails. */
sleep_interval = 1;
pid = -1;
for (retries = 0; retries < 4; retries++)
{
- pid = fork ();
+ pid = vfork ();
if (pid >= 0)
break;
sleep (sleep_interval);
close (last_pipe_input);
/* Exec the program. */
- (*func) (program, argv);
-
- fprintf (stderr, "%s: ", this_pname);
- fprintf (stderr, install_error_msg, program);
- fprintf (stderr, ": %s\n", xstrerror (errno));
- exit (-1);
+ if (flags & PEXECUTE_SEARCH)
+ execvp (program, argv);
+ else
+ execv (program, argv);
+
+ /* We don't want to call fprintf after vfork. */
+#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s))
+ writeerr (this_pname);
+ writeerr (": ");
+ writeerr ("installation problem, cannot exec '");
+ writeerr (program);
+ writeerr ("': ");
+ writeerr (xstrerror (errno));
+ writeerr ("\n");
+ _exit (-1);
/* NOTREACHED */
return 0;