X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libiberty%2Fpex-unix.c;h=85733a669232975b4e333a27044da0eb64add0f8;hb=0b5acd59dbd35b02c882dac40a4d47f227459235;hp=55a055e4b7e80a68e9a8265c858d06e5e95eafcf;hpb=8682c630271dac585aca4fde6eaa911959454ede;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libiberty/pex-unix.c b/libiberty/pex-unix.c index 55a055e4b7e..85733a66923 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, 2004 + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2009 Free Software Foundation, Inc. This file is part of the libiberty library. @@ -17,33 +17,46 @@ 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. */ +write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +Boston, MA 02110-1301, USA. */ +#include "config.h" +#include "libiberty.h" #include "pex-common.h" #include +#include #include #ifdef NEED_DECLARATION_ERRNO extern int errno; #endif +#ifdef HAVE_STDLIB_H +#include +#endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif -#ifdef HAVE_STDLIB_H -#include + +#include + +#ifdef HAVE_FCNTL_H +#include #endif #ifdef HAVE_SYS_WAIT_H #include #endif - -#ifndef HAVE_WAITPID -#define waitpid(pid, status, flags) wait(status) +#ifdef HAVE_GETRUSAGE +#include +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include #endif + #ifdef vfork /* Autoconf may define this to fork for us. */ # define VFORK_STRING "fork" #else @@ -52,90 +65,350 @@ extern int errno; #ifdef HAVE_VFORK_H #include #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/longjmp, 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; +#if defined(VMS) && defined (__LONG_POINTERS) +#ifndef __CHAR_PTR32 +typedef char * __char_ptr32 +__attribute__ ((mode (SI))); +#endif + +typedef __char_ptr32 *__char_ptr_char_ptr32 +__attribute__ ((mode (SI))); + +/* Return a 32 bit pointer to an array of 32 bit pointers + given a 64 bit pointer to an array of 64 bit pointers. */ + +static __char_ptr_char_ptr32 +to_ptr32 (char **ptr64) +{ + int argc; + __char_ptr_char_ptr32 short_argv; + + for (argc=0; ptr64[argc]; argc++); + + /* Reallocate argv with 32 bit pointers. */ + short_argv = (__char_ptr_char_ptr32) decc$malloc + (sizeof (__char_ptr32) * (argc + 1)); + + for (argc=0; ptr64[argc]; argc++) + short_argv[argc] = (__char_ptr32) decc$strdup (ptr64[argc]); + + short_argv[argc] = (__char_ptr32) 0; + return short_argv; - flags = flagsarg; +} +#else +#define to_ptr32(argv) argv +#endif - /* If this is the first process, initialize. */ - if (flags & PEXECUTE_FIRST) - last_pipe_input = STDIN_FILE_NO; +/* File mode to use for private and world-readable files. */ + +#if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH) +#define PUBLIC_MODE \ + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#else +#define PUBLIC_MODE 0666 +#endif - input_desc = last_pipe_input; +/* Get the exit status of a particular process, and optionally get the + time that it took. This is simple if we have wait4, slightly + harder if we have waitpid, and is a pain if we only have wait. */ - /* 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)) +static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *); + +#ifdef HAVE_WAIT4 + +static pid_t +pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status, + struct pex_time *time) +{ + pid_t ret; + struct rusage r; + +#ifdef HAVE_WAITPID + if (time == NULL) + return waitpid (pid, status, 0); +#endif + + ret = wait4 (pid, status, 0, &r); + + if (time != NULL) { - if (pipe (pdes) < 0) + time->user_seconds = r.ru_utime.tv_sec; + time->user_microseconds= r.ru_utime.tv_usec; + time->system_seconds = r.ru_stime.tv_sec; + time->system_microseconds= r.ru_stime.tv_usec; + } + + return ret; +} + +#else /* ! defined (HAVE_WAIT4) */ + +#ifdef HAVE_WAITPID + +#ifndef HAVE_GETRUSAGE + +static pid_t +pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status, + struct pex_time *time) +{ + if (time != NULL) + memset (time, 0, sizeof (struct pex_time)); + return waitpid (pid, status, 0); +} + +#else /* defined (HAVE_GETRUSAGE) */ + +static pid_t +pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status, + struct pex_time *time) +{ + struct rusage r1, r2; + pid_t ret; + + if (time == NULL) + return waitpid (pid, status, 0); + + getrusage (RUSAGE_CHILDREN, &r1); + + ret = waitpid (pid, status, 0); + if (ret < 0) + return ret; + + getrusage (RUSAGE_CHILDREN, &r2); + + time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec; + time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec; + if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec) + { + --time->user_seconds; + time->user_microseconds += 1000000; + } + + time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec; + time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec; + if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec) + { + --time->system_seconds; + time->system_microseconds += 1000000; + } + + return ret; +} + +#endif /* defined (HAVE_GETRUSAGE) */ + +#else /* ! defined (HAVE_WAITPID) */ + +struct status_list +{ + struct status_list *next; + pid_t pid; + int status; + struct pex_time time; +}; + +static pid_t +pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time) +{ + struct status_list **pp; + + for (pp = (struct status_list **) &obj->sysdep; + *pp != NULL; + pp = &(*pp)->next) + { + if ((*pp)->pid == pid) { - *errmsg_fmt = "pipe"; - *errmsg_arg = NULL; - return -1; + struct status_list *p; + + p = *pp; + *status = p->status; + if (time != NULL) + *time = p->time; + *pp = p->next; + free (p); + return pid; } - out = pdes[WRITE_PORT]; - last_pipe_input = pdes[READ_PORT]; } - else + + while (1) { - /* Last process. */ - out = STDOUT_FILE_NO; - last_pipe_input = STDIN_FILE_NO; + pid_t cpid; + struct status_list *psl; + struct pex_time pt; +#ifdef HAVE_GETRUSAGE + struct rusage r1, r2; +#endif + + if (time != NULL) + { +#ifdef HAVE_GETRUSAGE + getrusage (RUSAGE_CHILDREN, &r1); +#else + memset (&pt, 0, sizeof (struct pex_time)); +#endif + } + + cpid = wait (status); + +#ifdef HAVE_GETRUSAGE + if (time != NULL && cpid >= 0) + { + getrusage (RUSAGE_CHILDREN, &r2); + + pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec; + pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec; + if (pt.user_microseconds < 0) + { + --pt.user_seconds; + pt.user_microseconds += 1000000; + } + + pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec; + pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec; + if (pt.system_microseconds < 0) + { + --pt.system_seconds; + pt.system_microseconds += 1000000; + } + } +#endif + + if (cpid < 0 || cpid == pid) + { + if (time != NULL) + *time = pt; + return cpid; + } + + psl = XNEW (struct status_list); + psl->pid = cpid; + psl->status = *status; + if (time != NULL) + psl->time = pt; + psl->next = (struct status_list *) obj->sysdep; + obj->sysdep = (void *) psl; } +} + +#endif /* ! defined (HAVE_WAITPID) */ +#endif /* ! defined (HAVE_WAIT4) */ + +static void pex_child_error (struct pex_obj *, const char *, const char *, int) + ATTRIBUTE_NORETURN; +static int pex_unix_open_read (struct pex_obj *, const char *, int); +static int pex_unix_open_write (struct pex_obj *, const char *, int); +static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *, + char * const *, char * const *, + int, int, int, int, + const char **, int *); +static int pex_unix_close (struct pex_obj *, int); +static int pex_unix_wait (struct pex_obj *, pid_t, int *, struct pex_time *, + int, const char **, int *); +static int pex_unix_pipe (struct pex_obj *, int *, int); +static FILE *pex_unix_fdopenr (struct pex_obj *, int, int); +static FILE *pex_unix_fdopenw (struct pex_obj *, int, int); +static void pex_unix_cleanup (struct pex_obj *); + +/* The list of functions we pass to the common routines. */ - output_desc = out; +const struct pex_funcs funcs = +{ + pex_unix_open_read, + pex_unix_open_write, + pex_unix_exec_child, + pex_unix_close, + pex_unix_wait, + pex_unix_pipe, + pex_unix_fdopenr, + pex_unix_fdopenw, + pex_unix_cleanup +}; + +/* Return a newly initialized pex_obj structure. */ + +struct pex_obj * +pex_init (int flags, const char *pname, const char *tempbase) +{ + return pex_init_common (flags, pname, tempbase, &funcs); +} + +/* Open a file for reading. */ + +static int +pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name, + int binary ATTRIBUTE_UNUSED) +{ + return open (name, O_RDONLY); +} + +/* Open a file for writing. */ + +static int +pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name, + int binary ATTRIBUTE_UNUSED) +{ + /* Note that we can't use O_EXCL here because gcc may have already + created the temporary file via make_temp_file. */ + return open (name, O_WRONLY | O_CREAT | O_TRUNC, PUBLIC_MODE); +} + +/* Close a file. */ + +static int +pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd) +{ + return close (fd); +} + +/* Report an error from a child process. We don't use stdio routines, + because we might be here due to a vfork call. */ + +static void +pex_child_error (struct pex_obj *obj, const char *executable, + const char *errmsg, int err) +{ + int retval = 0; +#define writeerr(s) retval |= (write (STDERR_FILE_NO, s, strlen (s)) < 0) + writeerr (obj->pname); + writeerr (": error trying to exec '"); + writeerr (executable); + writeerr ("': "); + writeerr (errmsg); + writeerr (": "); + writeerr (xstrerror (err)); + writeerr ("\n"); +#undef writeerr + /* Exit with -2 if the error output failed, too. */ + _exit (retval == 0 ? -1 : -2); +} + +/* Execute a child. */ + +extern char **environ; + +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; + + /* We declare these to be volatile to avoid warnings from gcc about + them being clobbered by vfork. */ + volatile int sleep_interval; + volatile int retries; + + /* We vfork and then set environ in the child before calling execvp. + This clobbers the parent's environ so we need to restore it. + It would be nice to use one of the exec* functions that takes an + environment as a parameter, but that may have portability issues. */ + char **save_environ = environ; - /* Fork a subprocess; wait and retry if it fails. */ sleep_interval = 1; pid = -1; - for (retries = 0; retries < 4; retries++) + for (retries = 0; retries < 4; ++retries) { pid = vfork (); if (pid >= 0) @@ -147,69 +420,170 @@ pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, switch (pid) { case -1: - *errmsg_fmt = "fork"; - *errmsg_arg = NULL; - return -1; + *err = errno; + *errmsg = VFORK_STRING; + return (pid_t) -1; - case 0: /* child */ - /* Move the input and output pipes into place, if necessary. */ - if (input_desc != STDIN_FILE_NO) + case 0: + /* Child process. */ + if (in != STDIN_FILE_NO) + { + if (dup2 (in, STDIN_FILE_NO) < 0) + pex_child_error (obj, executable, "dup2", errno); + if (close (in) < 0) + pex_child_error (obj, executable, "close", errno); + } + if (out != STDOUT_FILE_NO) + { + if (dup2 (out, STDOUT_FILE_NO) < 0) + pex_child_error (obj, executable, "dup2", errno); + if (close (out) < 0) + pex_child_error (obj, executable, "close", errno); + } + if (errdes != STDERR_FILE_NO) + { + if (dup2 (errdes, STDERR_FILE_NO) < 0) + pex_child_error (obj, executable, "dup2", errno); + if (close (errdes) < 0) + pex_child_error (obj, executable, "close", errno); + } + if (toclose >= 0) { - close (STDIN_FILE_NO); - dup (input_desc); - close (input_desc); + if (close (toclose) < 0) + pex_child_error (obj, executable, "close", errno); } - if (output_desc != STDOUT_FILE_NO) + if ((flags & PEX_STDERR_TO_STDOUT) != 0) { - close (STDOUT_FILE_NO); - dup (output_desc); - close (output_desc); + if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0) + pex_child_error (obj, executable, "dup2", errno); } - /* Close the parent's descs that aren't wanted here. */ - if (last_pipe_input != STDIN_FILE_NO) - close (last_pipe_input); + if (env) + { + /* NOTE: In a standard vfork implementation this clobbers the + parent's copy of environ "too" (in reality there's only one copy). + This is ok as we restore it below. */ + environ = (char**) env; + } - /* Exec the program. */ - if (flags & PEXECUTE_SEARCH) - execvp (program, argv); + if ((flags & PEX_SEARCH) != 0) + { + execvp (executable, to_ptr32 (argv)); + pex_child_error (obj, executable, "execvp", errno); + } 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); + { + execv (executable, to_ptr32 (argv)); + pex_child_error (obj, executable, "execv", errno); + } + /* NOTREACHED */ - return 0; + return (pid_t) -1; 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. */ + /* Parent process. */ + + /* Restore environ. + Note that the parent either doesn't run until the child execs/exits + (standard vfork behaviour), or if it does run then vfork is behaving + more like fork. In either case we needn't worry about clobbering + the child's copy of environ. */ + environ = save_environ; + + if (in != STDIN_FILE_NO) + { + if (close (in) < 0) + { + *err = errno; + *errmsg = "close"; + return (pid_t) -1; + } + } + if (out != STDOUT_FILE_NO) + { + if (close (out) < 0) + { + *err = errno; + *errmsg = "close"; + return (pid_t) -1; + } + } + if (errdes != STDERR_FILE_NO) + { + if (close (errdes) < 0) + { + *err = errno; + *errmsg = "close"; + return (pid_t) -1; + } + } + return pid; } } -int -pwait (pid, status, flags) - int pid; - int *status; - int flags ATTRIBUTE_UNUSED; +/* Wait for a child process to complete. */ + +static int +pex_unix_wait (struct pex_obj *obj, pid_t pid, int *status, + struct pex_time *time, int done, const char **errmsg, + int *err) { - /* ??? Here's an opportunity to canonicalize the values in STATUS. - Needed? */ - pid = waitpid (pid, status, 0); - return pid; + /* If we are cleaning up when the caller didn't retrieve process + status for some reason, encourage the process to go away. */ + if (done) + kill (pid, SIGTERM); + + if (pex_wait (obj, pid, status, time) < 0) + { + *err = errno; + *errmsg = "wait"; + return -1; + } + + return 0; +} + +/* Create a pipe. */ + +static int +pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p, + int binary ATTRIBUTE_UNUSED) +{ + return pipe (p); +} + +/* Get a FILE pointer to read from a file descriptor. */ + +static FILE * +pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, + int binary ATTRIBUTE_UNUSED) +{ + return fdopen (fd, "r"); +} + +static FILE * +pex_unix_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, + int binary ATTRIBUTE_UNUSED) +{ + if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) + return NULL; + return fdopen (fd, "w"); +} + +static void +pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED) +{ +#if !defined (HAVE_WAIT4) && !defined (HAVE_WAITPID) + while (obj->sysdep != NULL) + { + struct status_list *this; + struct status_list *next; + + this = (struct status_list *) obj->sysdep; + next = this->next; + free (this); + obj->sysdep = (void *) next; + } +#endif }