bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-gnulib] addition: pipe.h, pipe-in.c, pipe-out.c, pipe-bidi.c


From: Bruno Haible
Subject: [Bug-gnulib] addition: pipe.h, pipe-in.c, pipe-out.c, pipe-bidi.c
Date: Tue, 27 Jan 2004 12:18:03 +0100
User-agent: KMail/1.5

Hi,

There were no comments about 'execute', so I committed it. Now comes the
module that allows to create subprocesses connected with pipes (in or
out or both).

Again, it's tested within gettext for 2 years. Comments?

Bruno

============================= modules/pipe =============================
Description:
Creation of subprocesses, communicating via pipes.

Files:
lib/pipe.h
lib/pipe-in.c
lib/pipe-out.c
lib/pipe-bidi.c
lib/w32spawn.h
m4/pipe.m4

Depends-on:
error
exit
fatal-signal
gettext
stdbool
strpbrk

configure.ac:
gl_PIPE

Makefile.am:
lib_SOURCES += pipe.h pipe-in.c pipe-out.c pipe-bidi.c w32spawn.h

Include:
"pipe.h"

Maintainer:
Bruno Haible

============================= lib/pipe.h =============================
/* Creation of subprocesses, communicating via pipes.
   Copyright (C) 2001-2003 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef _PIPE_H
#define _PIPE_H

/* Get pid_t.  */
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>

#include <stdbool.h>


#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 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
 *
 */
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
 *
 */
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 _MSC_VER || defined __MINGW32__
/* Native Woe32 API.  */
# define DEV_NULL "NUL"
#else
/* Unix API.  */
# define DEV_NULL "/dev/null"
#endif


#ifdef __cplusplus
}
#endif


#endif /* _PIPE_H */
============================= lib/pipe-in.c =============================
/* Creation of subprocesses, communicating via pipes.
   Copyright (C) 2001-2003 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

/* Specification.  */
#include "pipe.h"

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "error.h"
#include "exit.h"
#include "fatal-signal.h"
#include "wait-process.h"
#include "gettext.h"

#define _(str) gettext (str)

#if defined _MSC_VER || defined __MINGW32__

/* Native Woe32 API.  */
# include <process.h>
# include "w32spawn.h"

#else

/* Unix API.  */
# ifdef HAVE_POSIX_SPAWN
#  include <spawn.h>
# else
#  ifdef HAVE_VFORK_H
#   include <vfork.h>
#  endif
# endif

#endif

#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif


#ifdef EINTR

/* EINTR handling for close(), open().
   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 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])
{
#if defined _MSC_VER || defined __MINGW32__

  /* 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 orig_stdin;
  int orig_stdout;
  int orig_stderr;
  int child;
  int nulloutfd;
  int stdinfd;

  prog_argv = prepare_spawn (prog_argv);

  if (_pipe (ifd, 4096, O_BINARY | O_NOINHERIT) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           read        system         write
 *    parent  <-  ifd[0]   <-   ifd[1]   <-   child
 */

  /* Save standard file handles of parent process.  */
  if (prog_stdin != NULL)
    orig_stdin = dup_noinherit (STDIN_FILENO);
  orig_stdout = dup_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_noinherit (STDERR_FILENO);
  child = -1;

  /* Create standard file handles of child process.  */
  nulloutfd = -1;
  stdinfd = -1;
  if (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))))
      && (prog_stdin == NULL
          || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
              && (stdinfd == STDIN_FILENO
                  || (dup2 (stdinfd, STDIN_FILENO) >= 0
                      && close (stdinfd) >= 0)))))
    /* The child process doesn't inherit ifd[0] and ifd[1], but it
       inherits all open()ed or dup2()ed file handles (which is what
       we want in the case of STD*_FILENO) and also orig_stdin,
       orig_stdout, orig_stderr (which is not explicitly wanted but
       harmless).  */
    child = spawnvp (P_NOWAIT, prog_path, prog_argv);
  if (stdinfd >= 0)
    close (stdinfd);
  if (nulloutfd >= 0)
    close (nulloutfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
  dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
  if (prog_stdin != NULL)
    dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);

  close (ifd[1]);
  if (child == -1)
    {
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ifd[0]);
      return -1;
    }

  fd[0] = ifd[0];
  return child;

#else

  /* Unix API.  */
  int ifd[2];
#if HAVE_POSIX_SPAWN
  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;
#else
  int child;
#endif

  if (pipe (ifd) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           read        system         write
 *    parent  <-  ifd[0]   <-   ifd[1]   <-   child
 */

#if HAVE_POSIX_SPAWN
  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,
          (err = posix_spawn_file_actions_adddup2 (&actions,
                                                   ifd[1], STDOUT_FILENO)) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) != 0
          || (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)
          || (prog_stdin != NULL
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDIN_FILENO,
                                                          prog_stdin, O_RDONLY,
                                                          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);
      close (ifd[0]);
      close (ifd[1]);
      return -1;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
#else
  if (slave_process)
    block_fatal_signals ();
  /* Use vfork() instead of fork() for efficiency.  */
  if ((child = vfork ()) == 0)
    {
      /* Child process code.  */
      int nulloutfd;
      int stdinfd;

      if (dup2 (ifd[1], STDOUT_FILENO) >= 0
          && close (ifd[1]) >= 0
          && close (ifd[0]) >= 0
          && (!null_stderr
              || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
                  && (nulloutfd == STDERR_FILENO
                      || (dup2 (nulloutfd, STDERR_FILENO) >= 0
                          && close (nulloutfd) >= 0))))
          && (prog_stdin == NULL
              || ((stdinfd = open (prog_stdin, O_RDONLY, 0)) >= 0
                  && (stdinfd == STDIN_FILENO
                      || (dup2 (stdinfd, STDIN_FILENO) >= 0
                          && close (stdinfd) >= 0))))
          && (!slave_process || (unblock_fatal_signals (), true)))
        execvp (prog_path, prog_argv);
      _exit (127);
    }
  if (child == -1)
    {
      if (slave_process)
        unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ifd[0]);
      close (ifd[1]);
      return -1;
    }
#endif
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }
  close (ifd[1]);

  fd[0] = ifd[0];
  return child;

#endif
}
============================= lib/pipe-out.c =============================
/* Creation of subprocesses, communicating via pipes.
   Copyright (C) 2001-2003 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

/* Specification.  */
#include "pipe.h"

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "error.h"
#include "exit.h"
#include "fatal-signal.h"
#include "wait-process.h"
#include "gettext.h"

#define _(str) gettext (str)

#if defined _MSC_VER || defined __MINGW32__

/* Native Woe32 API.  */
# include <process.h>
# include "w32spawn.h"

#else

/* Unix API.  */
# ifdef HAVE_POSIX_SPAWN
#  include <spawn.h>
# else
#  ifdef HAVE_VFORK_H
#   include <vfork.h>
#  endif
# endif

#endif

#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif


#ifdef EINTR

/* EINTR handling for close(), open().
   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 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])
{
#if defined _MSC_VER || defined __MINGW32__

  /* 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
     source code.  */
  int ofd[2];
  int orig_stdin;
  int orig_stdout;
  int orig_stderr;
  int child;
  int nulloutfd;
  int stdoutfd;

  prog_argv = prepare_spawn (prog_argv);

  if (_pipe (ofd, 4096, O_BINARY | O_NOINHERIT) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child
 */

  /* Save standard file handles of parent process.  */
  orig_stdin = dup_noinherit (STDIN_FILENO);
  if (prog_stdout != NULL)
    orig_stdout = dup_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_noinherit (STDERR_FILENO);
  child = -1;

  /* Create standard file handles of child process.  */
  nulloutfd = -1;
  stdoutfd = -1;
  if (dup2 (ofd[0], STDIN_FILENO) >= 0
      && (!null_stderr
          || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
              && (nulloutfd == STDERR_FILENO
                  || (dup2 (nulloutfd, STDERR_FILENO) >= 0
                      && close (nulloutfd) >= 0))))
      && (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 ofd[0] and ofd[1], but it
       inherits all open()ed or dup2()ed file handles (which is what
       we want in the case of STD*_FILENO) and also orig_stdin,
       orig_stdout, orig_stderr (which is not explicitly wanted but
       harmless).  */
    child = spawnvp (P_NOWAIT, prog_path, prog_argv);
  if (stdoutfd >= 0)
    close (stdoutfd);
  if (nulloutfd >= 0)
    close (nulloutfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
  if (prog_stdout != NULL)
    dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
  dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);

  close (ofd[0]);
  if (child == -1)
    {
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ofd[1]);
      return -1;
    }

  fd[0] = ofd[1];
  return child;

#else

  /* Unix API.  */
  int ofd[2];
#if HAVE_POSIX_SPAWN
  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;
#else
  int child;
#endif

  if (pipe (ofd) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child
 */

#if HAVE_POSIX_SPAWN
  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,
          (err = posix_spawn_file_actions_adddup2 (&actions,
                                                   ofd[0], STDIN_FILENO)) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
          || (null_stderr
              && (err = posix_spawn_file_actions_addopen (&actions,
                                                          STDERR_FILENO,
                                                          "/dev/null", O_RDWR,
                                                          0))
                 != 0)
          || (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);
      close (ofd[0]);
      close (ofd[1]);
      return -1;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
#else
  if (slave_process)
    block_fatal_signals ();
  /* Use vfork() instead of fork() for efficiency.  */
  if ((child = vfork ()) == 0)
    {
      /* Child process code.  */
      int nulloutfd;
      int stdoutfd;

      if (dup2 (ofd[0], STDIN_FILENO) >= 0
          && close (ofd[0]) >= 0
          && close (ofd[1]) >= 0
          && (!null_stderr
              || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
                  && (nulloutfd == STDERR_FILENO
                      || (dup2 (nulloutfd, STDERR_FILENO) >= 0
                          && close (nulloutfd) >= 0))))
          && (prog_stdout == NULL
              || ((stdoutfd = open (prog_stdout, O_WRONLY, 0)) >= 0
                  && (stdoutfd == STDOUT_FILENO
                      || (dup2 (stdoutfd, STDOUT_FILENO) >= 0
                          && close (stdoutfd) >= 0))))
          && (!slave_process || (unblock_fatal_signals (), true)))
        execvp (prog_path, prog_argv);
      _exit (127);
    }
  if (child == -1)
    {
      if (slave_process)
        unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ofd[0]);
      close (ofd[1]);
      return -1;
    }
#endif
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }
  close (ofd[0]);

  fd[0] = ofd[1];
  return child;
#endif
}
============================= lib/pipe-bidi.c =============================
/* Creation of subprocesses, communicating via pipes.
   Copyright (C) 2001-2003 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

/* Specification.  */
#include "pipe.h"

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "error.h"
#include "exit.h"
#include "fatal-signal.h"
#include "wait-process.h"
#include "gettext.h"

#define _(str) gettext (str)

#if defined _MSC_VER || defined __MINGW32__

/* Native Woe32 API.  */
# include <process.h>
# include "w32spawn.h"

#else

/* Unix API.  */
# ifdef HAVE_POSIX_SPAWN
#  include <spawn.h>
# else
#  ifdef HAVE_VFORK_H
#   include <vfork.h>
#  endif
# endif

#endif

#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#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 inline int
nonintr_close (int fd)
{
  int retval;

  do
    retval = close (fd);
  while (retval < 0 && errno == EINTR);

  return retval;
}
#define close nonintr_close

#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])
{
#if defined _MSC_VER || defined __MINGW32__

  /* 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;

  prog_argv = prepare_spawn (prog_argv);

  if (_pipe (ifd, 4096, O_BINARY | O_NOINHERIT) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
  if (_pipe (ofd, 4096, O_BINARY | O_NOINHERIT) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child
 *    parent  <-   ifd[0]   <-   ifd[1]   <-   child
 *           read         system         write
 *
 */

  /* Save standard file handles of parent process.  */
  orig_stdin = dup_noinherit (STDIN_FILENO);
  orig_stdout = dup_noinherit (STDOUT_FILENO);
  if (null_stderr)
    orig_stderr = dup_noinherit (STDERR_FILENO);
  child = -1;

  /* Create standard file handles of child process.  */
  nulloutfd = -1;
  if (dup2 (ofd[0], STDIN_FILENO) >= 0
      && 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)))))
    /* 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) and also orig_stdin,
       orig_stdout, orig_stderr (which is not explicitly wanted but
       harmless).  */
    child = spawnvp (P_NOWAIT, prog_path, prog_argv);
  if (nulloutfd >= 0)
    close (nulloutfd);

  /* Restore standard file handles of parent process.  */
  if (null_stderr)
    dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr);
  dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout);
  dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin);

  close (ofd[0]);
  close (ifd[1]);
  if (child == -1)
    {
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ifd[0]);
      close (ofd[1]);
      return -1;
    }

  fd[0] = ifd[0];
  fd[1] = ofd[1];
  return child;

#else

  /* Unix API.  */
  int ifd[2];
  int ofd[2];
#if HAVE_POSIX_SPAWN
  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;
#else
  int child;
#endif

  if (pipe (ifd) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
  if (pipe (ofd) < 0)
    error (EXIT_FAILURE, errno, _("cannot create pipe"));
/* Data flow diagram:
 *
 *           write        system         read
 *    parent  ->   ofd[1]   ->   ofd[0]   ->   child
 *    parent  <-   ifd[0]   <-   ifd[1]   <-   child
 *           read         system         write
 *
 */

#if HAVE_POSIX_SPAWN
  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,
          (err = posix_spawn_file_actions_adddup2 (&actions,
                                                   ofd[0], STDIN_FILENO)) != 0
          || (err = posix_spawn_file_actions_adddup2 (&actions,
                                                      ifd[1], STDOUT_FILENO))
             != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ifd[1])) != 0
          || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
          || (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)
          || (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);
      close (ifd[0]);
      close (ifd[1]);
      close (ofd[0]);
      close (ofd[1]);
      return -1;
    }
  posix_spawn_file_actions_destroy (&actions);
  if (attrs_allocated)
    posix_spawnattr_destroy (&attrs);
#else
  if (slave_process)
    block_fatal_signals ();
  /* Use vfork() instead of fork() for efficiency.  */
  if ((child = vfork ()) == 0)
    {
      /* Child process code.  */
      int nulloutfd;

      if (dup2 (ofd[0], STDIN_FILENO) >= 0
          && dup2 (ifd[1], STDOUT_FILENO) >= 0
          && close (ofd[0]) >= 0
          && close (ifd[1]) >= 0
          && close (ofd[1]) >= 0
          && close (ifd[0]) >= 0
          && (!null_stderr
              || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0
                  && (nulloutfd == STDERR_FILENO
                      || (dup2 (nulloutfd, STDERR_FILENO) >= 0
                          && close (nulloutfd) >= 0))))
          && (!slave_process || (unblock_fatal_signals (), true)))
        execvp (prog_path, prog_argv);
      _exit (127);
    }
  if (child == -1)
    {
      if (slave_process)
        unblock_fatal_signals ();
      if (exit_on_error || !null_stderr)
        error (exit_on_error ? EXIT_FAILURE : 0, errno,
               _("%s subprocess failed"), progname);
      close (ifd[0]);
      close (ifd[1]);
      close (ofd[0]);
      close (ofd[1]);
      return -1;
    }
#endif
  if (slave_process)
    {
      register_slave_subprocess (child);
      unblock_fatal_signals ();
    }
  close (ofd[0]);
  close (ifd[1]);

  fd[0] = ifd[0];
  fd[1] = ofd[1];
  return child;

#endif
}
============================= m4/pipe.h =============================
# pipe.m4 serial 1
dnl Copyright (C) 2003 Free Software Foundation, Inc.
dnl This file is free software, distributed under the terms of the GNU
dnl General Public License.  As a special exception to the GNU General
dnl Public License, this file may be distributed as part of a program
dnl that contains a configuration script generated by Autoconf, under
dnl the same distribution terms as the rest of that program.

AC_DEFUN([gl_PIPE],
[
  dnl Prerequisites of lib/pipe.h.
  AC_CHECK_HEADERS_ONCE(unistd.h)
  dnl Prerequisites of lib/pipe-in.c, lib/pipe-out.c, lib/pipe-bidi.c.
  AC_REQUIRE([AC_C_INLINE])
  AC_REQUIRE([AC_TYPE_MODE_T])
  AC_REQUIRE([AC_FUNC_FORK])
  AC_CHECK_FUNCS(posix_spawn)
])





reply via email to

[Prev in Thread] Current Thread [Next in Thread]