(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
spawn-pipe.c
       1  /* Creation of subprocesses, communicating via pipes.
       2     Copyright (C) 2001-2004, 2006-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <haible@clisp.cons.org>, 2001.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  /* Tell clang not to warn about the 'child' variable, below.  */
      20  #if defined __clang__
      21  # pragma clang diagnostic ignored "-Wconditional-uninitialized"
      22  #endif
      23  
      24  #include <config.h>
      25  
      26  /* Specification.  */
      27  #include "spawn-pipe.h"
      28  
      29  #include <errno.h>
      30  #include <fcntl.h>
      31  #include <stdlib.h>
      32  #include <signal.h>
      33  #include <unistd.h>
      34  
      35  #include "canonicalize.h"
      36  #include "error.h"
      37  #include "fatal-signal.h"
      38  #include "filename.h"
      39  #include "findprog.h"
      40  #include "unistd-safer.h"
      41  #include "wait-process.h"
      42  #include "xalloc.h"
      43  #include "gettext.h"
      44  
      45  #define _(str) gettext (str)
      46  
      47  
      48  /* Choice of implementation for native Windows.
      49     - Define to 0 to use the posix_spawn facility (modules 'posix_spawn' and
      50       'posix_spawnp'), that is based on the module 'windows-spawn'.
      51     - Define to 1 to use the older code, that uses the module 'windows-spawn'
      52       directly.
      53     You can set this macro from a Makefile or at configure time, from the
      54     CPPFLAGS.  */
      55  #ifndef SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN
      56  # define SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN 0
      57  #endif
      58  
      59  
      60  #if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN
      61  
      62  /* Native Windows API.  */
      63  # if GNULIB_MSVC_NOTHROW
      64  #  include "msvc-nothrow.h"
      65  # else
      66  #  include <io.h>
      67  # endif
      68  # include <process.h>
      69  # include "windows-spawn.h"
      70  
      71  #elif defined __KLIBC__
      72  
      73  /* OS/2 kLIBC API.  */
      74  # include <process.h>
      75  # include "os2-spawn.h"
      76  
      77  #else
      78  
      79  /* Unix API.  */
      80  # include <spawn.h>
      81  
      82  #endif
      83  
      84  
      85  #ifdef EINTR
      86  
      87  /* EINTR handling for close().
      88     These functions can return -1/EINTR even though we don't have any
      89     signal handlers set up, namely when we get interrupted via SIGSTOP.  */
      90  
      91  static int
      92  nonintr_close (int fd)
      93  {
      94    int retval;
      95  
      96    do
      97      retval = close (fd);
      98    while (retval < 0 && errno == EINTR);
      99  
     100    return retval;
     101  }
     102  #undef close /* avoid warning related to gnulib module unistd */
     103  #define close nonintr_close
     104  
     105  #if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN
     106  static int
     107  nonintr_open (const char *pathname, int oflag, mode_t mode)
     108  {
     109    int retval;
     110  
     111    do
     112      retval = open (pathname, oflag, mode);
     113    while (retval < 0 && errno == EINTR);
     114  
     115    return retval;
     116  }
     117  # undef open /* avoid warning on VMS */
     118  # define open nonintr_open
     119  #endif
     120  
     121  #endif
     122  
     123  
     124  /* Open a pipe connected to a child process.
     125   *
     126   *           write       system                read
     127   *    parent  ->   fd[1]   ->   STDIN_FILENO    ->   child       if pipe_stdin
     128   *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child       if pipe_stdout
     129   *           read        system                write
     130   *
     131   * At least one of pipe_stdin, pipe_stdout must be true.
     132   * pipe_stdin and prog_stdin together determine the child's standard input.
     133   * pipe_stdout and prog_stdout together determine the child's standard output.
     134   * If pipe_stdin is true, prog_stdin is ignored.
     135   * If pipe_stdout is true, prog_stdout is ignored.
     136   */
     137  static pid_t
     138  create_pipe (const char *progname,
     139               const char *prog_path,
     140               const char * const *prog_argv,
     141               const char *directory,
     142               bool pipe_stdin, bool pipe_stdout,
     143               const char *prog_stdin, const char *prog_stdout,
     144               bool null_stderr,
     145               bool slave_process, bool exit_on_error,
     146               int fd[2])
     147  {
     148    int saved_errno;
     149    char *prog_path_to_free = NULL;
     150  
     151    if (directory != NULL)
     152      {
     153        /* If a change of directory is requested, make sure PROG_PATH is absolute
     154           before we do so.  This is needed because
     155             - posix_spawn and posix_spawnp are required to resolve a relative
     156               PROG_PATH *after* changing the directory.  See
     157               <https://www.austingroupbugs.net/view.php?id=1208>:
     158                 "if this pathname does not start with a <slash> it shall be
     159                  interpreted relative to the working directory of the child
     160                  process _after_ all file_actions have been performed."
     161               But this would be a surprising application behaviour, possibly
     162               even security relevant.
     163             - For the Windows CreateProcess() function, it is unspecified whether
     164               a relative file name is interpreted to the parent's current
     165               directory or to the specified directory.  See
     166               <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa>  */
     167        if (! IS_ABSOLUTE_FILE_NAME (prog_path))
     168          {
     169            const char *resolved_prog =
     170              find_in_given_path (prog_path, getenv ("PATH"), NULL, false);
     171            if (resolved_prog == NULL)
     172              goto fail_with_errno;
     173            if (resolved_prog != prog_path)
     174              prog_path_to_free = (char *) resolved_prog;
     175            prog_path = resolved_prog;
     176  
     177            if (! IS_ABSOLUTE_FILE_NAME (prog_path))
     178              {
     179                char *absolute_prog =
     180                  canonicalize_filename_mode (prog_path, CAN_MISSING | CAN_NOLINKS);
     181                if (absolute_prog == NULL)
     182                  {
     183                    free (prog_path_to_free);
     184                    goto fail_with_errno;
     185                  }
     186                free (prog_path_to_free);
     187                prog_path_to_free = absolute_prog;
     188                prog_path = absolute_prog;
     189  
     190                if (! IS_ABSOLUTE_FILE_NAME (prog_path))
     191                  abort ();
     192              }
     193          }
     194      }
     195  
     196    int ifd[2];
     197    int ofd[2];
     198  
     199    /* It is important to create the file descriptors with the close-on-exec bit
     200       set.
     201       * In the child process that we are about to create here, the file
     202         descriptors ofd[0] -> STDIN_FILENO and ifd[1] -> STDOUT_FILENO will be
     203         preserved across exec, because each dup2 call scheduled by
     204         posix_spawn_file_actions_adddup2 creates a file descriptor with the
     205         close-on-exec bit clear.  Similarly on native Windows, where we use
     206         explicit DuplicateHandle calls, and on kLIBC, where we use explicit dup2
     207         calls.
     208       * In the parent process, we close ofd[0] and ifd[1]; so, ofd[1] and ofd[0]
     209         are still open. But if the parent process spawns another child process
     210         later, if ofd[1] and ofd[0] were inherited by that child process, the
     211         "end of input" / "end of output" detection would not work any more.  The
     212         parent or the child process would block, as long as that other child
     213         process is running.  */
     214    if (pipe_stdout)
     215      if (pipe2_safer (ifd, O_BINARY | O_CLOEXEC) < 0)
     216        error (EXIT_FAILURE, errno, _("cannot create pipe"));
     217    if (pipe_stdin)
     218      if (pipe2_safer (ofd, O_BINARY | O_CLOEXEC) < 0)
     219        error (EXIT_FAILURE, errno, _("cannot create pipe"));
     220  /* Data flow diagram:
     221   *
     222   *           write        system         read
     223   *    parent  ->   ofd[1]   ->   ofd[0]   ->   child       if pipe_stdin
     224   *    parent  <-   ifd[0]   <-   ifd[1]   <-   child       if pipe_stdout
     225   *           read         system         write
     226   *
     227   */
     228  
     229  #if ((defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN) || defined __KLIBC__
     230  
     231    /* Native Windows API.
     232       This uses _pipe(), dup2(), and _spawnv().  It could also be implemented
     233       using the low-level functions CreatePipe(), DuplicateHandle(),
     234       CreateProcess() and _open_osfhandle(); see the GNU make and GNU clisp
     235       and cvs source code.  */
     236    char *argv_mem_to_free;
     237    int child;
     238    int nulloutfd;
     239    int stdinfd;
     240    int stdoutfd;
     241  
     242    const char **argv = prepare_spawn (prog_argv, &argv_mem_to_free);
     243    if (argv == NULL)
     244      xalloc_die ();
     245  
     246    child = -1;
     247  
     248  # if (defined _WIN32 && !defined __CYGWIN__) && SPAWN_PIPE_IMPL_AVOID_POSIX_SPAWN
     249    bool must_close_ifd1 = pipe_stdout;
     250    bool must_close_ofd0 = pipe_stdin;
     251  
     252    /* Create standard file handles of child process.  */
     253    HANDLE stdin_handle = INVALID_HANDLE_VALUE;
     254    HANDLE stdout_handle = INVALID_HANDLE_VALUE;
     255    nulloutfd = -1;
     256    stdinfd = -1;
     257    stdoutfd = -1;
     258    if ((!null_stderr
     259         || (nulloutfd = open ("NUL", O_RDWR, 0)) >= 0)
     260        && (pipe_stdin
     261            || prog_stdin == NULL
     262            || (stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0)
     263        && (pipe_stdout
     264            || prog_stdout == NULL
     265            || (stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0))
     266      /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1],
     267         but it inherits the three STD*_FILENO for which we pass the handles.  */
     268      /* Pass the environment explicitly.  This is needed if the program has
     269         modified the environment using putenv() or [un]setenv().  On Windows,
     270         processes have two environments, one in the "environment block" of the
     271         process and managed through SetEnvironmentVariable(), and one inside the
     272         process, in the location retrieved by the 'environ' macro.  If we were
     273         to pass NULL, the child process would inherit a copy of the environment
     274         block - ignoring the effects of putenv() and [un]setenv().  */
     275      {
     276        stdin_handle =
     277          (HANDLE) _get_osfhandle (pipe_stdin ? ofd[0] :
     278                                   prog_stdin == NULL ? STDIN_FILENO : stdinfd);
     279        if (pipe_stdin)
     280          {
     281            HANDLE curr_process = GetCurrentProcess ();
     282            HANDLE duplicate;
     283            if (!DuplicateHandle (curr_process, stdin_handle,
     284                                  curr_process, &duplicate,
     285                                  0, TRUE, DUPLICATE_SAME_ACCESS))
     286              {
     287                errno = EBADF; /* arbitrary */
     288                goto failed;
     289              }
     290            must_close_ofd0 = false;
     291            close (ofd[0]); /* implies CloseHandle (stdin_handle); */
     292            stdin_handle = duplicate;
     293          }
     294        stdout_handle =
     295          (HANDLE) _get_osfhandle (pipe_stdout ? ifd[1] :
     296                                   prog_stdout == NULL ? STDOUT_FILENO : stdoutfd);
     297        if (pipe_stdout)
     298          {
     299            HANDLE curr_process = GetCurrentProcess ();
     300            HANDLE duplicate;
     301            if (!DuplicateHandle (curr_process, stdout_handle,
     302                                  curr_process, &duplicate,
     303                                  0, TRUE, DUPLICATE_SAME_ACCESS))
     304              {
     305                errno = EBADF; /* arbitrary */
     306                goto failed;
     307              }
     308            must_close_ifd1 = false;
     309            close (ifd[1]); /* implies CloseHandle (stdout_handle); */
     310            stdout_handle = duplicate;
     311          }
     312        HANDLE stderr_handle =
     313          (HANDLE) _get_osfhandle (null_stderr ? nulloutfd : STDERR_FILENO);
     314  
     315        child = spawnpvech (P_NOWAIT, prog_path, argv + 1,
     316                            (const char * const *) environ, directory,
     317                            stdin_handle, stdout_handle, stderr_handle);
     318  #  if 0 /* Executing arbitrary files as shell scripts is unsecure.  */
     319        if (child == -1 && errno == ENOEXEC)
     320          {
     321            /* prog is not a native executable.  Try to execute it as a
     322               shell script.  Note that prepare_spawn() has already prepended
     323               a hidden element "sh.exe" to argv.  */
     324            argv[1] = prog_path;
     325            child = spawnpvech (P_NOWAIT, argv[0], argv,
     326                                (const char * const *) environ, directory,
     327                                stdin_handle, stdout_handle, stderr_handle);
     328          }
     329  #  endif
     330      }
     331   failed:
     332    if (child == -1)
     333      saved_errno = errno;
     334    if (stdinfd >= 0)
     335      close (stdinfd);
     336    if (stdoutfd >= 0)
     337      close (stdoutfd);
     338    if (nulloutfd >= 0)
     339      close (nulloutfd);
     340  
     341    if (pipe_stdin)
     342      {
     343        if (must_close_ofd0)
     344          close (ofd[0]);
     345        else
     346          if (stdin_handle != INVALID_HANDLE_VALUE)
     347            CloseHandle (stdin_handle);
     348      }
     349    if (pipe_stdout)
     350      {
     351        if (must_close_ifd1)
     352          close (ifd[1]);
     353        else
     354          if (stdout_handle != INVALID_HANDLE_VALUE)
     355            CloseHandle (stdout_handle);
     356      }
     357  
     358  # else /* __KLIBC__ */
     359    if (!(directory == NULL || strcmp (directory, ".") == 0))
     360      {
     361        /* A directory argument is not supported in this implementation.  */
     362        saved_errno = EINVAL;
     363        goto fail_with_saved_errno;
     364      }
     365  
     366    int orig_stdin;
     367    int orig_stdout;
     368    int orig_stderr;
     369  
     370    /* Save standard file handles of parent process.  */
     371    if (pipe_stdin || prog_stdin != NULL)
     372      orig_stdin = dup_safer_noinherit (STDIN_FILENO);
     373    if (pipe_stdout || prog_stdout != NULL)
     374      orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
     375    if (null_stderr)
     376      orig_stderr = dup_safer_noinherit (STDERR_FILENO);
     377  
     378    /* Create standard file handles of child process.  */
     379    nulloutfd = -1;
     380    stdinfd = -1;
     381    stdoutfd = -1;
     382    if ((!pipe_stdin || dup2 (ofd[0], STDIN_FILENO) >= 0)
     383        && (!pipe_stdout || dup2 (ifd[1], STDOUT_FILENO) >= 0)
     384        && (!null_stderr
     385            || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
     386                && (nulloutfd == STDERR_FILENO
     387                    || (dup2 (nulloutfd, STDERR_FILENO) >= 0
     388                        && close (nulloutfd) >= 0))))
     389        && (pipe_stdin
     390            || prog_stdin == NULL
     391            || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
     392                && (stdinfd == STDIN_FILENO
     393                    || (dup2 (stdinfd, STDIN_FILENO) >= 0
     394                        && close (stdinfd) >= 0))))
     395        && (pipe_stdout
     396            || prog_stdout == NULL
     397            || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
     398                && (stdoutfd == STDOUT_FILENO
     399                    || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
     400                        && close (stdoutfd) >= 0)))))
     401      /* The child process doesn't inherit ifd[0], ifd[1], ofd[0], ofd[1],
     402         but it inherits all open()ed or dup2()ed file handles (which is what
     403         we want in the case of STD*_FILENO).  */
     404      {
     405        child = _spawnvpe (P_NOWAIT, prog_path, argv + 1,
     406                           (const char **) environ);
     407  #  if 0 /* Executing arbitrary files as shell scripts is unsecure.  */
     408        if (child == -1 && errno == ENOEXEC)
     409          {
     410            /* prog is not a native executable.  Try to execute it as a
     411               shell script.  Note that prepare_spawn() has already prepended
     412               a hidden element "sh.exe" to argv.  */
     413            child = _spawnvpe (P_NOWAIT, argv[0], argv,
     414                               (const char **) environ);
     415          }
     416  #  endif
     417      }
     418    if (child == -1)
     419      saved_errno = errno;
     420    if (stdinfd >= 0)
     421      close (stdinfd);
     422    if (stdoutfd >= 0)
     423      close (stdoutfd);
     424    if (nulloutfd >= 0)
     425      close (nulloutfd);
     426  
     427    /* Restore standard file handles of parent process.  */
     428    if (null_stderr)
     429      undup_safer_noinherit (orig_stderr, STDERR_FILENO);
     430    if (pipe_stdout || prog_stdout != NULL)
     431      undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
     432    if (pipe_stdin || prog_stdin != NULL)
     433      undup_safer_noinherit (orig_stdin, STDIN_FILENO);
     434  
     435    if (pipe_stdin)
     436      close (ofd[0]);
     437    if (pipe_stdout)
     438      close (ifd[1]);
     439  # endif
     440  
     441    free (argv);
     442    free (argv_mem_to_free);
     443    free (prog_path_to_free);
     444  
     445    if (child == -1)
     446      {
     447        if (pipe_stdout)
     448          close (ifd[0]);
     449        if (pipe_stdin)
     450          close (ofd[1]);
     451        goto fail_with_saved_errno;
     452      }
     453  
     454    if (pipe_stdout)
     455      fd[0] = ifd[0];
     456    if (pipe_stdin)
     457      fd[1] = ofd[1];
     458    return child;
     459  
     460  #else
     461  
     462    /* Unix API.  */
     463    sigset_t blocked_signals;
     464    posix_spawn_file_actions_t actions;
     465    bool actions_allocated;
     466    posix_spawnattr_t attrs;
     467    bool attrs_allocated;
     468    int err;
     469    pid_t child;
     470  
     471    if (slave_process)
     472      {
     473        sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
     474        block_fatal_signals ();
     475      }
     476    actions_allocated = false;
     477    attrs_allocated = false;
     478    if ((err = posix_spawn_file_actions_init (&actions)) != 0
     479        || (actions_allocated = true,
     480            (pipe_stdin
     481             && (err = posix_spawn_file_actions_adddup2 (&actions,
     482                                                         ofd[0], STDIN_FILENO))
     483                != 0)
     484            || (pipe_stdout
     485                && (err = posix_spawn_file_actions_adddup2 (&actions,
     486                                                            ifd[1], STDOUT_FILENO))
     487                   != 0)
     488            || (pipe_stdin
     489                && (err = posix_spawn_file_actions_addclose (&actions, ofd[0]))
     490                   != 0)
     491            || (pipe_stdout
     492                && (err = posix_spawn_file_actions_addclose (&actions, ifd[1]))
     493                   != 0)
     494            || (pipe_stdin
     495                && (err = posix_spawn_file_actions_addclose (&actions, ofd[1]))
     496                   != 0)
     497            || (pipe_stdout
     498                && (err = posix_spawn_file_actions_addclose (&actions, ifd[0]))
     499                   != 0)
     500            || (null_stderr
     501                && (err = posix_spawn_file_actions_addopen (&actions,
     502                                                            STDERR_FILENO,
     503                                                            "/dev/null", O_RDWR,
     504                                                            0))
     505                   != 0)
     506            || (!pipe_stdin
     507                && prog_stdin != NULL
     508                && (err = posix_spawn_file_actions_addopen (&actions,
     509                                                            STDIN_FILENO,
     510                                                            prog_stdin, O_RDONLY,
     511                                                            0))
     512                   != 0)
     513            || (!pipe_stdout
     514                && prog_stdout != NULL
     515                && (err = posix_spawn_file_actions_addopen (&actions,
     516                                                            STDOUT_FILENO,
     517                                                            prog_stdout, O_WRONLY,
     518                                                            0))
     519                   != 0)
     520            || (directory != NULL
     521                && (err = posix_spawn_file_actions_addchdir (&actions,
     522                                                             directory)))
     523            || (slave_process
     524                && ((err = posix_spawnattr_init (&attrs)) != 0
     525                    || (attrs_allocated = true,
     526  # if defined _WIN32 && !defined __CYGWIN__
     527                        (err = posix_spawnattr_setpgroup (&attrs, 0)) != 0
     528                        || (err = posix_spawnattr_setflags (&attrs,
     529                                                           POSIX_SPAWN_SETPGROUP))
     530                           != 0
     531  # else
     532                        (err = posix_spawnattr_setsigmask (&attrs,
     533                                                           &blocked_signals))
     534                        != 0
     535                        || (err = posix_spawnattr_setflags (&attrs,
     536                                                          POSIX_SPAWN_SETSIGMASK))
     537                           != 0
     538  # endif
     539               )   )   )
     540            || (err = (directory != NULL
     541                       ? posix_spawn (&child, prog_path, &actions,
     542                                      attrs_allocated ? &attrs : NULL,
     543                                      (char * const *) prog_argv, environ)
     544                       : posix_spawnp (&child, prog_path, &actions,
     545                                       attrs_allocated ? &attrs : NULL,
     546                                       (char * const *) prog_argv, environ)))
     547               != 0))
     548      {
     549        if (actions_allocated)
     550          posix_spawn_file_actions_destroy (&actions);
     551        if (attrs_allocated)
     552          posix_spawnattr_destroy (&attrs);
     553        if (slave_process)
     554          unblock_fatal_signals ();
     555        if (pipe_stdout)
     556          {
     557            close (ifd[0]);
     558            close (ifd[1]);
     559          }
     560        if (pipe_stdin)
     561          {
     562            close (ofd[0]);
     563            close (ofd[1]);
     564          }
     565        free (prog_path_to_free);
     566        saved_errno = err;
     567        goto fail_with_saved_errno;
     568      }
     569    posix_spawn_file_actions_destroy (&actions);
     570    if (attrs_allocated)
     571      posix_spawnattr_destroy (&attrs);
     572    if (slave_process)
     573      {
     574        register_slave_subprocess (child);
     575        unblock_fatal_signals ();
     576      }
     577    if (pipe_stdin)
     578      close (ofd[0]);
     579    if (pipe_stdout)
     580      close (ifd[1]);
     581    free (prog_path_to_free);
     582  
     583    if (pipe_stdout)
     584      fd[0] = ifd[0];
     585    if (pipe_stdin)
     586      fd[1] = ofd[1];
     587    return child;
     588  
     589  #endif
     590  
     591   fail_with_errno:
     592    saved_errno = errno;
     593   fail_with_saved_errno:
     594    if (exit_on_error || !null_stderr)
     595      error (exit_on_error ? EXIT_FAILURE : 0, saved_errno,
     596             _("%s subprocess failed"), progname);
     597    errno = saved_errno;
     598    return -1;
     599  }
     600  
     601  /* Open a bidirectional pipe.
     602   *
     603   *           write       system                read
     604   *    parent  ->   fd[1]   ->   STDIN_FILENO    ->   child
     605   *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child
     606   *           read        system                write
     607   *
     608   */
     609  pid_t
     610  create_pipe_bidi (const char *progname,
     611                    const char *prog_path, const char * const *prog_argv,
     612                    const char *directory,
     613                    bool null_stderr,
     614                    bool slave_process, bool exit_on_error,
     615                    int fd[2])
     616  {
     617    pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
     618                                true, true, NULL, NULL,
     619                                null_stderr, slave_process, exit_on_error,
     620                                fd);
     621    return result;
     622  }
     623  
     624  /* Open a pipe for input from a child process.
     625   * The child's stdin comes from a file.
     626   *
     627   *           read        system                write
     628   *    parent  <-   fd[0]   <-   STDOUT_FILENO   <-   child
     629   *
     630   */
     631  pid_t
     632  create_pipe_in (const char *progname,
     633                  const char *prog_path, const char * const *prog_argv,
     634                  const char *directory,
     635                  const char *prog_stdin, bool null_stderr,
     636                  bool slave_process, bool exit_on_error,
     637                  int fd[1])
     638  {
     639    int iofd[2];
     640    pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
     641                                false, true, prog_stdin, NULL,
     642                                null_stderr, slave_process, exit_on_error,
     643                                iofd);
     644    if (result != -1)
     645      fd[0] = iofd[0];
     646    return result;
     647  }
     648  
     649  /* Open a pipe for output to a child process.
     650   * The child's stdout goes to a file.
     651   *
     652   *           write       system                read
     653   *    parent  ->   fd[0]   ->   STDIN_FILENO    ->   child
     654   *
     655   */
     656  pid_t
     657  create_pipe_out (const char *progname,
     658                   const char *prog_path, const char * const *prog_argv,
     659                   const char *directory,
     660                   const char *prog_stdout, bool null_stderr,
     661                   bool slave_process, bool exit_on_error,
     662                   int fd[1])
     663  {
     664    int iofd[2];
     665    pid_t result = create_pipe (progname, prog_path, prog_argv, directory,
     666                                true, false, NULL, prog_stdout,
     667                                null_stderr, slave_process, exit_on_error,
     668                                iofd);
     669    if (result != -1)
     670      fd[0] = iofd[1];
     671    return result;
     672  }