Hi, This program uses the temporary fifo management system that I built for zargs to provide generic process substitution for arguments to a sub-command.
This program has some advantages over the process substitution built into some shells (bash, zsh, ksh, ???): 1. It doesn't rely on having a shell that supports built-in process substitution. 2. By using descriptively named temporary fifos it allows programs that include filenames in output or diagnostic messages to provide more useful information than with '/dev/fd/*' inputs. 3. It supports `--files0-from=F' style argument passing, as well. Examples: Where in bash you might do: $ uniq bigfile | tee >(wc -l > bigfile-uniq.count) | gzip -c > bigfile-uniq.gz With psub you would do: $ uniq bigfile | psub tee ">wc -l > bigfile-uniq.count" | gzip -c > bigfile-uniq.gz And where in bash you might do: $ wc -l <(ls a) <(ls b) 2 /dev/fd/63 1 /dev/fd/62 3 total With psub you could do: $ psub wc -l "<ls a" "<ls b" 2 /tmp/psub25j3Al/ls a 1 /tmp/psub25j3Al/ls b 3 total Or even: $ find * -maxdepth 0 -type d | sed "s/^/<ls /" | tr '\n' '\0' | psub wc -l --files0-from=- 2 /tmp/psubSZT8xC/ls a 5 /tmp/psubSZT8xC/ls autom4te.cache 1 /tmp/psubSZT8xC/ls b 20 /tmp/psubSZT8xC/ls build-aux 13 /tmp/psubSZT8xC/ls doc 3 /tmp/psubSZT8xC/ls gl 19 /tmp/psubSZT8xC/ls gnulib 269 /tmp/psubSZT8xC/ls gnulib-tests 648 /tmp/psubSZT8xC/ls lib 299 /tmp/psubSZT8xC/ls m4 203 /tmp/psubSZT8xC/ls man 3 /tmp/psubSZT8xC/ls old 121 /tmp/psubSZT8xC/ls po 345 /tmp/psubSZT8xC/ls src 49 /tmp/psubSZT8xC/ls tests 2000 total The attached version doesn't include any `doc/' or `tests/' yet, but should be functional enough to give a sense of usage. If this looks like something that might be a useful addition to coreutils I'll be happy to write some documentation and tests and resubmit. Also available for fetch at: $ git fetch git://repo.or.cz/coreutils/bo.git psub:psub Thanks, Bo
From d61aff113e88ed72efc6d566b45fc487ab91481c Mon Sep 17 00:00:00 2001 From: Bo Borgerson <[EMAIL PROTECTED]> Date: Sun, 27 Apr 2008 20:11:39 -0400 Subject: [PATCH] Add new program: psub * src/psub.c: New program to manage temporary fifos for pipelines. * man/psub.x: Manfile template for new program. * src/Makefile.am: List new program. Signed-off-by: Bo Borgerson <[EMAIL PROTECTED]> --- man/psub.x | 4 + src/Makefile.am | 2 +- src/psub.c | 864 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 869 insertions(+), 1 deletions(-) create mode 100644 man/psub.x create mode 100644 src/psub.c diff --git a/man/psub.x b/man/psub.x new file mode 100644 index 0000000..aaa7313 --- /dev/null +++ b/man/psub.x @@ -0,0 +1,4 @@ +[NAME] +psub \- manage pipelines with temporary fifos +[DESCRIPTION] +.\" Add any additional description here diff --git a/src/Makefile.am b/src/Makefile.am index 668e178..225194b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,7 +37,7 @@ EXTRA_PROGRAMS = \ nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \ shuf sort split sum tac tail tr tsort unexpand uniq wc \ basename date dirname echo env expr factor false \ - id kill logname pathchk printenv printf pwd \ + id kill logname pathchk printenv printf psub pwd \ runcon seq sleep tee \ test true tty whoami yes \ base64 diff --git a/src/psub.c b/src/psub.c new file mode 100644 index 0000000..1834d0c --- /dev/null +++ b/src/psub.c @@ -0,0 +1,864 @@ +/* psub -- manage pipelines with temporary fifos + Copyright (C) 2008 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 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 <http://www.gnu.org/licenses/>. */ + + +/* psub - manage pipelines with temporary fifos + + Written by Bo Borgerson. */ + +#include <config.h> +#include <getopt.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include "system.h" +#include "error.h" +#include "long-options.h" +#include "readtokens0.h" +#include "quote.h" +#include "xstrtol.h" + +#define PROGRAM_NAME "psub" + +#define AUTHORS "Bo Borgerson" + +#ifndef DEFAULT_TMPDIR +# define DEFAULT_TMPDIR "/tmp" +#endif + +/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is + present. */ +#ifndef SA_NOCLDSTOP +# define SA_NOCLDSTOP 0 +/* No sigprocmask. Always 'return' zero. */ +# define sigprocmask(How, Set, Oset) (0) +# define sigset_t int +# if ! HAVE_SIGINTERRUPT +# define siginterrupt(sig, flag) /* empty */ +# endif +#endif + +#define FILES_FROM_OPT "--files0-from=" +#define FILES_FROM_OPT_LEN strlen (FILES_FROM_OPT); + +/* By default we won't put more than this many fifos in a single directory. + Can be overridden on the command-line with the --max-directory-entries + (-M) option. */ +#define DEFAULT_MAX_DIRENTS 128 + +static char *program_name; + +/* ARGs prefixed with this string will be left alone + (the string is stripped). */ +static char *skip_opt = "--psub-skip="; + +static struct option const long_options[] = { + {"max-directory-entries", required_argument, NULL, 'M'}, + {"temporary-directory", required_argument, NULL, 'T'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +static void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("\ +Usage: %s [OPTION]... COMMAND ARG [ARG]...\n\ + Or: %s [OPTION]... COMMAND [OPTION]... --files0-from=F\n\ +"), program_name, program_name); + + printf (_("\ +Examine the ARGs to COMMAND. For any ARG that begins with %s or %s\n\ +create a temporary fifo and set up the rest of ARG as a sub-pipeline\n\ +run with `sh', to either write to or read from the fifo as appropriate.\n\ +"), "<", ">"); + fputs (_("\ +Clean up all the temporary fifos after COMMAND finishes.\n\ +\n\ +Options (must come before COMMAND):\n\ +"), stdout); + printf (_("\ + -T, --temporary-directory=DIR put temporary directories in DIR,\n\ + not $TMPDIR or %s;\n\ + -M, --max-directory-entries=MAX put at most MAX fifos in any directory;\n\ + create a new directory after MAX\n\ +"), DEFAULT_TMPDIR); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\ +\n\ +Interpreted options to COMMAND:\n\ +\n\ +"), stdout); + fputs (_("\ + --psub-skip=ARG pass this ARG through unaffected\n\ + --files0-from=F read additional ARGs from F and pipe the modified\n\ + set to COMMAND through a fifo; F is replaced with\n\ + the name of the fifo.\n\ +"), stdout); + emit_bug_reporting_address (); + } + exit (status); +} + +/* The set of signals we need to look out for. */ +static int const all_sig[] = +{ + /* The usual suspects. */ + SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM, +#ifdef SIGPOLL + SIGPOLL, +#endif +#ifdef SIGPROF + SIGPROF, +#endif +#ifdef SIGVTALRM + SIGVTALRM, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif +}; + +enum { nsigs = sizeof all_sig / sizeof all_sig[0] }; + +/* The total number of forked processes that have not been reaped yet. */ +static size_t nprocs; + +/* The PID of our main child (the one who will be reading from our fifos). */ +static int command_pid; + +/* The exit status of our main child. */ +static int command_status; + +/* A list of the names of our fifos (so we can unlink them later). */ +static char **fifos; + +/* How many fifos are in our list. */ +static size_t fifo_count; + +/* How many fifos we've got space allocated for currently. */ +static size_t fifo_alloc; + +/* A list of temporary directories containing our fifos. */ +static char **fifo_dirs; + +/* How many temporary directories we have in our list. */ +static size_t fifo_dir_count = 0; + +/* How many directories we have space allocated for currently. */ +static size_t fifo_dir_alloc = 0; + +/* Directory where we create our temporary directories. */ +static char *fifo_dir_base; + +/* How many fifos we've put into the last temporary directory we created. */ +static size_t cur_fifo_dir_fifos = 0; + +/* How many fifos we'll put into a directory before creating a new one. + Can be set on the command-line with --max-directory-entries (-M). */ +static size_t max_fifo_dir_fifos = DEFAULT_MAX_DIRENTS; + +/* If our COMMAND uses --files0-from=F, then we need to take ARGs from that + file, and transform the --files0-from=F option to point to a fifo we'll + write to. */ +static char *files_from = NULL; + +/* The length first of the prefix "--files0-from=", then of our full + replacement */ +static size_t files_from_opt_len; + +/* The string containing first the prefix "--files0-from", then our + full replacement. */ +static char *files_from_opt; + +/* The index in the ARG list of the --files0-from option. */ +static int files_from_opt_i = 0; + +/* The set of signals that are caught. */ +static sigset_t caught_signals; + +/* Critical section status. */ +struct cs_status +{ + bool valid; + sigset_t sigs; +}; + +/* Enter a critical section. */ +static struct cs_status +cs_enter (void) +{ + struct cs_status status; + status.valid = (sigprocmask (SIG_BLOCK, + &caught_signals, &status.sigs) == 0); + return status; +} + +/* Leave a critical section. */ +static void +cs_leave (struct cs_status status) +{ + if (status.valid) + { + /* Ignore failure when restoring the signal mask. */ + sigprocmask (SIG_SETMASK, &status.sigs, NULL); + } +} + +/* Clean up after ourselves. First unlink fifos, then rmdir directories. + Free memory, even though we currently only clean up at exit. */ + +static void +cleanup (void) +{ + int i; + + for (i = 0; i < fifo_count; i++) + { + unlink (fifos[i]); + free (fifos[i]); + } + if (fifos) + { + free (fifos); + fifos = NULL; + } + for (i = 0; i < fifo_dir_count; i++) + { + if(rmdir (fifo_dirs[i]) == -1) + error (0, errno, _("rmdir failed: %s"), fifo_dirs[i]); + free (fifo_dirs[i]); + fifo_dirs[i] = NULL; + } + if (fifo_dirs) + { + free (fifo_dirs); + fifo_dirs = NULL; + } + if (fifo_dir_base) + { + free (fifo_dir_base); + fifo_dir_base = NULL; + } +} + +/* Cleanup actions to take when exiting. */ + +static void +exit_cleanup (void) +{ + /* Clean up in a critical section so that a signal handler does not + try to clean up too. */ + struct cs_status cs = cs_enter (); + cleanup (); + cs_leave (cs); + + close_stdout (); +} + +/* Handle interrupts and hangups. */ + + +static void +sighandler (int sig) +{ + size_t i; + static int n_sig = 0; + if (! SA_NOCLDSTOP) + signal (sig, SIG_IGN); + + cleanup (); + + for (i = 0; i < (1<<30); i++); + + signal (sig, SIG_DFL); + raise (sig); +} + +/* Wrapper around dup2 for convenience. */ + +static void +dup2_or_die (int oldfd, int newfd) +{ + if (dup2 (oldfd, newfd) < 0) + error (EXIT_FAILURE, errno, _("dup2 failed")); +} + +/* If 0 < PID, wait for the child process with that PID to exit. + If PID is -1, clean up a random child process which has finished. + If PID is -1 and no processes have quit yet, return 0 without waiting. */ + +static pid_t +reap (pid_t pid) +{ + int status; + pid_t cpid = waitpid (pid, &status, pid < 0 ? WNOHANG : 0); + + if (cpid < 0) + error (EXIT_FAILURE, errno, _("waitpid")); + + else if (0 < cpid) + { + if (! WIFEXITED (status) || WEXITSTATUS (status)) + error (0, 0, _("%s terminated abnormally"), + (cpid == command_pid)?"command":"child"); + if (cpid == command_pid) + command_status = status; + else + --nprocs; + } + + return cpid; +} + +/* Keep reaping finished children as long as there are more to reap. + This doesn't block waiting for any of them, it only reaps those + that are already dead. This is the sigaction handler for CHLD. */ + +static void +reap_some (int sig) +{ + pid_t pid; + + while (0 < nprocs && (pid = reap (-1))); + +} + +/* Fork a child process. Don't let the child know where the fifos + and temporary directories are. We want to clean them up ourselves. + Return the PID of the child or -1 if fork failed. */ + +static pid_t +safe_fork (void) +{ +#if HAVE_WORKING_FORK + char **saved_fifos; + int saved_errno; + int i; + unsigned int wait_retry = 1; + pid_t pid IF_LINT (= -1); + struct cs_status cs; + + + /* This is so the child process won't delete our temp files + if it receives a signal before exec-ing. */ + cs = cs_enter (); + saved_fifos = fifos; + fifos = NULL; + + pid = fork (); + saved_errno = errno; + if (pid) + fifos = saved_fifos; + + if (0 == pid) + for (i = 0; i < nsigs; i++) + signal (all_sig[i], SIG_DFL); + + cs_leave (cs); + errno = saved_errno; + + if (0 < pid) + ++nprocs; + + return pid; + +#else /* ! HAVE_WORKING_FORK */ + return -1; +#endif +} + +/* Create a temporary directory into which we'll put fifos. */ + +static void +create_fifo_dir (void) +{ + static char const slashbase[] = "/psubXXXXXX"; + struct cs_status cs; + int saved_errno; + size_t len = strlen (fifo_dir_base); + char *success; + char *fifo_dir; + + if (fifo_dir_count == fifo_dir_alloc) + fifo_dirs = X2NREALLOC (fifo_dirs, &fifo_dir_alloc); + + /* One extra for the trailing slash */ + fifo_dirs[fifo_dir_count] = xmalloc (len + sizeof slashbase + 1); + + fifo_dir = fifo_dirs[fifo_dir_count++]; + + memcpy (fifo_dir, fifo_dir_base, len); + memcpy (fifo_dir + len, slashbase, sizeof slashbase); + + + /* Create the directory in a critical section, to avoid races. */ + cs = cs_enter (); + success = mkdtemp (fifo_dir); + saved_errno = errno; + cs_leave (cs); + errno = saved_errno; + + if (!success) + { + /* Note that the last FIFO_DIR doesn't get freed, but we're exiting + anyway. */ + fifo_dir_count--; + error (EXIT_FAILURE, errno, + _("cannot create temporary directory: %s"), fifo_dir); + } + + /* Append a slash so we don't have to worry about it later. */ + fifo_dir[len + sizeof slashbase - 1] = '/'; + fifo_dir[len + sizeof slashbase] = '\0'; + + cur_fifo_dir_fifos = 0; +} + +/* Use DIR as our base temp dir. */ +static void +set_temp_dir (char const *dir) +{ + size_t len; + if (fifo_dir_base) + { + if (!STREQ (fifo_dir_base, dir)) + error (EXIT_FAILURE, 0, _("multiple temp dirs specified")); + } + else + { + len = strlen (dir); + fifo_dir_base = xmalloc (len + 1); + memcpy (fifo_dir_base, dir, len); + } +} + +/* Create a new fifo with a name resembling the file or command it's + associated with. */ +static char * +new_fifo (char const *file_name) +{ + char *fifo_tag = strrchr (file_name, '/'); + size_t len1, len2, len3 = 0; + size_t len3_alloc = INT_BUFSIZE_BOUND (unsigned int) + 1; + char i_str [INT_BUFSIZE_BOUND (unsigned int) + 1]; + unsigned int i = 0; + int still_trying = 1; + char *fifo; + char *fifo_dir; + + if (fifo_tag) + fifo_tag++; + else + fifo_tag = (char *)file_name; + + if (fifo_count == fifo_alloc) + fifos = X2NREALLOC (fifos, &fifo_alloc); + + if (!fifo_dir_count || cur_fifo_dir_fifos == max_fifo_dir_fifos) + create_fifo_dir (); + + cur_fifo_dir_fifos++; + + fifo_dir = fifo_dirs[fifo_dir_count - 1]; + + len1 = strlen (fifo_dir); + len2 = strlen (fifo_tag); + + fifos[fifo_count] = xmalloc (len1 + len2 + len3_alloc); + + fifo = fifos[fifo_count++]; + + memcpy (fifo, fifo_dir, len1); + memcpy (fifo + len1, fifo_tag, len2 + 1); + + /* There may be files with the same name in different directories, so + we need to handle collisions. This gets inefficient as we get more + identical filenames. */ + while (still_trying && (i < UINT_MAX)) + { + + if (i) + { + sprintf (i_str, "-%d", i); + len3 = strlen (i_str); + memcpy (fifo + len1 + len2, i_str, len3 + 1); + } + + if ((still_trying = mkfifo (fifo, 0600)) != 0) + { + if (errno != EEXIST) + error (EXIT_FAILURE, errno, _("cannot create fifo %s"), + quote (fifo)); + } + + i++; + } + + return fifo; +} + +/* If this ARG looks like an option, return it. Otherwise, return NULL. + Also, if it looks like a --files0-from option, set up for files_from + processing. */ +static char * +handle_option (int i, char *p) +{ + + static size_t skip_opt_len = -1; + + if (skip_opt_len == -1) + skip_opt_len = strlen (skip_opt); + + if (!strncmp (p, files_from_opt, files_from_opt_len)) + { + files_from = p + files_from_opt_len; + files_from_opt_i = i; + return p; + } + else if (!strncmp (p, skip_opt, skip_opt_len)) + return p + skip_opt_len; + else if (*p == '-') + return p; + + return NULL; +} + +/* Treat this ARG as a sub-command to be executed. Hook its output up to + a fifo, and return the name of the fifo. */ +static char * +handle_exec (int i, char *p) +{ + + if (p[0] == '<' || p[0] == '>') + { + int pipefd; + char control_char = *p++; + char *fifo = new_fifo (p); + + pid_t helper_pid = safe_fork (); + + if (helper_pid > 0) + { + + return fifo; + + } + else if (helper_pid == 0) + { + + close (STDIN_FILENO); + close (STDOUT_FILENO); + + if (control_char == '>') + { + /* This will block. */ + if ((pipefd = open (fifo, O_RDONLY)) == -1) + error (EXIT_FAILURE, errno, _("open failed: %s"), fifo); + + dup2_or_die (pipefd, STDIN_FILENO); + } + else + { + /* This will block. */ + if ((pipefd = open (fifo, O_WRONLY)) == -1) + error (EXIT_FAILURE, errno, _("open failed: %s"), fifo); + + dup2_or_die (pipefd, STDOUT_FILENO); + } + + execlp ("sh", "sh", "-c", p, NULL); + + error (EXIT_FAILURE, errno, _("execlp failed: sh -c %s"), quote (p)); + + } + + error (EXIT_FAILURE, errno, _("fork failed")); + } + +} + + + +/* Cascade through ARG handlers in order of precedence. Return the new + value to go in ARG's place. */ +static char * +handle_arg (int i, char *p) +{ + char *retval; + + if ((retval = handle_option (i, p)) != NULL) + return retval; + + if ((retval = handle_exec (i, p)) != NULL) + return retval; + + return p; +} + +/* Specify the maximum number of fifos we'll create in a single directory + before creating a new temporary directory. */ +static void +specify_max_dirent (int oi, char c, char const *s) +{ + uintmax_t n; + enum strtol_error e = xstrtoumax (s, NULL, 10, &n, ""); + + if (e == LONGINT_OK) + { + max_fifo_dir_fifos = n; + if (n == max_fifo_dir_fifos) + { + if (0 < max_fifo_dir_fifos) + return; + e = LONGINT_INVALID; + } + else + e = LONGINT_OVERFLOW; + } + + xstrtol_fatal (e, oi, c, long_options, s); +} + + +int +main (int argc, char **argv) +{ + int c = 0, i; + int oi = -1; + char **new_argv; + struct fstatus *fstatus; + struct Tokens tok; + char **files; + char *fifo; + int nfiles = 0; + + files_from_opt_len = FILES_FROM_OPT_LEN; + files_from_opt = xmalloc (files_from_opt_len + 1); + + memcpy (files_from_opt, FILES_FROM_OPT, files_from_opt_len); + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + initialize_exit_failure (EXIT_FAILURE); + atexit (exit_cleanup); + + /* This signal handler setup block is basically lifted straight out of + sort.c, which also manages temporary files that need to be cleaned up + before exiting, if possible. */ + { + size_t i; + +#if SA_NOCLDSTOP + struct sigaction act; + + sigemptyset (&caught_signals); + for (i = 0; i < nsigs; i++) + { + sigaction (all_sig[i], NULL, &act); + if (act.sa_handler != SIG_IGN) + sigaddset (&caught_signals, all_sig[i]); + } + + act.sa_handler = sighandler; + act.sa_mask = caught_signals; + act.sa_flags = 0; + + for (i = 0; i < nsigs; i++) + if (sigismember (&caught_signals, all_sig[i])) + sigaction (all_sig[i], &act, NULL); +#else + for (i = 0; i < nsigs; i++) + if (signal (all_sig[i], SIG_IGN) != SIG_IGN) + { + signal (all_sig[i], sighandler); + siginterrupt (all_sig[i], 1); + } +#endif + } + + + parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION, + usage, AUTHORS, (char const *) NULL); + + while ((c = getopt_long (argc, argv, "+T:M:o", long_options, &oi)) != -1) + switch (c) + { + + case 'T': + set_temp_dir (optarg); + break; + + case 'M': + specify_max_dirent (oi, c, optarg); + break; + + default: + usage (EXIT_FAILURE); + break; + } + + if (argc - optind < 2) + usage (EXIT_FAILURE); + + if (!fifo_dir_base) + { + char const *tmp_dir = getenv ("TMPDIR"); + set_temp_dir (tmp_dir ? tmp_dir : DEFAULT_TMPDIR); + } + + argc -= optind; + argv += optind; + + new_argv = xnmalloc (argc + 1, sizeof *argv); + memcpy (new_argv, argv, (argc + 1) * sizeof *argv); + + for (i = 1; i < argc; i++) + { + char *p = new_argv[i]; + + new_argv[i] = handle_arg (i, new_argv[i]); + } + + if (files_from) + { + FILE *stream; + + if (STREQ (files_from, "-")) + stream = stdin; + else + { + stream = fopen (files_from, "r"); + if (stream == NULL) + error (EXIT_FAILURE, errno, _("cannot open %s for reading"), + quote (files_from)); + } + + readtokens0_init (&tok); + + if (! readtokens0 (stream, &tok)) + error (EXIT_FAILURE, 0, _("cannot read file names from %s"), + quote (files_from)); + + files = tok.tok; + nfiles = tok.n_tok; + } + + if (files_from) + { + pid_t writer_pid; + + for (i = 0; i < nfiles; i++) + if ((fifo = handle_arg (i, files[i])) != NULL) + files[i] = fifo; + + fifo = new_fifo (files_from); + + writer_pid = safe_fork (); + + if (0 < writer_pid) + { + size_t fifo_len = strlen (fifo) + 1; + files_from_opt = xrealloc (files_from_opt, + files_from_opt_len + fifo_len); + + /* Put the new --files0-from=F option in place of the old one. */ + memcpy (files_from_opt + files_from_opt_len, fifo, fifo_len); + new_argv[files_from_opt_i] = files_from_opt; + } + else if (writer_pid == 0) + { + int writefd; + size_t written; + size_t arglen; + /* This will block. */ + if ((writefd = open (fifo, O_WRONLY)) == -1) + error (EXIT_FAILURE, errno, _("open failed: %s"), fifo); + for (i = 0; i < nfiles; i++) + { + arglen = strlen (files[i]) + 1; + if ((written = write (writefd, files[i], arglen)) != arglen) + error (EXIT_FAILURE, errno, _("write failed: %s"), fifo); + } + return 0; + } + else + error (EXIT_FAILURE, errno, _("fork failed")); + } + + + command_pid = safe_fork (); + + if (command_pid > 0) + { + struct sigaction child_handler; + + /* The main child (command) is waited for explicitly below. */ + if (--nprocs) + { + sigemptyset (&child_handler.sa_mask); + + child_handler.sa_handler = reap_some; + + /* Restart the blocking wait() after we clean up zombie helpers. */ + child_handler.sa_flags = SA_RESTART; + + sigaction (SIGCHLD, &child_handler, NULL); + } + + /* This blocks. We're stuck here (except for occasional non-blocking + reap sessions for helper children) until COMMAND is done. */ + reap (command_pid); + + if (WIFEXITED (command_status)) + command_status = WEXITSTATUS (command_status); + else if (WIFSIGNALED (command_status)) + command_status = WTERMSIG (command_status) + 128; + + return command_status; + } + else if (command_pid == 0) + { + + execvp (new_argv[0], new_argv); + + error (EXIT_FAILURE, errno, _("exec of %s failed"), + quote (new_argv[0])); + } + else + error (EXIT_FAILURE, 0, _("fork failed")); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ -- 1.5.4.3
_______________________________________________ Bug-coreutils mailing list Bug-coreutils@gnu.org http://lists.gnu.org/mailman/listinfo/bug-coreutils