aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libiberty/ChangeLog4
-rw-r--r--libiberty/pex-common.h5
-rw-r--r--libiberty/pex-unix.c81
3 files changed, 73 insertions, 17 deletions
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog
index 95cc222..2dce4d8 100644
--- a/libiberty/ChangeLog
+++ b/libiberty/ChangeLog
@@ -1,5 +1,9 @@
2004-03-17 Ian Lance Taylor <ian@wasabisystems.com>
+ * pex-unix.c (pexecute): Use vfork instead of fork, with
+ appropriate changes to make this safe.
+ * pex-common.h (STDERR_FILE_NO): Define.
+
* Makefile.in: Clean up REQUIRED_OFILES and CONFIGURED_OFILES for
an 80 column screen. Run maint-deps.
diff --git a/libiberty/pex-common.h b/libiberty/pex-common.h
index da2f71e..df3c0f6 100644
--- a/libiberty/pex-common.h
+++ b/libiberty/pex-common.h
@@ -1,6 +1,6 @@
/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Shared logic.
- 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.
@@ -33,6 +33,9 @@ Boston, MA 02111-1307, USA. */
/* stdout file number. */
#define STDOUT_FILE_NO 1
+/* stderr file number. */
+#define STDERR_FILE_NO 2
+
/* value of `pipe': port index for reading. */
#define READ_PORT 0
diff --git a/libiberty/pex-unix.c b/libiberty/pex-unix.c
index 14fe71e..ddbed8f 100644
--- a/libiberty/pex-unix.c
+++ b/libiberty/pex-unix.c
@@ -1,7 +1,7 @@
/* 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.
@@ -44,28 +44,66 @@ extern int errno;
#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;
@@ -82,22 +120,24 @@ pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
*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);
@@ -131,12 +171,21 @@ pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
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;