diff options
-rw-r--r-- | libiberty/ChangeLog | 4 | ||||
-rw-r--r-- | libiberty/pex-common.h | 5 | ||||
-rw-r--r-- | libiberty/pex-unix.c | 81 |
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; |