|  | /* Common code for executing a program in a sub-process. | 
|  | Copyright (C) 2005, 2010 Free Software Foundation, Inc. | 
|  | Written by Ian Lance Taylor <ian@airs.com>. | 
|  |  | 
|  | 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., 51 Franklin Street - Fifth Floor, | 
|  | Boston, MA 02110-1301, USA.  */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "libiberty.h" | 
|  | #include "pex-common.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <errno.h> | 
|  | #ifdef NEED_DECLARATION_ERRNO | 
|  | extern int errno; | 
|  | #endif | 
|  | #ifdef HAVE_STDLIB_H | 
|  | #include <stdlib.h> | 
|  | #endif | 
|  | #ifdef HAVE_STRING_H | 
|  | #include <string.h> | 
|  | #endif | 
|  | #ifdef HAVE_UNISTD_H | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | extern int mkstemps (char *, int); | 
|  |  | 
|  | /* This file contains subroutines for the program execution routines | 
|  | (pex_init, pex_run, etc.).  This file is compiled on all | 
|  | systems.  */ | 
|  |  | 
|  | static void pex_add_remove (struct pex_obj *, const char *, int); | 
|  | static int pex_get_status_and_time (struct pex_obj *, int, const char **, | 
|  | int *); | 
|  |  | 
|  | /* Initialize a pex_obj structure.  */ | 
|  |  | 
|  | struct pex_obj * | 
|  | pex_init_common (int flags, const char *pname, const char *tempbase, | 
|  | const struct pex_funcs *funcs) | 
|  | { | 
|  | struct pex_obj *obj; | 
|  |  | 
|  | obj = XNEW (struct pex_obj); | 
|  | obj->flags = flags; | 
|  | obj->pname = pname; | 
|  | obj->tempbase = tempbase; | 
|  | obj->next_input = STDIN_FILE_NO; | 
|  | obj->next_input_name = NULL; | 
|  | obj->next_input_name_allocated = 0; | 
|  | obj->stderr_pipe = -1; | 
|  | obj->count = 0; | 
|  | obj->children = NULL; | 
|  | obj->status = NULL; | 
|  | obj->time = NULL; | 
|  | obj->number_waited = 0; | 
|  | obj->input_file = NULL; | 
|  | obj->read_output = NULL; | 
|  | obj->read_err = NULL; | 
|  | obj->remove_count = 0; | 
|  | obj->remove = NULL; | 
|  | obj->funcs = funcs; | 
|  | obj->sysdep = NULL; | 
|  | return obj; | 
|  | } | 
|  |  | 
|  | /* Add a file to be removed when we are done.  */ | 
|  |  | 
|  | static void | 
|  | pex_add_remove (struct pex_obj *obj, const char *name, int allocated) | 
|  | { | 
|  | char *add; | 
|  |  | 
|  | ++obj->remove_count; | 
|  | obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count); | 
|  | if (allocated) | 
|  | add = (char *) name; | 
|  | else | 
|  | add = xstrdup (name); | 
|  | obj->remove[obj->remove_count - 1] = add; | 
|  | } | 
|  |  | 
|  | /* Generate a temporary file name based on OBJ, FLAGS, and NAME. | 
|  | Return NULL if we were unable to reserve a temporary filename. | 
|  |  | 
|  | If non-NULL, the result is either allocated with malloc, or the | 
|  | same pointer as NAME.  */ | 
|  | static char * | 
|  | temp_file (struct pex_obj *obj, int flags, char *name) | 
|  | { | 
|  | if (name == NULL) | 
|  | { | 
|  | if (obj->tempbase == NULL) | 
|  | { | 
|  | name = make_temp_file (NULL); | 
|  | } | 
|  | else | 
|  | { | 
|  | int len = strlen (obj->tempbase); | 
|  | int out; | 
|  |  | 
|  | if (len >= 6 | 
|  | && strcmp (obj->tempbase + len - 6, "XXXXXX") == 0) | 
|  | name = xstrdup (obj->tempbase); | 
|  | else | 
|  | name = concat (obj->tempbase, "XXXXXX", NULL); | 
|  |  | 
|  | out = mkstemps (name, 0); | 
|  | if (out < 0) | 
|  | { | 
|  | free (name); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* This isn't obj->funcs->close because we got the | 
|  | descriptor from mkstemps, not from a function in | 
|  | obj->funcs.  Calling close here is just like what | 
|  | make_temp_file does.  */ | 
|  | close (out); | 
|  | } | 
|  | } | 
|  | else if ((flags & PEX_SUFFIX) != 0) | 
|  | { | 
|  | if (obj->tempbase == NULL) | 
|  | name = make_temp_file (name); | 
|  | else | 
|  | name = concat (obj->tempbase, name, NULL); | 
|  | } | 
|  |  | 
|  | return name; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* As for pex_run (), but permits the environment for the child process | 
|  | to be specified. */ | 
|  |  | 
|  | const char * | 
|  | pex_run_in_environment (struct pex_obj *obj, int flags, const char *executable, | 
|  | char * const * argv, char * const * env, | 
|  | const char *orig_outname, const char *errname, | 
|  | int *err) | 
|  | { | 
|  | const char *errmsg; | 
|  | int in, out, errdes; | 
|  | char *outname; | 
|  | int outname_allocated; | 
|  | int p[2]; | 
|  | int toclose; | 
|  | pid_t pid; | 
|  |  | 
|  | in = -1; | 
|  | out = -1; | 
|  | errdes = -1; | 
|  | outname = (char *) orig_outname; | 
|  | outname_allocated = 0; | 
|  |  | 
|  | /* If the user called pex_input_file, close the file now.  */ | 
|  | if (obj->input_file) | 
|  | { | 
|  | if (fclose (obj->input_file) == EOF) | 
|  | { | 
|  | errmsg = "closing pipeline input file"; | 
|  | goto error_exit; | 
|  | } | 
|  | obj->input_file = NULL; | 
|  | } | 
|  |  | 
|  | /* Set IN.  */ | 
|  |  | 
|  | if (obj->next_input_name != NULL) | 
|  | { | 
|  | /* We have to make sure that the previous process has completed | 
|  | before we try to read the file.  */ | 
|  | if (!pex_get_status_and_time (obj, 0, &errmsg, err)) | 
|  | goto error_exit; | 
|  |  | 
|  | in = obj->funcs->open_read (obj, obj->next_input_name, | 
|  | (flags & PEX_BINARY_INPUT) != 0); | 
|  | if (in < 0) | 
|  | { | 
|  | *err = errno; | 
|  | errmsg = "open temporary file"; | 
|  | goto error_exit; | 
|  | } | 
|  | if (obj->next_input_name_allocated) | 
|  | { | 
|  | free (obj->next_input_name); | 
|  | obj->next_input_name_allocated = 0; | 
|  | } | 
|  | obj->next_input_name = NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | in = obj->next_input; | 
|  | if (in < 0) | 
|  | { | 
|  | *err = 0; | 
|  | errmsg = "pipeline already complete"; | 
|  | goto error_exit; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Set OUT and OBJ->NEXT_INPUT/OBJ->NEXT_INPUT_NAME.  */ | 
|  |  | 
|  | if ((flags & PEX_LAST) != 0) | 
|  | { | 
|  | if (outname == NULL) | 
|  | out = STDOUT_FILE_NO; | 
|  | else if ((flags & PEX_SUFFIX) != 0) | 
|  | { | 
|  | outname = concat (obj->tempbase, outname, NULL); | 
|  | outname_allocated = 1; | 
|  | } | 
|  | obj->next_input = -1; | 
|  | } | 
|  | else if ((obj->flags & PEX_USE_PIPES) == 0) | 
|  | { | 
|  | outname = temp_file (obj, flags, outname); | 
|  | if (! outname) | 
|  | { | 
|  | *err = 0; | 
|  | errmsg = "could not create temporary file"; | 
|  | goto error_exit; | 
|  | } | 
|  |  | 
|  | if (outname != orig_outname) | 
|  | outname_allocated = 1; | 
|  |  | 
|  | if ((obj->flags & PEX_SAVE_TEMPS) == 0) | 
|  | { | 
|  | pex_add_remove (obj, outname, outname_allocated); | 
|  | outname_allocated = 0; | 
|  | } | 
|  |  | 
|  | /* Hand off ownership of outname to the next stage.  */ | 
|  | obj->next_input_name = outname; | 
|  | obj->next_input_name_allocated = outname_allocated; | 
|  | outname_allocated = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_OUTPUT) != 0) < 0) | 
|  | { | 
|  | *err = errno; | 
|  | errmsg = "pipe"; | 
|  | goto error_exit; | 
|  | } | 
|  |  | 
|  | out = p[WRITE_PORT]; | 
|  | obj->next_input = p[READ_PORT]; | 
|  | } | 
|  |  | 
|  | if (out < 0) | 
|  | { | 
|  | out = obj->funcs->open_write (obj, outname, | 
|  | (flags & PEX_BINARY_OUTPUT) != 0, | 
|  | (flags & PEX_STDOUT_APPEND) != 0); | 
|  | if (out < 0) | 
|  | { | 
|  | *err = errno; | 
|  | errmsg = "open temporary output file"; | 
|  | goto error_exit; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (outname_allocated) | 
|  | { | 
|  | free (outname); | 
|  | outname_allocated = 0; | 
|  | } | 
|  |  | 
|  | /* Set ERRDES.  */ | 
|  |  | 
|  | if (errname != NULL && (flags & PEX_STDERR_TO_PIPE) != 0) | 
|  | { | 
|  | *err = 0; | 
|  | errmsg = "both ERRNAME and PEX_STDERR_TO_PIPE specified."; | 
|  | goto error_exit; | 
|  | } | 
|  |  | 
|  | if (obj->stderr_pipe != -1) | 
|  | { | 
|  | *err = 0; | 
|  | errmsg = "PEX_STDERR_TO_PIPE used in the middle of pipeline"; | 
|  | goto error_exit; | 
|  | } | 
|  |  | 
|  | if (errname == NULL) | 
|  | { | 
|  | if (flags & PEX_STDERR_TO_PIPE) | 
|  | { | 
|  | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_ERROR) != 0) < 0) | 
|  | { | 
|  | *err = errno; | 
|  | errmsg = "pipe"; | 
|  | goto error_exit; | 
|  | } | 
|  |  | 
|  | errdes = p[WRITE_PORT]; | 
|  | obj->stderr_pipe = p[READ_PORT]; | 
|  | } | 
|  | else | 
|  | { | 
|  | errdes = STDERR_FILE_NO; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | errdes = obj->funcs->open_write (obj, errname, | 
|  | (flags & PEX_BINARY_ERROR) != 0, | 
|  | (flags & PEX_STDERR_APPEND) != 0); | 
|  | if (errdes < 0) | 
|  | { | 
|  | *err = errno; | 
|  | errmsg = "open error file"; | 
|  | goto error_exit; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If we are using pipes, the child process has to close the next | 
|  | input pipe.  */ | 
|  |  | 
|  | if ((obj->flags & PEX_USE_PIPES) == 0) | 
|  | toclose = -1; | 
|  | else | 
|  | toclose = obj->next_input; | 
|  |  | 
|  | /* Run the program.  */ | 
|  |  | 
|  | pid = obj->funcs->exec_child (obj, flags, executable, argv, env, | 
|  | in, out, errdes, toclose, &errmsg, err); | 
|  | if (pid < 0) | 
|  | goto error_exit; | 
|  |  | 
|  | ++obj->count; | 
|  | obj->children = XRESIZEVEC (pid_t, obj->children, obj->count); | 
|  | obj->children[obj->count - 1] = pid; | 
|  |  | 
|  | return NULL; | 
|  |  | 
|  | error_exit: | 
|  | if (in >= 0 && in != STDIN_FILE_NO) | 
|  | obj->funcs->close (obj, in); | 
|  | if (out >= 0 && out != STDOUT_FILE_NO) | 
|  | obj->funcs->close (obj, out); | 
|  | if (errdes >= 0 && errdes != STDERR_FILE_NO) | 
|  | obj->funcs->close (obj, errdes); | 
|  | if (outname_allocated) | 
|  | free (outname); | 
|  | return errmsg; | 
|  | } | 
|  |  | 
|  | /* Run a program.  */ | 
|  |  | 
|  | const char * | 
|  | pex_run (struct pex_obj *obj, int flags, const char *executable, | 
|  | char * const * argv, const char *orig_outname, const char *errname, | 
|  | int *err) | 
|  | { | 
|  | return pex_run_in_environment (obj, flags, executable, argv, NULL, | 
|  | orig_outname, errname, err); | 
|  | } | 
|  |  | 
|  | /* Return a FILE pointer for a temporary file to fill with input for | 
|  | the pipeline.  */ | 
|  | FILE * | 
|  | pex_input_file (struct pex_obj *obj, int flags, const char *in_name) | 
|  | { | 
|  | char *name = (char *) in_name; | 
|  | FILE *f; | 
|  |  | 
|  | /* This must be called before the first pipeline stage is run, and | 
|  | there must not have been any other input selected.  */ | 
|  | if (obj->count != 0 | 
|  | || (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) | 
|  | || obj->next_input_name) | 
|  | { | 
|  | errno = EINVAL; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | name = temp_file (obj, flags, name); | 
|  | if (! name) | 
|  | return NULL; | 
|  |  | 
|  | f = fopen (name, (flags & PEX_BINARY_OUTPUT) ? "wb" : "w"); | 
|  | if (! f) | 
|  | { | 
|  | free (name); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | obj->input_file = f; | 
|  | obj->next_input_name = name; | 
|  | obj->next_input_name_allocated = (name != in_name); | 
|  |  | 
|  | return f; | 
|  | } | 
|  |  | 
|  | /* Return a stream for a pipe connected to the standard input of the | 
|  | first stage of the pipeline.  */ | 
|  | FILE * | 
|  | pex_input_pipe (struct pex_obj *obj, int binary) | 
|  | { | 
|  | int p[2]; | 
|  | FILE *f; | 
|  |  | 
|  | /* You must call pex_input_pipe before the first pex_run or pex_one.  */ | 
|  | if (obj->count > 0) | 
|  | goto usage_error; | 
|  |  | 
|  | /* You must be using pipes.  Implementations that don't support | 
|  | pipes clear this flag before calling pex_init_common.  */ | 
|  | if (! (obj->flags & PEX_USE_PIPES)) | 
|  | goto usage_error; | 
|  |  | 
|  | /* If we have somehow already selected other input, that's a | 
|  | mistake.  */ | 
|  | if ((obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) | 
|  | || obj->next_input_name) | 
|  | goto usage_error; | 
|  |  | 
|  | if (obj->funcs->pipe (obj, p, binary != 0) < 0) | 
|  | return NULL; | 
|  |  | 
|  | f = obj->funcs->fdopenw (obj, p[WRITE_PORT], binary != 0); | 
|  | if (! f) | 
|  | { | 
|  | int saved_errno = errno; | 
|  | obj->funcs->close (obj, p[READ_PORT]); | 
|  | obj->funcs->close (obj, p[WRITE_PORT]); | 
|  | errno = saved_errno; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | obj->next_input = p[READ_PORT]; | 
|  |  | 
|  | return f; | 
|  |  | 
|  | usage_error: | 
|  | errno = EINVAL; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Return a FILE pointer for the output of the last program | 
|  | executed.  */ | 
|  |  | 
|  | FILE * | 
|  | pex_read_output (struct pex_obj *obj, int binary) | 
|  | { | 
|  | if (obj->next_input_name != NULL) | 
|  | { | 
|  | const char *errmsg; | 
|  | int err; | 
|  |  | 
|  | /* We have to make sure that the process has completed before we | 
|  | try to read the file.  */ | 
|  | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) | 
|  | { | 
|  | errno = err; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | obj->read_output = fopen (obj->next_input_name, binary ? "rb" : "r"); | 
|  |  | 
|  | if (obj->next_input_name_allocated) | 
|  | { | 
|  | free (obj->next_input_name); | 
|  | obj->next_input_name_allocated = 0; | 
|  | } | 
|  | obj->next_input_name = NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | int o; | 
|  |  | 
|  | o = obj->next_input; | 
|  | if (o < 0 || o == STDIN_FILE_NO) | 
|  | return NULL; | 
|  | obj->read_output = obj->funcs->fdopenr (obj, o, binary); | 
|  | obj->next_input = -1; | 
|  | } | 
|  |  | 
|  | return obj->read_output; | 
|  | } | 
|  |  | 
|  | FILE * | 
|  | pex_read_err (struct pex_obj *obj, int binary) | 
|  | { | 
|  | int o; | 
|  |  | 
|  | o = obj->stderr_pipe; | 
|  | if (o < 0 || o == STDIN_FILE_NO) | 
|  | return NULL; | 
|  | obj->read_err = obj->funcs->fdopenr (obj, o, binary); | 
|  | obj->stderr_pipe = -1; | 
|  | return obj->read_err; | 
|  | } | 
|  |  | 
|  | /* Get the exit status and, if requested, the resource time for all | 
|  | the child processes.  Return 0 on failure, 1 on success.  */ | 
|  |  | 
|  | static int | 
|  | pex_get_status_and_time (struct pex_obj *obj, int done, const char **errmsg, | 
|  | int *err) | 
|  | { | 
|  | int ret; | 
|  | int i; | 
|  |  | 
|  | if (obj->number_waited == obj->count) | 
|  | return 1; | 
|  |  | 
|  | obj->status = XRESIZEVEC (int, obj->status, obj->count); | 
|  | if ((obj->flags & PEX_RECORD_TIMES) != 0) | 
|  | obj->time = XRESIZEVEC (struct pex_time, obj->time, obj->count); | 
|  |  | 
|  | ret = 1; | 
|  | for (i = obj->number_waited; i < obj->count; ++i) | 
|  | { | 
|  | if (obj->funcs->wait (obj, obj->children[i], &obj->status[i], | 
|  | obj->time == NULL ? NULL : &obj->time[i], | 
|  | done, errmsg, err) < 0) | 
|  | ret = 0; | 
|  | } | 
|  | obj->number_waited = i; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Get exit status of executed programs.  */ | 
|  |  | 
|  | int | 
|  | pex_get_status (struct pex_obj *obj, int count, int *vector) | 
|  | { | 
|  | if (obj->status == NULL) | 
|  | { | 
|  | const char *errmsg; | 
|  | int err; | 
|  |  | 
|  | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (count > obj->count) | 
|  | { | 
|  | memset (vector + obj->count, 0, (count - obj->count) * sizeof (int)); | 
|  | count = obj->count; | 
|  | } | 
|  |  | 
|  | memcpy (vector, obj->status, count * sizeof (int)); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Get process times of executed programs.  */ | 
|  |  | 
|  | int | 
|  | pex_get_times (struct pex_obj *obj, int count, struct pex_time *vector) | 
|  | { | 
|  | if (obj->status == NULL) | 
|  | { | 
|  | const char *errmsg; | 
|  | int err; | 
|  |  | 
|  | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (obj->time == NULL) | 
|  | return 0; | 
|  |  | 
|  | if (count > obj->count) | 
|  | { | 
|  | memset (vector + obj->count, 0, | 
|  | (count - obj->count) * sizeof (struct pex_time)); | 
|  | count = obj->count; | 
|  | } | 
|  |  | 
|  | memcpy (vector, obj->time, count * sizeof (struct pex_time)); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Free a pex_obj structure.  */ | 
|  |  | 
|  | void | 
|  | pex_free (struct pex_obj *obj) | 
|  | { | 
|  | /* Close pipe file descriptors corresponding to child's stdout and | 
|  | stderr so that the child does not hang trying to output something | 
|  | while we're waiting for it.  */ | 
|  | if (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) | 
|  | obj->funcs->close (obj, obj->next_input); | 
|  | if (obj->stderr_pipe >= 0 && obj->stderr_pipe != STDIN_FILE_NO) | 
|  | obj->funcs->close (obj, obj->stderr_pipe); | 
|  | if (obj->read_output != NULL) | 
|  | fclose (obj->read_output); | 
|  | if (obj->read_err != NULL) | 
|  | fclose (obj->read_err); | 
|  |  | 
|  | /* If the caller forgot to wait for the children, we do it here, to | 
|  | avoid zombies.  */ | 
|  | if (obj->status == NULL) | 
|  | { | 
|  | const char *errmsg; | 
|  | int err; | 
|  |  | 
|  | obj->flags &= ~ PEX_RECORD_TIMES; | 
|  | pex_get_status_and_time (obj, 1, &errmsg, &err); | 
|  | } | 
|  |  | 
|  | if (obj->next_input_name_allocated) | 
|  | free (obj->next_input_name); | 
|  | free (obj->children); | 
|  | free (obj->status); | 
|  | free (obj->time); | 
|  |  | 
|  | if (obj->remove_count > 0) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < obj->remove_count; ++i) | 
|  | { | 
|  | remove (obj->remove[i]); | 
|  | free (obj->remove[i]); | 
|  | } | 
|  | free (obj->remove); | 
|  | } | 
|  |  | 
|  | if (obj->funcs->cleanup != NULL) | 
|  | obj->funcs->cleanup (obj); | 
|  |  | 
|  | free (obj); | 
|  | } |