From: Bruno Haible Date: Sat, 11 Dec 2010 01:00:44 +0000 (+0100) Subject: Rename module 'pipe' to 'spawn-pipe'. X-Git-Tag: v0.1~3563 X-Git-Url: http://erislabs.org.uk/gitweb/?a=commitdiff_plain;h=8ecf1d24ee3fae764eaed003e615269787aeb8e3;p=gnulib.git Rename module 'pipe' to 'spawn-pipe'. * modules/spawn-pipe: New file, renamed from modules/pipe. (Files, configure.ac, Makefile.am): Update. (Include): Mention "spawn-pipe.h" instead of "pipe.h". * modules/pipe: Reduce to an obsolete indirection to 'spawn-pipe'. * lib/spawn-pipe.h: New file, renamed from lib/pipe.h. * lib/spawn-pipe.c: New file, renamed from lib/pipe.c. Include "spawn-pipe.h" instead of "pipe.h". * m4/spawn-pipe.m4: New file, renamed from m4/pipe.m4. Rename gl_PIPE to gl_SPAWN_PIPE. * modules/spawn-pipe-tests: New file, renamed from modules/pipe-tests. (Files, Makefile.am): Update. * tests/test-spawn-pipe.sh: New file, renamed from tests/test-pipe.sh. Update. * tests/test-spawn-pipe.c: New file, renamed from tests/test-pipe.c. Include "spawn-pipe.h" instead of "pipe.h". * lib/csharpcomp.c: Include "spawn-pipe.h" instead of "pipe.h". * lib/javacomp.c: Likewise. * lib/javaversion.c: Likewise. * lib/pipe-filter-gi.c: Likewise. * lib/pipe-filter-ii.c: Likewise. * modules/csharpcomp (Depends-on): Add 'spawn-pipe', remove 'pipe'. * modules/javacomp (Depends-on): Likewise. * modules/javaversion (Depends-on): Likewise. * modules/pipe-filter-gi (Depends-on): Likewise. * modules/pipe-filter-ii (Depends-on): Likewise. * MODULES.html.sh (Executing programs): Update. * NEWS: Mention the change. --- diff --git a/ChangeLog b/ChangeLog index 29849ba0d..137fc346d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2010-12-10 Bruno Haible + + Rename module 'pipe' to 'spawn-pipe'. + * modules/spawn-pipe: New file, renamed from modules/pipe. + (Files, configure.ac, Makefile.am): Update. + (Include): Mention "spawn-pipe.h" instead of "pipe.h". + * modules/pipe: Reduce to an obsolete indirection to 'spawn-pipe'. + * lib/spawn-pipe.h: New file, renamed from lib/pipe.h. + * lib/spawn-pipe.c: New file, renamed from lib/pipe.c. Include + "spawn-pipe.h" instead of "pipe.h". + * m4/spawn-pipe.m4: New file, renamed from m4/pipe.m4. Rename gl_PIPE + to gl_SPAWN_PIPE. + * modules/spawn-pipe-tests: New file, renamed from modules/pipe-tests. + (Files, Makefile.am): Update. + * tests/test-spawn-pipe.sh: New file, renamed from tests/test-pipe.sh. + Update. + * tests/test-spawn-pipe.c: New file, renamed from tests/test-pipe.c. + Include "spawn-pipe.h" instead of "pipe.h". + * lib/csharpcomp.c: Include "spawn-pipe.h" instead of "pipe.h". + * lib/javacomp.c: Likewise. + * lib/javaversion.c: Likewise. + * lib/pipe-filter-gi.c: Likewise. + * lib/pipe-filter-ii.c: Likewise. + * modules/csharpcomp (Depends-on): Add 'spawn-pipe', remove 'pipe'. + * modules/javacomp (Depends-on): Likewise. + * modules/javaversion (Depends-on): Likewise. + * modules/pipe-filter-gi (Depends-on): Likewise. + * modules/pipe-filter-ii (Depends-on): Likewise. + * MODULES.html.sh (Executing programs): Update. + * NEWS: Mention the change. + 2010-12-10 Eric Blake pipe-posix: new module diff --git a/MODULES.html.sh b/MODULES.html.sh index 456497fad..aee5c24dc 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -3324,7 +3324,7 @@ func_all_modules () func_module findprog-lgpl func_module wait-process func_module execute - func_module pipe + func_module spawn-pipe func_module pipe-filter-gi func_module pipe-filter-ii func_module sh-quote diff --git a/NEWS b/NEWS index 76239ddb0..49a44ec95 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ User visible incompatible changes Date Modules Changes +2010-12-10 pipe This module is renamed to spawn-pipe. The include + file is renamed to "spawn-pipe.h". + 2010-10-05 getdate This module is deprecated. Please use the new parse-datetime module for the replacement function parse_datetime(), or help us write diff --git a/lib/csharpcomp.c b/lib/csharpcomp.c index 8f791516a..b4d659040 100644 --- a/lib/csharpcomp.c +++ b/lib/csharpcomp.c @@ -27,7 +27,7 @@ #include #include "execute.h" -#include "pipe.h" +#include "spawn-pipe.h" #include "wait-process.h" #include "sh-quote.h" #include "safe-read.h" diff --git a/lib/javacomp.c b/lib/javacomp.c index 0bd5ec5d6..fc3365214 100644 --- a/lib/javacomp.c +++ b/lib/javacomp.c @@ -32,7 +32,7 @@ #include "javaversion.h" #include "execute.h" -#include "pipe.h" +#include "spawn-pipe.h" #include "wait-process.h" #include "classpath.h" #include "xsetenv.h" diff --git a/lib/javaversion.c b/lib/javaversion.c index 7d76539d1..b7fc4acf0 100644 --- a/lib/javaversion.c +++ b/lib/javaversion.c @@ -31,7 +31,7 @@ #endif #include "javaexec.h" -#include "pipe.h" +#include "spawn-pipe.h" #include "wait-process.h" #include "error.h" #include "gettext.h" diff --git a/lib/pipe-filter-gi.c b/lib/pipe-filter-gi.c index 19521836a..85a8d33c6 100644 --- a/lib/pipe-filter-gi.c +++ b/lib/pipe-filter-gi.c @@ -34,7 +34,7 @@ #endif #include "error.h" -#include "pipe.h" +#include "spawn-pipe.h" #include "wait-process.h" #include "xalloc.h" #include "gettext.h" diff --git a/lib/pipe-filter-ii.c b/lib/pipe-filter-ii.c index b25662d0e..93dfbc12f 100644 --- a/lib/pipe-filter-ii.c +++ b/lib/pipe-filter-ii.c @@ -33,7 +33,7 @@ #endif #include "error.h" -#include "pipe.h" +#include "spawn-pipe.h" #include "wait-process.h" #include "gettext.h" diff --git a/lib/pipe.c b/lib/pipe.c deleted file mode 100644 index 1c81d1452..000000000 --- a/lib/pipe.c +++ /dev/null @@ -1,450 +0,0 @@ -/* Creation of subprocesses, communicating via pipes. - Copyright (C) 2001-2004, 2006-2010 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - - -#include - -/* Specification. */ -#include "pipe.h" - -#include -#include -#include -#include -#include - -#include "error.h" -#include "fatal-signal.h" -#include "unistd-safer.h" -#include "wait-process.h" -#include "gettext.h" - -#define _(str) gettext (str) - -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - -/* Native Woe32 API. */ -# include -# include "w32spawn.h" - -#else - -/* Unix API. */ -# include - -#endif - -/* The results of open() in this file are not used with fchdir, - therefore save some unnecessary work in fchdir.c. */ -#undef open -#undef close - - -#ifdef EINTR - -/* EINTR handling for close(). - These functions can return -1/EINTR even though we don't have any - signal handlers set up, namely when we get interrupted via SIGSTOP. */ - -static inline int -nonintr_close (int fd) -{ - int retval; - - do - retval = close (fd); - while (retval < 0 && errno == EINTR); - - return retval; -} -#define close nonintr_close - -static inline int -nonintr_open (const char *pathname, int oflag, mode_t mode) -{ - int retval; - - do - retval = open (pathname, oflag, mode); - while (retval < 0 && errno == EINTR); - - return retval; -} -#undef open /* avoid warning on VMS */ -#define open nonintr_open - -#endif - - -/* Open a pipe connected to a child process. - * - * write system read - * parent -> fd[1] -> STDIN_FILENO -> child if pipe_stdin - * parent <- fd[0] <- STDOUT_FILENO <- child if pipe_stdout - * read system write - * - * At least one of pipe_stdin, pipe_stdout must be true. - * pipe_stdin and prog_stdin together determine the child's standard input. - * pipe_stdout and prog_stdout together determine the child's standard output. - * If pipe_stdin is true, prog_stdin is ignored. - * If pipe_stdout is true, prog_stdout is ignored. - */ -static pid_t -create_pipe (const char *progname, - const char *prog_path, char **prog_argv, - bool pipe_stdin, bool pipe_stdout, - const char *prog_stdin, const char *prog_stdout, - bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[2]) -{ -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - - /* Native Woe32 API. - This uses _pipe(), dup2(), and spawnv(). It could also be implemented - using the low-level functions CreatePipe(), DuplicateHandle(), - CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp - and cvs source code. */ - int ifd[2]; - int ofd[2]; - int orig_stdin; - int orig_stdout; - int orig_stderr; - int child; - int nulloutfd; - int stdinfd; - int stdoutfd; - int saved_errno; - - /* FIXME: Need to free memory allocated by prepare_spawn. */ - prog_argv = prepare_spawn (prog_argv); - - if (pipe_stdout) - if (pipe2_safer (ifd, O_BINARY | O_CLOEXEC) < 0) - error (EXIT_FAILURE, errno, _("cannot create pipe")); - if (pipe_stdin) - if (pipe2_safer (ofd, O_BINARY | O_CLOEXEC) < 0) - error (EXIT_FAILURE, errno, _("cannot create pipe")); -/* Data flow diagram: - * - * write system read - * parent -> ofd[1] -> ofd[0] -> child if pipe_stdin - * parent <- ifd[0] <- ifd[1] <- child if pipe_stdout - * read system write - * - */ - - /* Save standard file handles of parent process. */ - if (pipe_stdin || prog_stdin != NULL) - orig_stdin = dup_safer_noinherit (STDIN_FILENO); - if (pipe_stdout || prog_stdout != NULL) - orig_stdout = dup_safer_noinherit (STDOUT_FILENO); - if (null_stderr) - orig_stderr = dup_safer_noinherit (STDERR_FILENO); - child = -1; - - /* Create standard file handles of child process. */ - nulloutfd = -1; - stdinfd = -1; - stdoutfd = -1; - if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0) - && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0) - && (!null_stderr - || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0 - && (nulloutfd == STDERR_FILENO - || (dup2 (nulloutfd, STDERR_FILENO) >= 0 - && close (nulloutfd) >= 0)))) - && (pipe_stdin - || prog_stdin == NULL - || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0 - && (stdinfd == STDIN_FILENO - || (dup2 (stdinfd, STDIN_FILENO) >= 0 - && close (stdinfd) >= 0)))) - && (pipe_stdout - || prog_stdout == NULL - || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0 - && (stdoutfd == STDOUT_FILENO - || (dup2 (stdoutfd, STDOUT_FILENO) >= 0 - && close (stdoutfd) >= 0))))) - /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1], - but it inherits all open()ed or dup2()ed file handles (which is what - we want in the case of STD*_FILENO). */ - /* Use spawnvpe and pass the environment explicitly. This is needed if - the program has modified the environment using putenv() or [un]setenv(). - On Windows, programs have two environments, one in the "environment - block" of the process and managed through SetEnvironmentVariable(), and - one inside the process, in the location retrieved by the 'environ' - macro. When using spawnvp() without 'e', the child process inherits a - copy of the environment block - ignoring the effects of putenv() and - [un]setenv(). */ - { - child = spawnvpe (P_NOWAIT, prog_path, (const char **) prog_argv, - (const char **) environ); - if (child < 0 && errno == ENOEXEC) - { - /* prog is not an native executable. Try to execute it as a - shell script. Note that prepare_spawn() has already prepended - a hidden element "sh.exe" to prog_argv. */ - --prog_argv; - child = spawnvpe (P_NOWAIT, prog_argv[0], (const char **) prog_argv, - (const char **) environ); - } - } - if (child == -1) - saved_errno = errno; - if (stdinfd >= 0) - close (stdinfd); - if (stdoutfd >= 0) - close (stdoutfd); - if (nulloutfd >= 0) - close (nulloutfd); - - /* Restore standard file handles of parent process. */ - if (null_stderr) - undup_safer_noinherit (orig_stderr, STDERR_FILENO); - if (pipe_stdout || prog_stdout != NULL) - undup_safer_noinherit (orig_stdout, STDOUT_FILENO); - if (pipe_stdin || prog_stdin != NULL) - undup_safer_noinherit (orig_stdin, STDIN_FILENO); - - if (pipe_stdin) - close (ofd[0]); - if (pipe_stdout) - close (ifd[1]); - if (child == -1) - { - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, - _("%s subprocess failed"), progname); - if (pipe_stdout) - close (ifd[0]); - if (pipe_stdin) - close (ofd[1]); - errno = saved_errno; - return -1; - } - - if (pipe_stdout) - fd[0] = ifd[0]; - if (pipe_stdin) - fd[1] = ofd[1]; - return child; - -#else - - /* Unix API. */ - int ifd[2]; - int ofd[2]; - sigset_t blocked_signals; - posix_spawn_file_actions_t actions; - bool actions_allocated; - posix_spawnattr_t attrs; - bool attrs_allocated; - int err; - pid_t child; - - if (pipe_stdout) - if (pipe_safer (ifd) < 0) - error (EXIT_FAILURE, errno, _("cannot create pipe")); - if (pipe_stdin) - if (pipe_safer (ofd) < 0) - error (EXIT_FAILURE, errno, _("cannot create pipe")); -/* Data flow diagram: - * - * write system read - * parent -> ofd[1] -> ofd[0] -> child if pipe_stdin - * parent <- ifd[0] <- ifd[1] <- child if pipe_stdout - * read system write - * - */ - - if (slave_process) - { - sigprocmask (SIG_SETMASK, NULL, &blocked_signals); - block_fatal_signals (); - } - actions_allocated = false; - attrs_allocated = false; - if ((err = posix_spawn_file_actions_init (&actions)) != 0 - || (actions_allocated = true, - (pipe_stdin - && (err = posix_spawn_file_actions_adddup2 (&actions, - ofd[0], STDIN_FILENO)) - != 0) - || (pipe_stdout - && (err = posix_spawn_file_actions_adddup2 (&actions, - ifd[1], STDOUT_FILENO)) - != 0) - || (pipe_stdin - && (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) - != 0) - || (pipe_stdout - && (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) - != 0) - || (pipe_stdin - && (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) - != 0) - || (pipe_stdout - && (err = posix_spawn_file_actions_addclose (&actions, ifd[0])) - != 0) - || (null_stderr - && (err = posix_spawn_file_actions_addopen (&actions, - STDERR_FILENO, - "/dev/null", O_RDWR, - 0)) - != 0) - || (!pipe_stdin - && prog_stdin != NULL - && (err = posix_spawn_file_actions_addopen (&actions, - STDIN_FILENO, - prog_stdin, O_RDONLY, - 0)) - != 0) - || (!pipe_stdout - && prog_stdout != NULL - && (err = posix_spawn_file_actions_addopen (&actions, - STDOUT_FILENO, - prog_stdout, O_WRONLY, - 0)) - != 0) - || (slave_process - && ((err = posix_spawnattr_init (&attrs)) != 0 - || (attrs_allocated = true, - (err = posix_spawnattr_setsigmask (&attrs, - &blocked_signals)) - != 0 - || (err = posix_spawnattr_setflags (&attrs, - POSIX_SPAWN_SETSIGMASK)) - != 0))) - || (err = posix_spawnp (&child, prog_path, &actions, - attrs_allocated ? &attrs : NULL, prog_argv, - environ)) - != 0)) - { - if (actions_allocated) - posix_spawn_file_actions_destroy (&actions); - if (attrs_allocated) - posix_spawnattr_destroy (&attrs); - if (slave_process) - unblock_fatal_signals (); - if (exit_on_error || !null_stderr) - error (exit_on_error ? EXIT_FAILURE : 0, err, - _("%s subprocess failed"), progname); - if (pipe_stdout) - { - close (ifd[0]); - close (ifd[1]); - } - if (pipe_stdin) - { - close (ofd[0]); - close (ofd[1]); - } - errno = err; - return -1; - } - posix_spawn_file_actions_destroy (&actions); - if (attrs_allocated) - posix_spawnattr_destroy (&attrs); - if (slave_process) - { - register_slave_subprocess (child); - unblock_fatal_signals (); - } - if (pipe_stdin) - close (ofd[0]); - if (pipe_stdout) - close (ifd[1]); - - if (pipe_stdout) - fd[0] = ifd[0]; - if (pipe_stdin) - fd[1] = ofd[1]; - return child; - -#endif -} - -/* Open a bidirectional pipe. - * - * write system read - * parent -> fd[1] -> STDIN_FILENO -> child - * parent <- fd[0] <- STDOUT_FILENO <- child - * read system write - * - */ -pid_t -create_pipe_bidi (const char *progname, - const char *prog_path, char **prog_argv, - bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[2]) -{ - pid_t result = create_pipe (progname, prog_path, prog_argv, - true, true, NULL, NULL, - null_stderr, slave_process, exit_on_error, - fd); - return result; -} - -/* Open a pipe for input from a child process. - * The child's stdin comes from a file. - * - * read system write - * parent <- fd[0] <- STDOUT_FILENO <- child - * - */ -pid_t -create_pipe_in (const char *progname, - const char *prog_path, char **prog_argv, - const char *prog_stdin, bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[1]) -{ - int iofd[2]; - pid_t result = create_pipe (progname, prog_path, prog_argv, - false, true, prog_stdin, NULL, - null_stderr, slave_process, exit_on_error, - iofd); - if (result != -1) - fd[0] = iofd[0]; - return result; -} - -/* Open a pipe for output to a child process. - * The child's stdout goes to a file. - * - * write system read - * parent -> fd[0] -> STDIN_FILENO -> child - * - */ -pid_t -create_pipe_out (const char *progname, - const char *prog_path, char **prog_argv, - const char *prog_stdout, bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[1]) -{ - int iofd[2]; - pid_t result = create_pipe (progname, prog_path, prog_argv, - true, false, NULL, prog_stdout, - null_stderr, slave_process, exit_on_error, - iofd); - if (result != -1) - fd[0] = iofd[1]; - return result; -} diff --git a/lib/pipe.h b/lib/pipe.h deleted file mode 100644 index 7517892ba..000000000 --- a/lib/pipe.h +++ /dev/null @@ -1,147 +0,0 @@ -/* Creation of subprocesses, communicating via pipes. - Copyright (C) 2001-2003, 2006, 2008-2010 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _PIPE_H -#define _PIPE_H - -/* Get pid_t. */ -#include -#include -#include - -#include - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* All these functions create a subprocess and don't wait for its termination. - They return the process id of the subprocess. They also return in fd[] - one or two file descriptors for communication with the subprocess. - If the subprocess creation fails: if exit_on_error is true, the main - process exits with an error message; otherwise, an error message is given - if null_stderr is false, then -1 is returned, with errno set, and fd[] - remain uninitialized. - - After finishing communication, the caller should call wait_subprocess() - to get rid of the subprocess in the process table. - - If slave_process is true, the child process will be terminated when its - creator receives a catchable fatal signal or exits normally. If - slave_process is false, the child process will continue running in this - case, until it is lucky enough to attempt to communicate with its creator - and thus get a SIGPIPE signal. - - If exit_on_error is false, a child process id of -1 should be treated the - same way as a subprocess which accepts no input, produces no output and - terminates with exit code 127. Why? Some errors during posix_spawnp() - cause the function posix_spawnp() to return an error code; some other - errors cause the subprocess to exit with return code 127. It is - implementation dependent which error is reported which way. The caller - must treat both cases as equivalent. - - It is recommended that no signal is blocked or ignored (i.e. have a - signal handler with value SIG_IGN) while any of these functions is called. - The reason is that child processes inherit the mask of blocked signals - from their parent (both through posix_spawn() and fork()/exec()); - likewise, signals ignored in the parent are also ignored in the child - (except possibly for SIGCHLD). And POSIX:2001 says [in the description - of exec()]: - "it should be noted that many existing applications wrongly - assume that they start with certain signals set to the default - action and/or unblocked. In particular, applications written - with a simpler signal model that does not include blocking of - signals, such as the one in the ISO C standard, may not behave - properly if invoked with some signals blocked. Therefore, it is - best not to block or ignore signals across execs without explicit - reason to do so, and especially not to block signals across execs - of arbitrary (not closely co-operating) programs." */ - -/* Open a pipe for output to a child process. - * The child's stdout goes to a file. - * - * write system read - * parent -> fd[0] -> STDIN_FILENO -> child - * - * Note: When writing to a child process, it is useful to ignore the SIGPIPE - * signal and the EPIPE error code. - */ -extern pid_t create_pipe_out (const char *progname, - const char *prog_path, char **prog_argv, - const char *prog_stdout, bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[1]); - -/* Open a pipe for input from a child process. - * The child's stdin comes from a file. - * - * read system write - * parent <- fd[0] <- STDOUT_FILENO <- child - * - */ -extern pid_t create_pipe_in (const char *progname, - const char *prog_path, char **prog_argv, - const char *prog_stdin, bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[1]); - -/* Open a bidirectional pipe. - * - * write system read - * parent -> fd[1] -> STDIN_FILENO -> child - * parent <- fd[0] <- STDOUT_FILENO <- child - * read system write - * - * Note: When writing to a child process, it is useful to ignore the SIGPIPE - * signal and the EPIPE error code. - * - * Note: The parent process must be careful to avoid deadlock. - * 1) If you write more than PIPE_MAX bytes or, more generally, if you write - * more bytes than the subprocess can handle at once, the subprocess - * may write its data and wait on you to read it, but you are currently - * busy writing. - * 2) When you don't know ahead of time how many bytes the subprocess - * will produce, the usual technique of calling read (fd, buf, BUFSIZ) - * with a fixed BUFSIZ will, on Linux 2.2.17 and on BSD systems, cause - * the read() call to block until *all* of the buffer has been filled. - * But the subprocess cannot produce more data until you gave it more - * input. But you are currently busy reading from it. - */ -extern pid_t create_pipe_bidi (const char *progname, - const char *prog_path, char **prog_argv, - bool null_stderr, - bool slave_process, bool exit_on_error, - int fd[2]); - -/* The name of the "always silent" device. */ -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ -/* Native Woe32 API. */ -# define DEV_NULL "NUL" -#else -/* Unix API. */ -# define DEV_NULL "/dev/null" -#endif - - -#ifdef __cplusplus -} -#endif - - -#endif /* _PIPE_H */ diff --git a/lib/spawn-pipe.c b/lib/spawn-pipe.c new file mode 100644 index 000000000..809cccf41 --- /dev/null +++ b/lib/spawn-pipe.c @@ -0,0 +1,450 @@ +/* Creation of subprocesses, communicating via pipes. + Copyright (C) 2001-2004, 2006-2010 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +#include + +/* Specification. */ +#include "spawn-pipe.h" + +#include +#include +#include +#include +#include + +#include "error.h" +#include "fatal-signal.h" +#include "unistd-safer.h" +#include "wait-process.h" +#include "gettext.h" + +#define _(str) gettext (str) + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + +/* Native Woe32 API. */ +# include +# include "w32spawn.h" + +#else + +/* Unix API. */ +# include + +#endif + +/* The results of open() in this file are not used with fchdir, + therefore save some unnecessary work in fchdir.c. */ +#undef open +#undef close + + +#ifdef EINTR + +/* EINTR handling for close(). + These functions can return -1/EINTR even though we don't have any + signal handlers set up, namely when we get interrupted via SIGSTOP. */ + +static inline int +nonintr_close (int fd) +{ + int retval; + + do + retval = close (fd); + while (retval < 0 && errno == EINTR); + + return retval; +} +#define close nonintr_close + +static inline int +nonintr_open (const char *pathname, int oflag, mode_t mode) +{ + int retval; + + do + retval = open (pathname, oflag, mode); + while (retval < 0 && errno == EINTR); + + return retval; +} +#undef open /* avoid warning on VMS */ +#define open nonintr_open + +#endif + + +/* Open a pipe connected to a child process. + * + * write system read + * parent -> fd[1] -> STDIN_FILENO -> child if pipe_stdin + * parent <- fd[0] <- STDOUT_FILENO <- child if pipe_stdout + * read system write + * + * At least one of pipe_stdin, pipe_stdout must be true. + * pipe_stdin and prog_stdin together determine the child's standard input. + * pipe_stdout and prog_stdout together determine the child's standard output. + * If pipe_stdin is true, prog_stdin is ignored. + * If pipe_stdout is true, prog_stdout is ignored. + */ +static pid_t +create_pipe (const char *progname, + const char *prog_path, char **prog_argv, + bool pipe_stdin, bool pipe_stdout, + const char *prog_stdin, const char *prog_stdout, + bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[2]) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + + /* Native Woe32 API. + This uses _pipe(), dup2(), and spawnv(). It could also be implemented + using the low-level functions CreatePipe(), DuplicateHandle(), + CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp + and cvs source code. */ + int ifd[2]; + int ofd[2]; + int orig_stdin; + int orig_stdout; + int orig_stderr; + int child; + int nulloutfd; + int stdinfd; + int stdoutfd; + int saved_errno; + + /* FIXME: Need to free memory allocated by prepare_spawn. */ + prog_argv = prepare_spawn (prog_argv); + + if (pipe_stdout) + if (pipe2_safer (ifd, O_BINARY | O_CLOEXEC) < 0) + error (EXIT_FAILURE, errno, _("cannot create pipe")); + if (pipe_stdin) + if (pipe2_safer (ofd, O_BINARY | O_CLOEXEC) < 0) + error (EXIT_FAILURE, errno, _("cannot create pipe")); +/* Data flow diagram: + * + * write system read + * parent -> ofd[1] -> ofd[0] -> child if pipe_stdin + * parent <- ifd[0] <- ifd[1] <- child if pipe_stdout + * read system write + * + */ + + /* Save standard file handles of parent process. */ + if (pipe_stdin || prog_stdin != NULL) + orig_stdin = dup_safer_noinherit (STDIN_FILENO); + if (pipe_stdout || prog_stdout != NULL) + orig_stdout = dup_safer_noinherit (STDOUT_FILENO); + if (null_stderr) + orig_stderr = dup_safer_noinherit (STDERR_FILENO); + child = -1; + + /* Create standard file handles of child process. */ + nulloutfd = -1; + stdinfd = -1; + stdoutfd = -1; + if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0) + && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0) + && (!null_stderr + || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0 + && (nulloutfd == STDERR_FILENO + || (dup2 (nulloutfd, STDERR_FILENO) >= 0 + && close (nulloutfd) >= 0)))) + && (pipe_stdin + || prog_stdin == NULL + || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0 + && (stdinfd == STDIN_FILENO + || (dup2 (stdinfd, STDIN_FILENO) >= 0 + && close (stdinfd) >= 0)))) + && (pipe_stdout + || prog_stdout == NULL + || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0 + && (stdoutfd == STDOUT_FILENO + || (dup2 (stdoutfd, STDOUT_FILENO) >= 0 + && close (stdoutfd) >= 0))))) + /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1], + but it inherits all open()ed or dup2()ed file handles (which is what + we want in the case of STD*_FILENO). */ + /* Use spawnvpe and pass the environment explicitly. This is needed if + the program has modified the environment using putenv() or [un]setenv(). + On Windows, programs have two environments, one in the "environment + block" of the process and managed through SetEnvironmentVariable(), and + one inside the process, in the location retrieved by the 'environ' + macro. When using spawnvp() without 'e', the child process inherits a + copy of the environment block - ignoring the effects of putenv() and + [un]setenv(). */ + { + child = spawnvpe (P_NOWAIT, prog_path, (const char **) prog_argv, + (const char **) environ); + if (child < 0 && errno == ENOEXEC) + { + /* prog is not an native executable. Try to execute it as a + shell script. Note that prepare_spawn() has already prepended + a hidden element "sh.exe" to prog_argv. */ + --prog_argv; + child = spawnvpe (P_NOWAIT, prog_argv[0], (const char **) prog_argv, + (const char **) environ); + } + } + if (child == -1) + saved_errno = errno; + if (stdinfd >= 0) + close (stdinfd); + if (stdoutfd >= 0) + close (stdoutfd); + if (nulloutfd >= 0) + close (nulloutfd); + + /* Restore standard file handles of parent process. */ + if (null_stderr) + undup_safer_noinherit (orig_stderr, STDERR_FILENO); + if (pipe_stdout || prog_stdout != NULL) + undup_safer_noinherit (orig_stdout, STDOUT_FILENO); + if (pipe_stdin || prog_stdin != NULL) + undup_safer_noinherit (orig_stdin, STDIN_FILENO); + + if (pipe_stdin) + close (ofd[0]); + if (pipe_stdout) + close (ifd[1]); + if (child == -1) + { + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, + _("%s subprocess failed"), progname); + if (pipe_stdout) + close (ifd[0]); + if (pipe_stdin) + close (ofd[1]); + errno = saved_errno; + return -1; + } + + if (pipe_stdout) + fd[0] = ifd[0]; + if (pipe_stdin) + fd[1] = ofd[1]; + return child; + +#else + + /* Unix API. */ + int ifd[2]; + int ofd[2]; + sigset_t blocked_signals; + posix_spawn_file_actions_t actions; + bool actions_allocated; + posix_spawnattr_t attrs; + bool attrs_allocated; + int err; + pid_t child; + + if (pipe_stdout) + if (pipe_safer (ifd) < 0) + error (EXIT_FAILURE, errno, _("cannot create pipe")); + if (pipe_stdin) + if (pipe_safer (ofd) < 0) + error (EXIT_FAILURE, errno, _("cannot create pipe")); +/* Data flow diagram: + * + * write system read + * parent -> ofd[1] -> ofd[0] -> child if pipe_stdin + * parent <- ifd[0] <- ifd[1] <- child if pipe_stdout + * read system write + * + */ + + if (slave_process) + { + sigprocmask (SIG_SETMASK, NULL, &blocked_signals); + block_fatal_signals (); + } + actions_allocated = false; + attrs_allocated = false; + if ((err = posix_spawn_file_actions_init (&actions)) != 0 + || (actions_allocated = true, + (pipe_stdin + && (err = posix_spawn_file_actions_adddup2 (&actions, + ofd[0], STDIN_FILENO)) + != 0) + || (pipe_stdout + && (err = posix_spawn_file_actions_adddup2 (&actions, + ifd[1], STDOUT_FILENO)) + != 0) + || (pipe_stdin + && (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) + != 0) + || (pipe_stdout + && (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) + != 0) + || (pipe_stdin + && (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) + != 0) + || (pipe_stdout + && (err = posix_spawn_file_actions_addclose (&actions, ifd[0])) + != 0) + || (null_stderr + && (err = posix_spawn_file_actions_addopen (&actions, + STDERR_FILENO, + "/dev/null", O_RDWR, + 0)) + != 0) + || (!pipe_stdin + && prog_stdin != NULL + && (err = posix_spawn_file_actions_addopen (&actions, + STDIN_FILENO, + prog_stdin, O_RDONLY, + 0)) + != 0) + || (!pipe_stdout + && prog_stdout != NULL + && (err = posix_spawn_file_actions_addopen (&actions, + STDOUT_FILENO, + prog_stdout, O_WRONLY, + 0)) + != 0) + || (slave_process + && ((err = posix_spawnattr_init (&attrs)) != 0 + || (attrs_allocated = true, + (err = posix_spawnattr_setsigmask (&attrs, + &blocked_signals)) + != 0 + || (err = posix_spawnattr_setflags (&attrs, + POSIX_SPAWN_SETSIGMASK)) + != 0))) + || (err = posix_spawnp (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ)) + != 0)) + { + if (actions_allocated) + posix_spawn_file_actions_destroy (&actions); + if (attrs_allocated) + posix_spawnattr_destroy (&attrs); + if (slave_process) + unblock_fatal_signals (); + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, err, + _("%s subprocess failed"), progname); + if (pipe_stdout) + { + close (ifd[0]); + close (ifd[1]); + } + if (pipe_stdin) + { + close (ofd[0]); + close (ofd[1]); + } + errno = err; + return -1; + } + posix_spawn_file_actions_destroy (&actions); + if (attrs_allocated) + posix_spawnattr_destroy (&attrs); + if (slave_process) + { + register_slave_subprocess (child); + unblock_fatal_signals (); + } + if (pipe_stdin) + close (ofd[0]); + if (pipe_stdout) + close (ifd[1]); + + if (pipe_stdout) + fd[0] = ifd[0]; + if (pipe_stdin) + fd[1] = ofd[1]; + return child; + +#endif +} + +/* Open a bidirectional pipe. + * + * write system read + * parent -> fd[1] -> STDIN_FILENO -> child + * parent <- fd[0] <- STDOUT_FILENO <- child + * read system write + * + */ +pid_t +create_pipe_bidi (const char *progname, + const char *prog_path, char **prog_argv, + bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[2]) +{ + pid_t result = create_pipe (progname, prog_path, prog_argv, + true, true, NULL, NULL, + null_stderr, slave_process, exit_on_error, + fd); + return result; +} + +/* Open a pipe for input from a child process. + * The child's stdin comes from a file. + * + * read system write + * parent <- fd[0] <- STDOUT_FILENO <- child + * + */ +pid_t +create_pipe_in (const char *progname, + const char *prog_path, char **prog_argv, + const char *prog_stdin, bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[1]) +{ + int iofd[2]; + pid_t result = create_pipe (progname, prog_path, prog_argv, + false, true, prog_stdin, NULL, + null_stderr, slave_process, exit_on_error, + iofd); + if (result != -1) + fd[0] = iofd[0]; + return result; +} + +/* Open a pipe for output to a child process. + * The child's stdout goes to a file. + * + * write system read + * parent -> fd[0] -> STDIN_FILENO -> child + * + */ +pid_t +create_pipe_out (const char *progname, + const char *prog_path, char **prog_argv, + const char *prog_stdout, bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[1]) +{ + int iofd[2]; + pid_t result = create_pipe (progname, prog_path, prog_argv, + true, false, NULL, prog_stdout, + null_stderr, slave_process, exit_on_error, + iofd); + if (result != -1) + fd[0] = iofd[1]; + return result; +} diff --git a/lib/spawn-pipe.h b/lib/spawn-pipe.h new file mode 100644 index 000000000..449583dc4 --- /dev/null +++ b/lib/spawn-pipe.h @@ -0,0 +1,147 @@ +/* Creation of subprocesses, communicating via pipes. + Copyright (C) 2001-2003, 2006, 2008-2010 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _SPAWN_PIPE_H +#define _SPAWN_PIPE_H + +/* Get pid_t. */ +#include +#include +#include + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* All these functions create a subprocess and don't wait for its termination. + They return the process id of the subprocess. They also return in fd[] + one or two file descriptors for communication with the subprocess. + If the subprocess creation fails: if exit_on_error is true, the main + process exits with an error message; otherwise, an error message is given + if null_stderr is false, then -1 is returned, with errno set, and fd[] + remain uninitialized. + + After finishing communication, the caller should call wait_subprocess() + to get rid of the subprocess in the process table. + + If slave_process is true, the child process will be terminated when its + creator receives a catchable fatal signal or exits normally. If + slave_process is false, the child process will continue running in this + case, until it is lucky enough to attempt to communicate with its creator + and thus get a SIGPIPE signal. + + If exit_on_error is false, a child process id of -1 should be treated the + same way as a subprocess which accepts no input, produces no output and + terminates with exit code 127. Why? Some errors during posix_spawnp() + cause the function posix_spawnp() to return an error code; some other + errors cause the subprocess to exit with return code 127. It is + implementation dependent which error is reported which way. The caller + must treat both cases as equivalent. + + It is recommended that no signal is blocked or ignored (i.e. have a + signal handler with value SIG_IGN) while any of these functions is called. + The reason is that child processes inherit the mask of blocked signals + from their parent (both through posix_spawn() and fork()/exec()); + likewise, signals ignored in the parent are also ignored in the child + (except possibly for SIGCHLD). And POSIX:2001 says [in the description + of exec()]: + "it should be noted that many existing applications wrongly + assume that they start with certain signals set to the default + action and/or unblocked. In particular, applications written + with a simpler signal model that does not include blocking of + signals, such as the one in the ISO C standard, may not behave + properly if invoked with some signals blocked. Therefore, it is + best not to block or ignore signals across execs without explicit + reason to do so, and especially not to block signals across execs + of arbitrary (not closely co-operating) programs." */ + +/* Open a pipe for output to a child process. + * The child's stdout goes to a file. + * + * write system read + * parent -> fd[0] -> STDIN_FILENO -> child + * + * Note: When writing to a child process, it is useful to ignore the SIGPIPE + * signal and the EPIPE error code. + */ +extern pid_t create_pipe_out (const char *progname, + const char *prog_path, char **prog_argv, + const char *prog_stdout, bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[1]); + +/* Open a pipe for input from a child process. + * The child's stdin comes from a file. + * + * read system write + * parent <- fd[0] <- STDOUT_FILENO <- child + * + */ +extern pid_t create_pipe_in (const char *progname, + const char *prog_path, char **prog_argv, + const char *prog_stdin, bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[1]); + +/* Open a bidirectional pipe. + * + * write system read + * parent -> fd[1] -> STDIN_FILENO -> child + * parent <- fd[0] <- STDOUT_FILENO <- child + * read system write + * + * Note: When writing to a child process, it is useful to ignore the SIGPIPE + * signal and the EPIPE error code. + * + * Note: The parent process must be careful to avoid deadlock. + * 1) If you write more than PIPE_MAX bytes or, more generally, if you write + * more bytes than the subprocess can handle at once, the subprocess + * may write its data and wait on you to read it, but you are currently + * busy writing. + * 2) When you don't know ahead of time how many bytes the subprocess + * will produce, the usual technique of calling read (fd, buf, BUFSIZ) + * with a fixed BUFSIZ will, on Linux 2.2.17 and on BSD systems, cause + * the read() call to block until *all* of the buffer has been filled. + * But the subprocess cannot produce more data until you gave it more + * input. But you are currently busy reading from it. + */ +extern pid_t create_pipe_bidi (const char *progname, + const char *prog_path, char **prog_argv, + bool null_stderr, + bool slave_process, bool exit_on_error, + int fd[2]); + +/* The name of the "always silent" device. */ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +/* Native Woe32 API. */ +# define DEV_NULL "NUL" +#else +/* Unix API. */ +# define DEV_NULL "/dev/null" +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SPAWN_PIPE_H */ diff --git a/m4/pipe.m4 b/m4/pipe.m4 deleted file mode 100644 index 401881c8f..000000000 --- a/m4/pipe.m4 +++ /dev/null @@ -1,12 +0,0 @@ -# pipe.m4 serial 4 -dnl Copyright (C) 2004, 2008, 2009, 2010 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_PIPE], -[ - dnl Prerequisites of lib/pipe.c. - AC_REQUIRE([AC_C_INLINE]) - AC_REQUIRE([AC_TYPE_MODE_T]) -]) diff --git a/m4/spawn-pipe.m4 b/m4/spawn-pipe.m4 new file mode 100644 index 000000000..022c5a0f3 --- /dev/null +++ b/m4/spawn-pipe.m4 @@ -0,0 +1,12 @@ +# spawn-pipe.m4 serial 1 +dnl Copyright (C) 2004, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_SPAWN_PIPE], +[ + dnl Prerequisites of lib/spawn-pipe.c. + AC_REQUIRE([AC_C_INLINE]) + AC_REQUIRE([AC_TYPE_MODE_T]) +]) diff --git a/modules/csharpcomp b/modules/csharpcomp index 3b3ee9089..853ad84ad 100644 --- a/modules/csharpcomp +++ b/modules/csharpcomp @@ -9,7 +9,7 @@ Depends-on: stdbool xmalloca execute -pipe +spawn-pipe wait-process getline sh-quote diff --git a/modules/javacomp b/modules/javacomp index 3bc33b81f..6cb6845f9 100644 --- a/modules/javacomp +++ b/modules/javacomp @@ -10,7 +10,7 @@ stdbool unistd javaversion execute -pipe +spawn-pipe wait-process classpath xsetenv diff --git a/modules/javaversion b/modules/javaversion index 8011077dd..bf1a47be3 100644 --- a/modules/javaversion +++ b/modules/javaversion @@ -10,7 +10,7 @@ lib/javaversion.class Depends-on: javaexec stdbool -pipe +spawn-pipe wait-process getline gettext-h diff --git a/modules/pipe b/modules/pipe index 922bda16c..3d6ccee52 100644 --- a/modules/pipe +++ b/modules/pipe @@ -1,48 +1,23 @@ Description: Creation of subprocesses, communicating via pipes. +Status: +obsolete + +Notice: +This module is obsolete. Use the module 'spawn-pipe' instead. + Files: -lib/pipe.h -lib/pipe.c -lib/w32spawn.h -m4/pipe.m4 Depends-on: -cloexec -dup2 -environ -error -exit -fatal-signal -gettext-h -open -pipe2 -pipe2-safer -spawn -posix_spawnp -posix_spawn_file_actions_init -posix_spawn_file_actions_addclose -posix_spawn_file_actions_adddup2 -posix_spawn_file_actions_addopen -posix_spawn_file_actions_destroy -posix_spawnattr_init -posix_spawnattr_setsigmask -posix_spawnattr_setflags -posix_spawnattr_destroy -stdbool -strpbrk -unistd -unistd-safer -wait-process +spawn-pipe configure.ac: -gl_PIPE Makefile.am: -lib_SOURCES += pipe.h pipe.c w32spawn.h Include: -"pipe.h" +"spawn-pipe.h" License: GPL diff --git a/modules/pipe-filter-gi b/modules/pipe-filter-gi index 95ab27a6c..7122c1ad4 100644 --- a/modules/pipe-filter-gi +++ b/modules/pipe-filter-gi @@ -7,7 +7,7 @@ lib/pipe-filter-gi.c lib/pipe-filter-aux.h Depends-on: -pipe +spawn-pipe wait-process error exit diff --git a/modules/pipe-filter-ii b/modules/pipe-filter-ii index 6ae280199..f845a8f5a 100644 --- a/modules/pipe-filter-ii +++ b/modules/pipe-filter-ii @@ -7,7 +7,7 @@ lib/pipe-filter-ii.c lib/pipe-filter-aux.h Depends-on: -pipe +spawn-pipe wait-process error exit diff --git a/modules/pipe-tests b/modules/pipe-tests deleted file mode 100644 index b20ea687a..000000000 --- a/modules/pipe-tests +++ /dev/null @@ -1,14 +0,0 @@ -Files: -tests/test-pipe.sh -tests/test-pipe.c -tests/macros.h - -Depends-on: -progname - -configure.ac: - -Makefile.am: -TESTS += test-pipe.sh -check_PROGRAMS += test-pipe -test_pipe_LDADD = $(LDADD) @LIBINTL@ diff --git a/modules/spawn-pipe b/modules/spawn-pipe new file mode 100644 index 000000000..04441b1a4 --- /dev/null +++ b/modules/spawn-pipe @@ -0,0 +1,51 @@ +Description: +Creation of subprocesses, communicating via pipes. + +Files: +lib/spawn-pipe.h +lib/spawn-pipe.c +lib/w32spawn.h +m4/spawn-pipe.m4 + +Depends-on: +cloexec +dup2 +environ +error +exit +fatal-signal +gettext-h +open +pipe2 +pipe2-safer +spawn +posix_spawnp +posix_spawn_file_actions_init +posix_spawn_file_actions_addclose +posix_spawn_file_actions_adddup2 +posix_spawn_file_actions_addopen +posix_spawn_file_actions_destroy +posix_spawnattr_init +posix_spawnattr_setsigmask +posix_spawnattr_setflags +posix_spawnattr_destroy +stdbool +strpbrk +unistd +unistd-safer +wait-process + +configure.ac: +gl_SPAWN_PIPE + +Makefile.am: +lib_SOURCES += spawn-pipe.h spawn-pipe.c w32spawn.h + +Include: +"spawn-pipe.h" + +License: +GPL + +Maintainer: +Bruno Haible diff --git a/modules/spawn-pipe-tests b/modules/spawn-pipe-tests new file mode 100644 index 000000000..c55903358 --- /dev/null +++ b/modules/spawn-pipe-tests @@ -0,0 +1,14 @@ +Files: +tests/test-spawn-pipe.sh +tests/test-spawn-pipe.c +tests/macros.h + +Depends-on: +progname + +configure.ac: + +Makefile.am: +TESTS += test-spawn-pipe.sh +check_PROGRAMS += test-spawn-pipe +test_spawn_pipe_LDADD = $(LDADD) @LIBINTL@ diff --git a/tests/test-pipe.c b/tests/test-pipe.c deleted file mode 100644 index 2dcab58b2..000000000 --- a/tests/test-pipe.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Test of create_pipe_bidi/wait_subprocess. - Copyright (C) 2009, 2010 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your option) - any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#include - -#include "pipe.h" -#include "wait-process.h" - -#include -#include -#include -#include -#include -#include - -/* Depending on arguments, this test intentionally closes stderr or - starts life with stderr closed. So, we arrange to have fd 10 - (outside the range of interesting fd's during the test) set up to - duplicate the original stderr. */ - -#define BACKUP_STDERR_FILENO 10 -#define ASSERT_STREAM myerr -#include "macros.h" - -static FILE *myerr; - -/* Code executed by the child process. argv[1] = "child". */ -static int -child_main (int argc, char *argv[]) -{ - char buffer[2] = { 's', 't' }; - int fd; - int ret; - - ASSERT (argc == 3); - - /* Read one byte from fd 0, and write its value plus one to fd 1. - fd 2 should be closed iff the argument is 1. Check that no other file - descriptors leaked. */ - - ASSERT (read (STDIN_FILENO, buffer, 2) == 1); - - buffer[0]++; - ASSERT (write (STDOUT_FILENO, buffer, 1) == 1); - - errno = 0; - ret = dup2 (STDERR_FILENO, STDERR_FILENO); - switch (atoi (argv[2])) - { - case 0: - /* Expect fd 2 is open. */ - ASSERT (ret == STDERR_FILENO); - break; - case 1: - /* Expect fd 2 is closed. */ - ASSERT (ret == -1); - ASSERT (errno == EBADF); - break; - default: - ASSERT (false); - } - - for (fd = 3; fd < 7; fd++) - { - errno = 0; - ASSERT (close (fd) == -1); - ASSERT (errno == EBADF); - } - - return 0; -} - -/* Create a bi-directional pipe to a test child, and validate that the - child program returns the expected output. The child is the same - program as the parent ARGV0, but with different arguments. - STDERR_CLOSED is true if we have already closed fd 2. */ -static void -test_pipe (const char *argv0, bool stderr_closed) -{ - int fd[2]; - char *argv[4]; - pid_t pid; - char buffer[2] = { 'a', 't' }; - - /* Set up child. */ - argv[0] = (char *) argv0; - argv[1] = (char *) "child"; - argv[2] = (char *) (stderr_closed ? "1" : "0"); - argv[3] = NULL; - pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd); - ASSERT (0 <= pid); - ASSERT (STDERR_FILENO < fd[0]); - ASSERT (STDERR_FILENO < fd[1]); - - /* Push child's input. */ - ASSERT (write (fd[1], buffer, 1) == 1); - ASSERT (close (fd[1]) == 0); - - /* Get child's output. */ - ASSERT (read (fd[0], buffer, 2) == 1); - - /* Wait for child. */ - ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0); - ASSERT (close (fd[0]) == 0); - - /* Check the result. */ - ASSERT (buffer[0] == 'b'); - ASSERT (buffer[1] == 't'); -} - -/* Code executed by the parent process. */ -static int -parent_main (int argc, char *argv[]) -{ - int test; - int fd; - - ASSERT (argc == 2); - - /* Selectively close various standard fds, to verify the child process is - not impacted by this. */ - test = atoi (argv[1]); - switch (test) - { - case 0: - break; - case 1: - close (0); - break; - case 2: - close (1); - break; - case 3: - close (0); - close (1); - break; - case 4: - close (2); - break; - case 5: - close (0); - close (2); - break; - case 6: - close (1); - close (2); - break; - case 7: - close (0); - close (1); - close (2); - break; - default: - ASSERT (false); - } - - /* Plug any file descriptor leaks inherited from outside world before - starting, so that child has a clean slate (at least for the fds that we - might be manipulating). */ - for (fd = 3; fd < 7; fd++) - close (fd); - - test_pipe (argv[0], test >= 4); - - return 0; -} - -int -main (int argc, char *argv[]) -{ - if (argc < 2) - { - fprintf (stderr, "%s: need arguments\n", argv[0]); - return 2; - } - if (strcmp (argv[1], "child") == 0) - { - /* fd 2 might be closed, but fd BACKUP_STDERR_FILENO is the original - stderr. */ - myerr = fdopen (BACKUP_STDERR_FILENO, "w"); - if (!myerr) - return 2; - return child_main (argc, argv); - } - /* We might close fd 2 later, so save it in fd 10. */ - if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO - || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) - return 2; - return parent_main (argc, argv); -} diff --git a/tests/test-pipe.sh b/tests/test-pipe.sh deleted file mode 100755 index 323c90fb9..000000000 --- a/tests/test-pipe.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -st=0 -for i in 0 1 2 3 4 5 6 7 ; do - ./test-pipe${EXEEXT} $i \ - || { echo test-pipe.sh: iteration $i failed >&2; st=1; } -done -exit $st diff --git a/tests/test-spawn-pipe.c b/tests/test-spawn-pipe.c new file mode 100644 index 000000000..1f606f006 --- /dev/null +++ b/tests/test-spawn-pipe.c @@ -0,0 +1,204 @@ +/* Test of create_pipe_bidi/wait_subprocess. + Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "spawn-pipe.h" +#include "wait-process.h" + +#include +#include +#include +#include +#include +#include + +/* Depending on arguments, this test intentionally closes stderr or + starts life with stderr closed. So, we arrange to have fd 10 + (outside the range of interesting fd's during the test) set up to + duplicate the original stderr. */ + +#define BACKUP_STDERR_FILENO 10 +#define ASSERT_STREAM myerr +#include "macros.h" + +static FILE *myerr; + +/* Code executed by the child process. argv[1] = "child". */ +static int +child_main (int argc, char *argv[]) +{ + char buffer[2] = { 's', 't' }; + int fd; + int ret; + + ASSERT (argc == 3); + + /* Read one byte from fd 0, and write its value plus one to fd 1. + fd 2 should be closed iff the argument is 1. Check that no other file + descriptors leaked. */ + + ASSERT (read (STDIN_FILENO, buffer, 2) == 1); + + buffer[0]++; + ASSERT (write (STDOUT_FILENO, buffer, 1) == 1); + + errno = 0; + ret = dup2 (STDERR_FILENO, STDERR_FILENO); + switch (atoi (argv[2])) + { + case 0: + /* Expect fd 2 is open. */ + ASSERT (ret == STDERR_FILENO); + break; + case 1: + /* Expect fd 2 is closed. */ + ASSERT (ret == -1); + ASSERT (errno == EBADF); + break; + default: + ASSERT (false); + } + + for (fd = 3; fd < 7; fd++) + { + errno = 0; + ASSERT (close (fd) == -1); + ASSERT (errno == EBADF); + } + + return 0; +} + +/* Create a bi-directional pipe to a test child, and validate that the + child program returns the expected output. The child is the same + program as the parent ARGV0, but with different arguments. + STDERR_CLOSED is true if we have already closed fd 2. */ +static void +test_pipe (const char *argv0, bool stderr_closed) +{ + int fd[2]; + char *argv[4]; + pid_t pid; + char buffer[2] = { 'a', 't' }; + + /* Set up child. */ + argv[0] = (char *) argv0; + argv[1] = (char *) "child"; + argv[2] = (char *) (stderr_closed ? "1" : "0"); + argv[3] = NULL; + pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd); + ASSERT (0 <= pid); + ASSERT (STDERR_FILENO < fd[0]); + ASSERT (STDERR_FILENO < fd[1]); + + /* Push child's input. */ + ASSERT (write (fd[1], buffer, 1) == 1); + ASSERT (close (fd[1]) == 0); + + /* Get child's output. */ + ASSERT (read (fd[0], buffer, 2) == 1); + + /* Wait for child. */ + ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0); + ASSERT (close (fd[0]) == 0); + + /* Check the result. */ + ASSERT (buffer[0] == 'b'); + ASSERT (buffer[1] == 't'); +} + +/* Code executed by the parent process. */ +static int +parent_main (int argc, char *argv[]) +{ + int test; + int fd; + + ASSERT (argc == 2); + + /* Selectively close various standard fds, to verify the child process is + not impacted by this. */ + test = atoi (argv[1]); + switch (test) + { + case 0: + break; + case 1: + close (0); + break; + case 2: + close (1); + break; + case 3: + close (0); + close (1); + break; + case 4: + close (2); + break; + case 5: + close (0); + close (2); + break; + case 6: + close (1); + close (2); + break; + case 7: + close (0); + close (1); + close (2); + break; + default: + ASSERT (false); + } + + /* Plug any file descriptor leaks inherited from outside world before + starting, so that child has a clean slate (at least for the fds that we + might be manipulating). */ + for (fd = 3; fd < 7; fd++) + close (fd); + + test_pipe (argv[0], test >= 4); + + return 0; +} + +int +main (int argc, char *argv[]) +{ + if (argc < 2) + { + fprintf (stderr, "%s: need arguments\n", argv[0]); + return 2; + } + if (strcmp (argv[1], "child") == 0) + { + /* fd 2 might be closed, but fd BACKUP_STDERR_FILENO is the original + stderr. */ + myerr = fdopen (BACKUP_STDERR_FILENO, "w"); + if (!myerr) + return 2; + return child_main (argc, argv); + } + /* We might close fd 2 later, so save it in fd 10. */ + if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO + || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) + return 2; + return parent_main (argc, argv); +} diff --git a/tests/test-spawn-pipe.sh b/tests/test-spawn-pipe.sh new file mode 100755 index 000000000..2e4ea129e --- /dev/null +++ b/tests/test-spawn-pipe.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +st=0 +for i in 0 1 2 3 4 5 6 7 ; do + ./test-spawn-pipe${EXEEXT} $i \ + || { echo test-spawn-pipe.sh: iteration $i failed >&2; st=1; } +done +exit $st