| /* 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, 2004 |
| Free Software Foundation, Inc. |
| |
| This file is part of the libiberty library. |
| Libiberty is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| Libiberty is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public |
| License along with libiberty; see the file COPYING.LIB. If not, |
| write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "pex-common.h" |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #ifdef NEED_DECLARATION_ERRNO |
| extern int errno; |
| #endif |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #endif |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #ifdef HAVE_SYS_WAIT_H |
| #include <sys/wait.h> |
| #endif |
| |
| #ifndef HAVE_WAITPID |
| #define waitpid(pid, status, flags) wait(status) |
| #endif |
| |
| #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, |
| flagsarg) |
| const char *program; |
| char * const *argv; |
| const char *this_pname; |
| const char *temp_base ATTRIBUTE_UNUSED; |
| char **errmsg_fmt, **errmsg_arg; |
| int flagsarg; |
| { |
| int pid; |
| int pdes[2]; |
| int out; |
| int input_desc, output_desc; |
| 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; |
| |
| input_desc = last_pipe_input; |
| |
| /* If this isn't the last process, make a pipe for its output, |
| and record it as waiting to be the input to the next process. */ |
| if (! (flags & PEXECUTE_LAST)) |
| { |
| if (pipe (pdes) < 0) |
| { |
| *errmsg_fmt = "pipe"; |
| *errmsg_arg = NULL; |
| return -1; |
| } |
| out = pdes[WRITE_PORT]; |
| last_pipe_input = pdes[READ_PORT]; |
| } |
| else |
| { |
| /* Last process. */ |
| 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 = vfork (); |
| if (pid >= 0) |
| break; |
| sleep (sleep_interval); |
| sleep_interval *= 2; |
| } |
| |
| switch (pid) |
| { |
| case -1: |
| *errmsg_fmt = "fork"; |
| *errmsg_arg = NULL; |
| return -1; |
| |
| case 0: /* child */ |
| /* Move the input and output pipes into place, if necessary. */ |
| if (input_desc != STDIN_FILE_NO) |
| { |
| close (STDIN_FILE_NO); |
| dup (input_desc); |
| close (input_desc); |
| } |
| if (output_desc != STDOUT_FILE_NO) |
| { |
| close (STDOUT_FILE_NO); |
| dup (output_desc); |
| close (output_desc); |
| } |
| |
| /* Close the parent's descs that aren't wanted here. */ |
| if (last_pipe_input != STDIN_FILE_NO) |
| close (last_pipe_input); |
| |
| /* Exec the program. */ |
| 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; |
| |
| default: |
| /* In the parent, after forking. |
| Close the descriptors that we made for this child. */ |
| if (input_desc != STDIN_FILE_NO) |
| close (input_desc); |
| if (output_desc != STDOUT_FILE_NO) |
| close (output_desc); |
| |
| /* Return child's process number. */ |
| return pid; |
| } |
| } |
| |
| int |
| pwait (pid, status, flags) |
| int pid; |
| int *status; |
| int flags ATTRIBUTE_UNUSED; |
| { |
| /* ??? Here's an opportunity to canonicalize the values in STATUS. |
| Needed? */ |
| pid = waitpid (pid, status, 0); |
| return pid; |
| } |