From 329c5bb8506405e9732735bffd44349981016d2a Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 17 Mar 2006 20:36:14 +0000 Subject: gdb/ChangeLog: 2006-03-17 Jim Blandy Add support for 'target remote |' on MinGW. * ser-mingw.c (struct pipe_state): New structure. (make_pipe_state, free_pipe_state, cleanup_pipe_state) (pipe_windows_open, pipe_windows_close, pipe_windows_read) (pipe_windows_write, pipe_wait_handle): New functions. (_initialize_ser_windows): Register a "pipe" interface based on them. include/ChangeLog: 2006-03-15 Jim Blandy * libiberty.h (pex_write_input): New declaration. libiberty/ChangeLog: 2006-03-15 Jim Blandy * pex-common.c (pex_write_input): New function. * pexecute.txh (pex_write_input): Document it. * pex-common.h (struct pex_funcs): New function ptr fdopenw. * pex-unix.c (pex_unix_fdopenw): New function. (funcs): List it as our fdopenw function. * pex-win32.c (pex_win32_fdopenw): New function. (funcs): List it as our fdopenw function. * pex-djgpp.c (funcs): Leave fdopenw null. * pex-msdos (funcs): Same. * functions.texi: Regenerated. 2006-03-12 Jim Blandy * pex-common.h (struct pex_obj): Doc fixes. 2006-03-11 Jim Blandy * functions.texi: Regenerate. --- gdb/ChangeLog | 10 ++ gdb/ser-mingw.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++ include/ChangeLog | 4 + include/libiberty.h | 27 ++++++ libiberty/ChangeLog | 21 +++++ libiberty/functions.texi | 69 ++++++++++++-- libiberty/pex-common.c | 46 +++++++++ libiberty/pex-common.h | 32 ++++--- libiberty/pex-djgpp.c | 1 + libiberty/pex-msdos.c | 1 + libiberty/pex-unix.c | 11 +++ libiberty/pex-win32.c | 14 +++ libiberty/pexecute.txh | 29 ++++++ 13 files changed, 479 insertions(+), 22 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 34b1c6cf046..0cbace82b44 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2006-03-17 Jim Blandy + + Add support for 'target remote |' on MinGW. + * ser-mingw.c (struct pipe_state): New structure. + (make_pipe_state, free_pipe_state, cleanup_pipe_state) + (pipe_windows_open, pipe_windows_close, pipe_windows_read) + (pipe_windows_write, pipe_wait_handle): New functions. + (_initialize_ser_windows): Register a "pipe" interface based on + them. + 2006-02-26 Mark Kettenis * i386obsd-tdep.c (i386obsd_trapframe_sniffer): Also recognize diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c index 7a6f232379e..5d0c0733b57 100644 --- a/gdb/ser-mingw.c +++ b/gdb/ser-mingw.c @@ -571,6 +571,214 @@ ser_console_get_tty_state (struct serial *scb) return NULL; } +struct pipe_state +{ + /* Since we use the pipe_select_thread for our select emulation, + we need to place the state structure it requires at the front + of our state. */ + struct ser_console_state wait; + + /* The pex obj for our (one-stage) pipeline. */ + struct pex_obj *pex; + + /* Streams for the pipeline's input and output. */ + FILE *input, *output; +}; + +static struct pipe_state * +make_pipe_state (void) +{ + struct pipe_state *ps = XMALLOC (struct pipe_state); + + memset (ps, 0, sizeof (*ps)); + ps->wait.read_event = INVALID_HANDLE_VALUE; + ps->wait.except_event = INVALID_HANDLE_VALUE; + ps->wait.start_select = INVALID_HANDLE_VALUE; + ps->wait.stop_select = INVALID_HANDLE_VALUE; + + return ps; +} + +static void +free_pipe_state (struct pipe_state *ps) +{ + int saved_errno = errno; + + if (ps->wait.read_event != INVALID_HANDLE_VALUE) + CloseHandle (ps->wait.read_event); + if (ps->wait.except_event != INVALID_HANDLE_VALUE) + CloseHandle (ps->wait.except_event); + if (ps->wait.start_select != INVALID_HANDLE_VALUE) + CloseHandle (ps->wait.start_select); + + /* If we have a select thread running, let the select thread free + the stop event. */ + if (ps->wait.stop_select != INVALID_HANDLE_VALUE) + SetEvent (ps->wait.stop_select); + + if (ps->pex) + pex_free (ps->pex); + if (ps->input) + fclose (ps->input); + /* pex_free closes ps->output. */ + + xfree (ps); + + errno = saved_errno; +} + +static void +cleanup_pipe_state (void *untyped) +{ + struct pipe_state *ps = untyped; + + free_pipe_state (ps); +} + +static int +pipe_windows_open (struct serial *scb, const char *name) +{ + char **argv = buildargv (name); + struct cleanup *back_to = make_cleanup_freeargv (argv); + if (! argv[0] || argv[0][0] == '\0') + error ("missing child command"); + + struct pipe_state *ps = make_pipe_state (); + make_cleanup (cleanup_pipe_state, ps); + + ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL); + if (! ps->pex) + goto fail; + ps->input = pex_write_input (ps->pex, 1); + if (! ps->input) + goto fail; + + { + int err; + const char *err_msg + = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT, + argv[0], argv, NULL, NULL, + &err); + + if (err_msg) + { + /* Our caller expects us to return -1, but all they'll do with + it generally is print the message based on errno. We have + all the same information here, plus err_msg provided by + pex_run, so we just raise the error here. */ + if (err) + error ("error starting child process '%s': %s: %s", + name, err_msg, safe_strerror (err)); + else + error ("error starting child process '%s': %s", + name, err_msg); + } + } + + ps->output = pex_read_output (ps->pex, 1); + if (! ps->output) + goto fail; + + scb->fd = fileno (ps->output); + scb->state = (void *) ps; + + discard_cleanups (back_to); + return 0; + + fail: + do_cleanups (back_to); + return -1; +} + + +static void +pipe_windows_close (struct serial *scb) +{ + struct pipe_state *ps = scb->state; + + /* In theory, we should try to kill the subprocess here, but the pex + interface doesn't give us enough information to do that. Usually + closing the input pipe will get the message across. */ + + free_pipe_state (ps); +} + + +static int +pipe_windows_read (struct serial *scb, size_t count) +{ + HANDLE pipeline_out = (HANDLE) _get_osfhandle (scb->fd); + if (pipeline_out == INVALID_HANDLE_VALUE) + return -1; + + DWORD available; + if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL)) + return -1; + + if (count > available) + count = available; + + DWORD bytes_read; + if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL)) + return -1; + + return bytes_read; +} + + +static int +pipe_windows_write (struct serial *scb, const void *buf, size_t count) +{ + struct pipe_state *ps = scb->state; + int pipeline_in_fd = fileno (ps->input); + if (pipeline_in_fd < 0) + return -1; + + HANDLE pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd); + if (pipeline_in == INVALID_HANDLE_VALUE) + return -1; + + DWORD written; + if (! WriteFile (pipeline_in, buf, count, &written, NULL)) + return -1; + + return written; +} + + +static void +pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct pipe_state *ps = scb->state; + + /* Have we allocated our events yet? */ + if (ps->wait.read_event == INVALID_HANDLE_VALUE) + { + DWORD threadId; + + /* Create auto reset events to wake and terminate the select thread. */ + ps->wait.start_select = CreateEvent (0, FALSE, FALSE, 0); + ps->wait.stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Create our own events to report read and exceptions separately. + The exception event is currently never used. */ + ps->wait.read_event = CreateEvent (0, FALSE, FALSE, 0); + ps->wait.except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* Start the select thread. */ + CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + } + + ResetEvent (ps->wait.read_event); + ResetEvent (ps->wait.except_event); + + SetEvent (ps->wait.start_select); + + *read = ps->wait.read_event; + *except = ps->wait.except_event; +} + + struct net_windows_state { HANDLE read_event; @@ -763,6 +971,34 @@ _initialize_ser_windows (void) serial_add_interface (ops); + /* The pipe interface. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "pipe"; + ops->next = 0; + ops->open = pipe_windows_open; + ops->close = pipe_windows_close; + ops->readchar = ser_base_readchar; + ops->write = ser_base_write; + ops->flush_output = ser_base_flush_output; + ops->flush_input = ser_base_flush_input; + ops->send_break = ser_base_send_break; + ops->go_raw = ser_base_raw; + ops->get_tty_state = ser_base_get_tty_state; + ops->set_tty_state = ser_base_set_tty_state; + ops->print_tty_state = ser_base_print_tty_state; + ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; + ops->setbaudrate = ser_base_setbaudrate; + ops->setstopbits = ser_base_setstopbits; + ops->drain_output = ser_base_drain_output; + ops->async = ser_base_async; + ops->read_prim = pipe_windows_read; + ops->write_prim = pipe_windows_write; + ops->wait_handle = pipe_wait_handle; + + serial_add_interface (ops); + /* If WinSock works, register the TCP/UDP socket driver. */ if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) diff --git a/include/ChangeLog b/include/ChangeLog index 3c6907f56dc..f0bc4b37be1 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2006-03-15 Jim Blandy + + * libiberty.h (pex_write_input): New declaration. + 2006-02-17 Shrirang Khisti Anil Paranjape Shilin Shakti diff --git a/include/libiberty.h b/include/libiberty.h index c264cb2ab0e..6fdc6e9d0c8 100644 --- a/include/libiberty.h +++ b/include/libiberty.h @@ -448,6 +448,33 @@ extern const char *pex_run (struct pex_obj *obj, int flags, const char *outname, const char *errname, int *err); +/* Return a `FILE' pointer FP for the standard input of the first + program in the pipeline; FP is opened for writing. You must have + passed `PEX_USE_PIPES' to the `pex_init' call that returned OBJ. + You must close FP yourself with `fclose' to indicate that the + pipeline's input is complete. + + The file descriptor underlying FP is marked not to be inherited by + child processes. + + This call is not supported on systems which do not support pipes; + it returns with an error. (We could implement it by writing a + temporary file, but then you would need to write all your data and + close FP before your first call to `pex_run' -- and that wouldn't + work on systems that do support pipes: the pipe would fill up, and + you would block. So there isn't any easy way to conceal the + differences between the two types of systems.) + + If you call both `pex_write_input' and `pex_read_output', be + careful to avoid deadlock. If the output pipe fills up, so that + each program in the pipeline is waiting for the next to read more + data, and you fill the input pipe by writing more data to FP, then + there is no way to make progress: the only process that could read + data from the output pipe is you, but you are blocked on the input + pipe. */ + +extern FILE *pex_write_input (struct pex_obj *obj, int binary); + /* Read the standard output of the last program to be executed. pex_run can not be called after this. BINARY should be non-zero if the file should be opened in binary mode; this is ignored on Unix. diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index acdfad0657f..549e9b6609f 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,24 @@ +2006-03-15 Jim Blandy + + * pex-common.c (pex_write_input): New function. + * pexecute.txh (pex_write_input): Document it. + * pex-common.h (struct pex_funcs): New function ptr fdopenw. + * pex-unix.c (pex_unix_fdopenw): New function. + (funcs): List it as our fdopenw function. + * pex-win32.c (pex_win32_fdopenw): New function. + (funcs): List it as our fdopenw function. + * pex-djgpp.c (funcs): Leave fdopenw null. + * pex-msdos (funcs): Same. + * functions.texi: Regenerated. + +2006-03-12 Jim Blandy + + * pex-common.h (struct pex_obj): Doc fixes. + +2006-03-11 Jim Blandy + + * functions.texi: Regenerate. + 2006-02-21 Ben Elliston * pexecute.c (pwait): Syntax fix for previous change. diff --git a/libiberty/functions.texi b/libiberty/functions.texi index 8b4a50ef45e..ab2461dc8d2 100644 --- a/libiberty/functions.texi +++ b/libiberty/functions.texi @@ -214,6 +214,26 @@ symbolic name or message. @end deftypefn +@c argv.c:293 +@deftypefn Extension void expandargv (int *@var{argcp}, char ***@var{argvp}) + +The @var{argcp} and @code{argvp} arguments are pointers to the usual +@code{argc} and @code{argv} arguments to @code{main}. This function +looks for arguments that begin with the character @samp{@@}. Any such +arguments are interpreted as ``response files''. The contents of the +response file are interpreted as additional command line options. In +particular, the file is separated into whitespace-separated strings; +each such string is taken as a command-line option. The new options +are inserted in place of the option naming the response file, and +@code{*argcp} and @code{*argvp} will be updated. If the value of +@code{*argvp} is modified by this function, then the new value has +been dynamically allocated and can be deallocated by the caller with +@code{freeargv}. However, most callers will simply call +@code{expandargv} near the beginning of @code{main} and allow the +operating system to free the memory when the program exits. + +@end deftypefn + @c fdmatch.c:23 @deftypefn Extension int fdmatch (int @var{fd1}, int @var{fd2}) @@ -648,14 +668,14 @@ reading and writing. @end deftypefn -@c pexecute.txh:169 +@c pexecute.txh:198 @deftypefn Extension void pex_free (struct pex_obj @var{obj}) Clean up and free all data associated with @var{obj}. @end deftypefn -@c pexecute.txh:144 +@c pexecute.txh:173 @deftypefn Extension int pex_get_status (struct pex_obj *@var{obj}, int @var{count}, int *@var{vector}) Returns the exit status of all programs run using @var{obj}. @@ -665,7 +685,7 @@ to @code{pex_run}. Returns 0 on error, 1 on success. @end deftypefn -@c pexecute.txh:153 +@c pexecute.txh:182 @deftypefn Extension int pex_get_times (struct pex_obj *@var{obj}, int @var{count}, struct pex_time *@var{vector}) Returns the process execution times of all programs run using @@ -682,7 +702,7 @@ process times, all the fields will be set to @code{0}. @end deftypefn -@c pexecute.txh:1 +@c pexecute.txh:2 @deftypefn Extension {struct pex_obj *} pex_init (int @var{flags}, const char *@var{pname}, const char *@var{tempbase}) Prepare to execute one or more programs, with standard output of each @@ -714,7 +734,7 @@ temporary files; it may be @code{NULL} to use a randomly chosen name. @end deftypefn -@c pexecute.txh:175 +@c pexecute.txh:204 @deftypefn Extension {const char *} pex_one (int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{pname}, const char *@var{outname}, const char *@var{errname}, int *@var{status}, int *@var{err}) An interface to permit the easy execution of a @@ -727,7 +747,7 @@ be set to the exit status of the program. @end deftypefn -@c pexecute.txh:132 +@c pexecute.txh:161 @deftypefn Extension {FILE *} pex_read_output (struct pex_obj *@var{obj}, int @var{binary}) Returns a @code{FILE} pointer which may be used to read the standard @@ -740,7 +760,7 @@ it will be closed by @code{pex_free}. @end deftypefn -@c pexecute.txh:32 +@c pexecute.txh:33 @deftypefn Extension {const char *} pex_run (struct pex_obj *@var{obj}, int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{outname}, const char *@var{errname}, int *@var{err}) Execute one program in a pipeline. On success this returns @@ -841,7 +861,36 @@ value, or to 0 if there is no relevant @code{errno}. @end deftypefn -@c pexecute.txh:187 +@c pexecute.txh:133 +@deftypefn Extension {FILE *} pex_write_input (struct pex_obj *@var{obj}, int @var{binary}) + +Return a @code{FILE} pointer @var{fp} for the standard input of the +first program in the pipeline; @var{fp} is opened for writing. You +must have passed @code{PEX_USE_PIPES} to the @code{pex_init} call that +returned @var{obj}. You must close @var{fp} yourself with +@code{fclose} to indicate that the pipeline's input is complete. + +The file descriptor underlying @var{fp} is marked not to be inherited +by child processes. + +This call is not supported on systems which do not support pipes; it +returns with an error. (We could implement it by writing a temporary +file, but then you would need to write all your data and close +@var{fp} before your first call to @code{pex_run} --- and that +wouldn't work on systems that do support pipes: the pipe would fill +up, and you would block. So there isn't any easy way to conceal the +differences between the two types of systems.) + +If you call both @code{pex_write_input} and @code{pex_read_output}, be +careful to avoid deadlock. If the output pipe fills up, so that each +program in the pipeline is waiting for the next to read more data, and +you fill the input pipe by writing more data to @var{fp}, then there +is no way to make progress: the only process that could read data from +the output pipe is you, but you are blocked on the input pipe. + +@end deftypefn + +@c pexecute.txh:216 @deftypefn Extension int pexecute (const char *@var{program}, char * const *@var{argv}, const char *@var{this_pname}, const char *@var{temp_base}, char **@var{errmsg_fmt}, char **@var{errmsg_arg}, int flags) This is the old interface to execute one or more programs. It is @@ -869,7 +918,7 @@ name is unset/removed. @end deftypefn -@c pexecute.txh:195 +@c pexecute.txh:224 @deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags}) Another part of the old execution interface. @@ -1194,7 +1243,7 @@ translation is found, returns 0. @end deftypefn -@c strverscmp.c:24 +@c strverscmp.c:25 @deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2}) The @code{strverscmp} function compares the string @var{s1} against @var{s2}, considering them as holding indices/version numbers. Return diff --git a/libiberty/pex-common.c b/libiberty/pex-common.c index b2ca6e08ce2..38e3ac76956 100644 --- a/libiberty/pex-common.c +++ b/libiberty/pex-common.c @@ -297,6 +297,52 @@ pex_run (struct pex_obj *obj, int flags, const char *executable, return errmsg; } +/* Return a FILE pointer for the input of the first program + executed. */ + +FILE * +pex_write_input (struct pex_obj *obj, int binary) +{ + int p[2]; + FILE *write_input; + + /* You must call pex_write_input 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; + + write_input = obj->funcs->fdopenw (obj, p[WRITE_PORT], binary != 0); + if (! write_input) + { + 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 write_input; + + usage_error: + errno = EINVAL; + return NULL; +} + /* Return a FILE pointer for the output of the last program executed. */ diff --git a/libiberty/pex-common.h b/libiberty/pex-common.h index bd4f908769f..13ec98149ea 100644 --- a/libiberty/pex-common.h +++ b/libiberty/pex-common.h @@ -61,7 +61,7 @@ struct pex_obj int next_input_name_allocated; /* Number of child processes. */ int count; - /* PIDs of child processes; array allocated using maloc. */ + /* PIDs of child processes; array allocated using malloc. */ long *children; /* Exit statuses of child processes; array allocated using malloc. */ int *status; @@ -88,10 +88,11 @@ struct pex_funcs { /* Open file NAME for reading. If BINARY is non-zero, open in binary mode. Return >= 0 on success, -1 on error. */ - int (*open_read) (struct pex_obj *, const char *name, int binary); + int (*open_read) (struct pex_obj *, const char */* name */, int /* binary */); /* Open file NAME for writing. If BINARY is non-zero, open in binary mode. Return >= 0 on success, -1 on error. */ - int (*open_write) (struct pex_obj *, const char *name, int binary); + int (*open_write) (struct pex_obj *, const char */* name */, + int /* binary */); /* Execute a child process. FLAGS, EXECUTABLE, ARGV, ERR are from pex_run. IN, OUT, ERRDES are each a descriptor, from open_read, open_write, or pipe, or they are one of STDIN_FILE_NO, @@ -99,25 +100,32 @@ struct pex_funcs should be closed. The function should handle the PEX_STDERR_TO_STDOUT flag. Return >= 0 on success, or -1 on error and set *ERRMSG and *ERR. */ - long (*exec_child) (struct pex_obj *, int flags, const char *executable, - char * const * argv, int in, int out, int errdes, - const char **errmsg, int *err); + long (*exec_child) (struct pex_obj *, int /* flags */, + const char */* executable */, char * const * /* argv */, + int /* in */, int /* out */, int /* errdes */, + const char **/* errmsg */, int */* err */); /* Close a descriptor. Return 0 on success, -1 on error. */ int (*close) (struct pex_obj *, int); /* Wait for a child to complete, returning exit status in *STATUS and time in *TIME (if it is not null). CHILD is from fork. DONE is 1 if this is called via pex_free. ERRMSG and ERR are as in fork. Return 0 on success, -1 on error. */ - int (*wait) (struct pex_obj *, long, int *status, struct pex_time *time, - int done, const char **errmsg, int *err); + int (*wait) (struct pex_obj *, long /* child */, int * /* status */, + struct pex_time * /* time */, int /* done */, + const char ** /* errmsg */, int * /* err */); /* Create a pipe (only called if PEX_USE_PIPES is set) storing two - descriptin in *P. If BINARY is non-zero, open in binary mode. - Return 0 on success, -1 on error. */ - int (*pipe) (struct pex_obj *, int *p, int binary); + descriptors in P[0] and P[1]. If BINARY is non-zero, open in + binary mode. Return 0 on success, -1 on error. */ + int (*pipe) (struct pex_obj *, int * /* p */, int /* binary */); /* Get a FILE pointer to read from a file descriptor (only called if PEX_USE_PIPES is set). If BINARY is non-zero, open in binary mode. Return pointer on success, NULL on error. */ - FILE * (*fdopenr) (struct pex_obj *, int fd, int binary); + FILE * (*fdopenr) (struct pex_obj *, int /* fd */, int /* binary */); + /* Get a FILE pointer to write to the file descriptor FD (only + called if PEX_USE_PIPES is set). If BINARY is non-zero, open in + binary mode. Arrange for FD not to be inherited by the child + processes. Return pointer on success, NULL on error. */ + FILE * (*fdopenw) (struct pex_obj *, int /* fd */, int /* binary */); /* Free any system dependent data associated with OBJ. May be NULL if there is nothing to do. */ void (*cleanup) (struct pex_obj *); diff --git a/libiberty/pex-djgpp.c b/libiberty/pex-djgpp.c index 6e58e3fd8dc..17fbf2cc7e4 100644 --- a/libiberty/pex-djgpp.c +++ b/libiberty/pex-djgpp.c @@ -62,6 +62,7 @@ const struct pex_funcs funcs = pex_djgpp_wait, NULL, /* pipe */ NULL, /* fdopenr */ + NULL, /* fdopenw */ NULL /* cleanup */ }; diff --git a/libiberty/pex-msdos.c b/libiberty/pex-msdos.c index 2256117d1bb..db22337aa2a 100644 --- a/libiberty/pex-msdos.c +++ b/libiberty/pex-msdos.c @@ -73,6 +73,7 @@ const struct pex_funcs funcs = pex_msdos_wait, NULL, /* pipe */ NULL, /* fdopenr */ + NULL, /* fdopenw */ pex_msdos_cleanup }; diff --git a/libiberty/pex-unix.c b/libiberty/pex-unix.c index 35a545cb17b..c92a4297971 100644 --- a/libiberty/pex-unix.c +++ b/libiberty/pex-unix.c @@ -277,6 +277,7 @@ 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 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. */ @@ -290,6 +291,7 @@ const struct pex_funcs funcs = pex_unix_wait, pex_unix_pipe, pex_unix_fdopenr, + pex_unix_fdopenw, pex_unix_cleanup }; @@ -495,6 +497,15 @@ pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, 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) { diff --git a/libiberty/pex-win32.c b/libiberty/pex-win32.c index ed45e5b8bb8..221e1b2f095 100644 --- a/libiberty/pex-win32.c +++ b/libiberty/pex-win32.c @@ -191,6 +191,7 @@ static int pex_win32_wait (struct pex_obj *, long, int *, struct pex_time *, int, const char **, int *); static int pex_win32_pipe (struct pex_obj *, int *, int); static FILE *pex_win32_fdopenr (struct pex_obj *, int, int); +static FILE *pex_win32_fdopenw (struct pex_obj *, int, int); /* The list of functions we pass to the common routines. */ @@ -203,6 +204,7 @@ const struct pex_funcs funcs = pex_win32_wait, pex_win32_pipe, pex_win32_fdopenr, + pex_win32_fdopenw, NULL /* cleanup */ }; @@ -702,6 +704,18 @@ pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, return fdopen (fd, binary ? "rb" : "r"); } +static FILE * +pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, + int binary) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + if (h == INVALID_HANDLE_VALUE) + return NULL; + if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0)) + return NULL; + return fdopen (fd, binary ? "wb" : "w"); +} + #ifdef MAIN #include diff --git a/libiberty/pexecute.txh b/libiberty/pexecute.txh index 461ff33d018..2bfad0c8b75 100644 --- a/libiberty/pexecute.txh +++ b/libiberty/pexecute.txh @@ -1,3 +1,4 @@ +@c -*- mode: texinfo -*- @deftypefn Extension {struct pex_obj *} pex_init (int @var{flags}, const char *@var{pname}, const char *@var{tempbase}) Prepare to execute one or more programs, with standard output of each @@ -129,6 +130,34 @@ value, or to 0 if there is no relevant @code{errno}. @end deftypefn +@deftypefn Extension {FILE *} pex_write_input (struct pex_obj *@var{obj}, int @var{binary}) + +Return a @code{FILE} pointer @var{fp} for the standard input of the +first program in the pipeline; @var{fp} is opened for writing. You +must have passed @code{PEX_USE_PIPES} to the @code{pex_init} call that +returned @var{obj}. You must close @var{fp} yourself with +@code{fclose} to indicate that the pipeline's input is complete. + +The file descriptor underlying @var{fp} is marked not to be inherited +by child processes. + +This call is not supported on systems which do not support pipes; it +returns with an error. (We could implement it by writing a temporary +file, but then you would need to write all your data and close +@var{fp} before your first call to @code{pex_run} --- and that +wouldn't work on systems that do support pipes: the pipe would fill +up, and you would block. So there isn't any easy way to conceal the +differences between the two types of systems.) + +If you call both @code{pex_write_input} and @code{pex_read_output}, be +careful to avoid deadlock. If the output pipe fills up, so that each +program in the pipeline is waiting for the next to read more data, and +you fill the input pipe by writing more data to @var{fp}, then there +is no way to make progress: the only process that could read data from +the output pipe is you, but you are blocked on the input pipe. + +@end deftypefn + @deftypefn Extension {FILE *} pex_read_output (struct pex_obj *@var{obj}, int @var{binary}) Returns a @code{FILE} pointer which may be used to read the standard -- cgit v1.2.1