X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libiberty%2Fpex-unix.c;h=35a545cb17b1dccebf2949472cb75d7df6afd9be;hb=6a0e74dc89f12f5c7fd14f685f7387fcf8a57b58;hp=14fe71ed09c0f1e46bcb93e6df3e80eb3ed3e00c;hpb=8cb0536d25508acaffb368dc075c5038eaa4e068;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libiberty/pex-unix.c b/libiberty/pex-unix.c index 14fe71ed09c..35a545cb17b 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, 2005 Free Software Foundation, Inc. This file is part of the libiberty library. @@ -17,87 +17,355 @@ 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 +#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 +# define VFORK_STRING "vfork" +#endif +#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 */ + + +/* 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 -#ifndef HAVE_WAITPID -#define waitpid(pid, status, flags) wait(status) +/* 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. */ + +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 -extern int execv (); -extern int execvp (); + ret = wait4 (pid, status, 0, &r); + + if (time != NULL) + { + 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) */ -int -pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags) - const char *program; - char * const *argv; - const char *this_pname; - const char *temp_base ATTRIBUTE_UNUSED; - char **errmsg_fmt, **errmsg_arg; - int flags; +static pid_t +pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status, + struct pex_time *time) { - int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv); - int pid; - int pdes[2]; - int input_desc, output_desc; - 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; + struct rusage r1, r2; + pid_t ret; - /* If this is the first process, initialize. */ - if (flags & PEXECUTE_FIRST) - last_pipe_input = STDIN_FILE_NO; + if (time == NULL) + return waitpid (pid, status, 0); - input_desc = last_pipe_input; + getrusage (RUSAGE_CHILDREN, &r1); - /* 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)) + 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) { - if (pipe (pdes) < 0) + --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; } - output_desc = pdes[WRITE_PORT]; - last_pipe_input = pdes[READ_PORT]; } - else + + while (1) { - /* Last process. */ - output_desc = 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 long pex_unix_exec_child (struct pex_obj *, int, const char *, + char * const *, int, int, int, + const char **, int *); +static int pex_unix_close (struct pex_obj *, int); +static int pex_unix_wait (struct pex_obj *, long, 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 void pex_unix_cleanup (struct pex_obj *); + +/* The list of functions we pass to the common routines. */ + +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_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) +{ +#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s)) + writeerr (obj->pname); + writeerr (": error trying to exec '"); + writeerr (executable); + writeerr ("': "); + writeerr (errmsg); + writeerr (": "); + writeerr (xstrerror (err)); + writeerr ("\n"); + _exit (-1); +} + +/* Execute a child. */ + +static long +pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable, + char * const * argv, int in, int out, int errdes, + 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; - /* 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 = fork (); + pid = vfork (); if (pid >= 0) break; sleep (sleep_interval); @@ -107,60 +375,139 @@ pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags) switch (pid) { case -1: - *errmsg_fmt = "fork"; - *errmsg_arg = NULL; + *err = errno; + *errmsg = VFORK_STRING; return -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) { - close (STDIN_FILE_NO); - dup (input_desc); - close (input_desc); + 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 (output_desc != STDOUT_FILE_NO) + if (out != STDOUT_FILE_NO) { - close (STDOUT_FILE_NO); - dup (output_desc); - close (output_desc); + 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 ((flags & PEX_STDERR_TO_STDOUT) != 0) + { + if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0) + pex_child_error (obj, executable, "dup2", errno); + } + if ((flags & PEX_SEARCH) != 0) + { + execvp (executable, argv); + pex_child_error (obj, executable, "execvp", errno); + } + else + { + execv (executable, argv); + pex_child_error (obj, executable, "execv", errno); } - /* Close the parent's descs that aren't wanted here. */ - if (last_pipe_input != STDIN_FILE_NO) - 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); /* NOTREACHED */ - return 0; + return -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. */ - return pid; + /* Parent process. */ + if (in != STDIN_FILE_NO) + { + if (close (in) < 0) + { + *err = errno; + *errmsg = "close"; + return -1; + } + } + if (out != STDOUT_FILE_NO) + { + if (close (out) < 0) + { + *err = errno; + *errmsg = "close"; + return -1; + } + } + if (errdes != STDERR_FILE_NO) + { + if (close (errdes) < 0) + { + *err = errno; + *errmsg = "close"; + return -1; + } + } + + return (long) pid; + } +} + +/* Wait for a child process to complete. */ + +static int +pex_unix_wait (struct pex_obj *obj, long pid, int *status, + struct pex_time *time, int done, const char **errmsg, + int *err) +{ + /* 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; } -int -pwait (pid, status, flags) - int pid; - int *status; - int flags ATTRIBUTE_UNUSED; +/* Create a pipe. */ + +static int +pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p, + int binary ATTRIBUTE_UNUSED) { - /* ??? Here's an opportunity to canonicalize the values in STATUS. - Needed? */ - pid = waitpid (pid, status, 0); - return pid; + 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 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 }