| /* Creation of subprocesses, communicating via pipes. | |
| Copyright (C) 2001-2004, 2006-2026 Free Software Foundation, Inc. | |
| Written by Bruno Haible <haible@clisp.cons.org>, 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 <https://www.gnu.org/licenses/>. */ | |
| /* Tell clang not to warn about the 'child' variable, below. */ | |
| #if defined __clang__ | |
| # pragma clang diagnostic ignored "-Wconditional-uninitialized" | |
| #endif | |
| #include <config.h> | |
| /* Specification. */ | |
| #include "spawn-pipe.h" | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdlib.h> | |
| #include <signal.h> | |
| #include <unistd.h> | |
| #include "canonicalize.h" | |
| #include <error.h> | |
| #include "fatal-signal.h" | |
| #include "filename.h" | |
| #include "findprog.h" | |
| #include "windows-path.h" | |
| #include "unistd-safer.h" | |
| #include "wait-process.h" | |
| #include "xalloc.h" | |
| #include "gettext.h" | |
| #define _(msgid) dgettext (GNULIB_TEXT_DOMAIN, msgid) | |
| /* Choice of implementation for native Windows. | |
| - Define to 0 to use the posix_spawn facility (modules 'posix_spawn' and | |
| 'posix_spawnp'), that is based on the module 'windows-spawn'. | |
| - Define to 1 to use the older code, that uses the module 'windows-spawn' | |
| directly. | |
| You can set this macro from a Makefile or at configure time, from the | |
| CPPFLAGS. */ | |
| #ifndef SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN | |
| # define SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN 0 | |
| #endif | |
| #if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN | |
| /* Native Windows API. */ | |
| # if GNULIB_MSVC_NOTHROW | |
| # include "msvc-nothrow.h" | |
| # else | |
| # include <io.h> | |
| # endif | |
| # include <process.h> | |
| # include "windows-spawn.h" | |
| #elif defined __KLIBC__ | |
| /* OS/2 kLIBC API. */ | |
| # include <process.h> | |
| # include "os2-spawn.h" | |
| #else | |
| /* Unix API. */ | |
| # include <spawn.h> | |
| #endif | |
| #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 int | |
| nonintr_close (int fd) | |
| { | |
| int retval; | |
| do | |
| retval = close (fd); | |
| while (retval < 0 && errno == EINTR); | |
| return retval; | |
| } | |
| #undef close /* avoid warning related to gnulib module unistd */ | |
| #define close nonintr_close | |
| #if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN | |
| static 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 | |
| #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, | |
| const char * const *prog_argv, | |
| const char * const *dll_dirs, | |
| const char *directory, | |
| 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]) | |
| { | |
| int saved_errno; | |
| char *prog_path_to_free = NULL; | |
| if (directory != NULL) | |
| { | |
| /* If a change of directory is requested, make sure PROG_PATH is absolute | |
| before we do so. This is needed because | |
| - posix_spawn and posix_spawnp are required to resolve a relative | |
| PROG_PATH *after* changing the directory. See | |
| <https://www.austingroupbugs.net/view.php?id=1208>: | |
| "if this pathname does not start with a <slash> it shall be | |
| interpreted relative to the working directory of the child | |
| process _after_ all file_actions have been performed." | |
| But this would be a surprising application behaviour, possibly | |
| even security relevant. | |
| - For the Windows CreateProcess() function, it is unspecified whether | |
| a relative file name is interpreted to the parent's current | |
| directory or to the specified directory. See | |
| <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa> */ | |
| if (! IS_ABSOLUTE_FILE_NAME (prog_path)) | |
| { | |
| const char *resolved_prog = | |
| find_in_given_path (prog_path, getenv ("PATH"), NULL, false); | |
| if (resolved_prog == NULL) | |
| goto fail_with_errno; | |
| if (resolved_prog != prog_path) | |
| prog_path_to_free = (char *) resolved_prog; | |
| prog_path = resolved_prog; | |
| if (! IS_ABSOLUTE_FILE_NAME (prog_path)) | |
| { | |
| char *absolute_prog = | |
| canonicalize_filename_mode (prog_path, CAN_MISSING | CAN_NOLINKS); | |
| if (absolute_prog == NULL) | |
| { | |
| free (prog_path_to_free); | |
| goto fail_with_errno; | |
| } | |
| free (prog_path_to_free); | |
| prog_path_to_free = absolute_prog; | |
| prog_path = absolute_prog; | |
| if (! IS_ABSOLUTE_FILE_NAME (prog_path)) | |
| abort (); | |
| } | |
| } | |
| } | |
| int ifd[2]; | |
| int ofd[2]; | |
| /* It is important to create the file descriptors with the close-on-exec bit | |
| set. | |
| * In the child process that we are about to create here, the file | |
| descriptors ofd[0] -> STDIN_FILENO and ifd[1] -> STDOUT_FILENO will be | |
| preserved across exec, because each dup2 call scheduled by | |
| posix_spawn_file_actions_adddup2 creates a file descriptor with the | |
| close-on-exec bit clear. Similarly on native Windows, where we use | |
| explicit DuplicateHandle calls, and on kLIBC, where we use explicit dup2 | |
| calls. | |
| * In the parent process, we close ofd[0] and ifd[1]; so, ofd[1] and ofd[0] | |
| are still open. But if the parent process spawns another child process | |
| later, if ofd[1] and ofd[0] were inherited by that child process, the | |
| "end of input" / "end of output" detection would not work any more. The | |
| parent or the child process would block, as long as that other child | |
| process is running. */ | |
| 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 | |
| * | |
| */ | |
| #if ((defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN) || defined __KLIBC__ | |
| /* Native Windows 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. */ | |
| char *argv_mem_to_free; | |
| int child; | |
| int nulloutfd; | |
| int stdinfd; | |
| int stdoutfd; | |
| const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free); | |
| if (argv == NULL) | |
| xalloc_die (); | |
| child = -1; | |
| # if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN | |
| bool must_close_ifd1 = pipe_stdout; | |
| bool must_close_ofd0 = pipe_stdin; | |
| /* Create standard file handles of child process. */ | |
| HANDLE stdin_handle = INVALID_HANDLE_VALUE; | |
| HANDLE stdout_handle = INVALID_HANDLE_VALUE; | |
| nulloutfd = -1; | |
| stdinfd = -1; | |
| stdoutfd = -1; | |
| if ((!null_stderr | |
| || (nulloutfd = open ("NUL", O_RDWR, 0)) >= 0) | |
| && (pipe_stdin | |
| || prog_stdin == NULL | |
| || (stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0) | |
| && (pipe_stdout | |
| || prog_stdout == NULL | |
| || (stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0)) | |
| /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1], | |
| but it inherits the three STD*_FILENO for which we pass the handles. */ | |
| /* Pass the environment explicitly. This is needed if the program has | |
| modified the environment using putenv() or [un]setenv(). On Windows, | |
| processes 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. If we were | |
| to pass NULL, the child process would inherit a copy of the environment | |
| block - ignoring the effects of putenv() and [un]setenv(). */ | |
| { | |
| stdin_handle = | |
| (HANDLE) _get_osfhandle (pipe_stdin ? ofd[0] : | |
| prog_stdin == NULL ? STDIN_FILENO : stdinfd); | |
| if (pipe_stdin) | |
| { | |
| HANDLE curr_process = GetCurrentProcess (); | |
| HANDLE duplicate; | |
| if (!DuplicateHandle (curr_process, stdin_handle, | |
| curr_process, &duplicate, | |
| 0, TRUE, DUPLICATE_SAME_ACCESS)) | |
| { | |
| errno = EBADF; /* arbitrary */ | |
| goto failed; | |
| } | |
| must_close_ofd0 = false; | |
| close (ofd[0]); /* implies CloseHandle (stdin_handle); */ | |
| stdin_handle = duplicate; | |
| } | |
| stdout_handle = | |
| (HANDLE) _get_osfhandle (pipe_stdout ? ifd[1] : | |
| prog_stdout == NULL ? STDOUT_FILENO : stdoutfd); | |
| if (pipe_stdout) | |
| { | |
| HANDLE curr_process = GetCurrentProcess (); | |
| HANDLE duplicate; | |
| if (!DuplicateHandle (curr_process, stdout_handle, | |
| curr_process, &duplicate, | |
| 0, TRUE, DUPLICATE_SAME_ACCESS)) | |
| { | |
| errno = EBADF; /* arbitrary */ | |
| goto failed; | |
| } | |
| must_close_ifd1 = false; | |
| close (ifd[1]); /* implies CloseHandle (stdout_handle); */ | |
| stdout_handle = duplicate; | |
| } | |
| HANDLE stderr_handle = | |
| (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO); | |
| child = spawnpvech (P_NOWAIT, prog_path, argv + 1, | |
| (const char * const *) environ, dll_dirs, | |
| directory, | |
| stdin_handle, stdout_handle, stderr_handle); | |
| # if 0 /* Executing arbitrary files as shell scripts is unsecure. */ | |
| if (child == -1 && errno == ENOEXEC) | |
| { | |
| /* prog is not a native executable. Try to execute it as a | |
| shell script. Note that prepare_spawn() has already prepended | |
| a hidden element "sh.exe" to argv. */ | |
| argv[1] = prog_path; | |
| child = spawnpvech (P_NOWAIT, argv[0], argv, | |
| (const char * const *) environ, dll_dirs, | |
| directory, | |
| stdin_handle, stdout_handle, stderr_handle); | |
| } | |
| # endif | |
| } | |
| failed: | |
| if (child == -1) | |
| saved_errno = errno; | |
| if (stdinfd >= 0) | |
| close (stdinfd); | |
| if (stdoutfd >= 0) | |
| close (stdoutfd); | |
| if (nulloutfd >= 0) | |
| close (nulloutfd); | |
| if (pipe_stdin) | |
| { | |
| if (must_close_ofd0) | |
| close (ofd[0]); | |
| else | |
| if (stdin_handle != INVALID_HANDLE_VALUE) | |
| CloseHandle (stdin_handle); | |
| } | |
| if (pipe_stdout) | |
| { | |
| if (must_close_ifd1) | |
| close (ifd[1]); | |
| else | |
| if (stdout_handle != INVALID_HANDLE_VALUE) | |
| CloseHandle (stdout_handle); | |
| } | |
| # else /* __KLIBC__ */ | |
| int new_stdin = STDIN_FILENO; | |
| int new_stdout = STDOUT_FILENO; | |
| int new_stderr = STDERR_FILENO; | |
| /* Create standard file handles of child process. */ | |
| nulloutfd = -1; | |
| stdinfd = -1; | |
| stdoutfd = -1; | |
| if ((!pipe_stdin || (new_stdin = ofd[0]) >= 0) | |
| && (!pipe_stdout || (new_stdout = ifd[1]) >= 0) | |
| && (!null_stderr | |
| || ((nulloutfd = open ("NUL", O_RDWR | O_CLOEXEC, 0)) >= 0 | |
| && (new_stderr = nulloutfd) >= 0)) | |
| && (pipe_stdin | |
| || prog_stdin == NULL | |
| || ((stdinfd = open (prog_stdin, O_RDONLY | O_CLOEXEC, 0)) >= 0 | |
| && (new_stdin = stdinfd) >= 0)) | |
| && (pipe_stdout | |
| || prog_stdout == NULL | |
| || ((stdoutfd = open (prog_stdout, O_WRONLY | O_CLOEXEC, 0)) >= 0 | |
| && (new_stdout = 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). */ | |
| { | |
| child = spawnpvech (P_NOWAIT, prog_path, argv + 1, | |
| (const char **) environ, directory, | |
| new_stdin, new_stdout, new_stderr); | |
| # if 0 /* Executing arbitrary files as shell scripts is unsecure. */ | |
| if (child == -1 && errno == ENOEXEC) | |
| { | |
| /* prog is not a native executable. Try to execute it as a | |
| shell script. Note that prepare_spawn() has already prepended | |
| a hidden element "sh.exe" to argv. */ | |
| child = spawnpvech (P_NOWAIT, argv[0], argv, | |
| (const char **) environ, directory, | |
| new_stdin, new_stdout, new_stderr); | |
| } | |
| # endif | |
| } | |
| if (child == -1) | |
| saved_errno = errno; | |
| if (stdinfd >= 0) | |
| close (stdinfd); | |
| if (stdoutfd >= 0) | |
| close (stdoutfd); | |
| if (nulloutfd >= 0) | |
| close (nulloutfd); | |
| if (pipe_stdin) | |
| close (ofd[0]); | |
| if (pipe_stdout) | |
| close (ifd[1]); | |
| # endif | |
| free (argv); | |
| free (argv_mem_to_free); | |
| free (prog_path_to_free); | |
| if (child == -1) | |
| { | |
| if (pipe_stdout) | |
| close (ifd[0]); | |
| if (pipe_stdin) | |
| close (ofd[1]); | |
| goto fail_with_saved_errno; | |
| } | |
| if (pipe_stdout) | |
| fd[0] = ifd[0]; | |
| if (pipe_stdin) | |
| fd[1] = ofd[1]; | |
| return child; | |
| #else | |
| /* Unix API. */ | |
| char **child_environ = environ; | |
| char **malloced_environ = NULL; | |
| # if defined _WIN32 || defined __CYGWIN__ | |
| if (dll_dirs != NULL && dll_dirs[0] != NULL) | |
| { | |
| malloced_environ = extended_environ (dll_dirs); | |
| if (malloced_environ == NULL) | |
| goto fail_with_errno; | |
| child_environ = malloced_environ; | |
| } | |
| # endif | |
| sigset_t blocked_signals; | |
| if (slave_process) | |
| { | |
| sigprocmask (SIG_SETMASK, NULL, &blocked_signals); | |
| block_fatal_signals (); | |
| } | |
| posix_spawn_file_actions_t actions; | |
| bool actions_allocated = false; | |
| posix_spawnattr_t attrs; | |
| bool attrs_allocated = false; | |
| int err; | |
| pid_t child; | |
| 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) | |
| || (directory != NULL | |
| && (err = posix_spawn_file_actions_addchdir (&actions, | |
| directory))) | |
| || (slave_process | |
| && ((err = posix_spawnattr_init (&attrs)) != 0 | |
| || (attrs_allocated = true, | |
| # if defined _WIN32 && !defined __CYGWIN__ | |
| (err = posix_spawnattr_setpgroup (&attrs, 0)) != 0 | |
| || (err = posix_spawnattr_setflags (&attrs, | |
| POSIX_SPAWN_SETPGROUP)) | |
| != 0 | |
| # else | |
| (err = posix_spawnattr_setsigmask (&attrs, | |
| &blocked_signals)) | |
| != 0 | |
| || (err = posix_spawnattr_setflags (&attrs, | |
| POSIX_SPAWN_SETSIGMASK)) | |
| != 0 | |
| # endif | |
| ) ) ) | |
| || (err = (directory != NULL | |
| ? posix_spawn (&child, prog_path, &actions, | |
| attrs_allocated ? &attrs : NULL, | |
| (char * const *) prog_argv, | |
| child_environ) | |
| : posix_spawnp (&child, prog_path, &actions, | |
| attrs_allocated ? &attrs : NULL, | |
| (char * const *) prog_argv, | |
| child_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 (malloced_environ != NULL) | |
| { | |
| free (malloced_environ[0]); | |
| free (malloced_environ); | |
| } | |
| if (pipe_stdout) | |
| { | |
| close (ifd[0]); | |
| close (ifd[1]); | |
| } | |
| if (pipe_stdin) | |
| { | |
| close (ofd[0]); | |
| close (ofd[1]); | |
| } | |
| free (prog_path_to_free); | |
| saved_errno = err; | |
| goto fail_with_saved_errno; | |
| } | |
| posix_spawn_file_actions_destroy (&actions); | |
| if (attrs_allocated) | |
| posix_spawnattr_destroy (&attrs); | |
| if (slave_process) | |
| { | |
| register_slave_subprocess (child); | |
| unblock_fatal_signals (); | |
| } | |
| if (malloced_environ != NULL) | |
| { | |
| free (malloced_environ[0]); | |
| free (malloced_environ); | |
| } | |
| if (pipe_stdin) | |
| close (ofd[0]); | |
| if (pipe_stdout) | |
| close (ifd[1]); | |
| free (prog_path_to_free); | |
| if (pipe_stdout) | |
| fd[0] = ifd[0]; | |
| if (pipe_stdin) | |
| fd[1] = ofd[1]; | |
| return child; | |
| #endif | |
| fail_with_errno: | |
| saved_errno = errno; | |
| fail_with_saved_errno: | |
| if (exit_on_error || !null_stderr) | |
| error (exit_on_error ? EXIT_FAILURE : 0, saved_errno, | |
| _("%s subprocess failed"), progname); | |
| errno = saved_errno; | |
| return -1; | |
| } | |
| /* 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, const char * const *prog_argv, | |
| const char * const *dll_dirs, | |
| const char *directory, | |
| bool null_stderr, | |
| bool slave_process, bool exit_on_error, | |
| int fd[2]) | |
| { | |
| pid_t result = create_pipe (progname, prog_path, prog_argv, dll_dirs, | |
| directory, | |
| 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, const char * const *prog_argv, | |
| const char * const *dll_dirs, | |
| const char *directory, | |
| 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, dll_dirs, | |
| directory, | |
| 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, const char * const *prog_argv, | |
| const char * const *dll_dirs, | |
| const char *directory, | |
| 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, dll_dirs, | |
| directory, | |
| true, false, NULL, prog_stdout, | |
| null_stderr, slave_process, exit_on_error, | |
| iofd); | |
| if (result != -1) | |
| fd[0] = iofd[1]; | |
| return result; | |
| } |