diff options
author | Nicolas Schier <nicolas@fjasle.eu> | 2017-12-31 15:26:35 +0100 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2017-12-31 11:29:15 -0400 |
commit | 5083c319d130bbf5ef1a071d178f1f30aa049e58 (patch) | |
tree | e8fba3f62d68ce5384c2098799a9334623c99e51 | |
parent | 19d1bde3491f737c2d3babd94de0de8eb9d9b1c9 (diff) | |
download | moreutils-5083c319d130bbf5ef1a071d178f1f30aa049e58.tar.gz |
pee: ignore SIGPIPE and write errors (Closes: #697052)
Without ignoring SIGPIPE, any command run by 'pee' that exits early will
close the pipe to 'pee' and thus cause a SIGPIPE that terminates 'pee'.
The more convinient (and possible less surprising) way is probably to
simply ignore the SIGPIPE and let all other commands issued by 'pee'
continue without any harm.
The same argumentation goes for ignoring write errors, as any early
exiting child of 'pee' is closing the pipe and thus causing a write
error.
With this patch examples like
seq 100000 | pee 'head -n1' 'tail -n1'
echo foo | pee cat 'echo bar' cat cat
do output the expected lines, in contrast to
seq 100000 | pee --no-ignore-sigpipe --no-ignore-write-errors 'head -n1' 'tail -n1'
echo foo | pee --no-ignore-sigpipe --no-ignore-write-errors cat 'echo bar' cat cat
.
Thanks to Ole Jørgen Brønner.
Signed-off-by: Nicolas Schier <nicolas@fjasle.eu>
-rw-r--r-- | pee.c | 53 | ||||
-rw-r--r-- | pee.docbook | 42 |
2 files changed, 90 insertions, 5 deletions
@@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <sys/types.h> #include <sys/wait.h> @@ -28,12 +29,43 @@ close_pipes(FILE **p, size_t i) int main(int argc, char **argv) { + int ignore_write_error = 1; + int ignore_sigpipe = 1; size_t i, r; FILE **pipes; + int *inactive_pipe; + int inactive_pipes = 0; char buf[BUFSIZ]; + while(argc > 1) { + if (!strcmp(argv[1], "--no-ignore-sigpipe")) { + argc--, argv++; + ignore_sigpipe = 0; + continue; + } else if (!strcmp(argv[1], "--ignore-sigpipe")) { + argc--, argv++; + ignore_sigpipe = 1; + continue; + } else if (!strcmp(argv[1], "--no-ignore-write-errors")) { + argc--, argv++; + ignore_write_error = 0; + continue; + } else if (!strcmp(argv[1], "--ignore-write-errors")) { + argc--, argv++; + ignore_write_error = 1; + continue; + } + break; + } + + if (ignore_sigpipe && (signal(SIGPIPE, SIG_IGN) == SIG_ERR)) { + fprintf(stderr, "Unable to ignore SIGPIPE\n"); + exit(EXIT_FAILURE); + } + pipes = malloc(((argc - 1) * sizeof *pipes)); - if (!pipes) + inactive_pipe = calloc((argc - 1), (sizeof *inactive_pipe)); + if (!pipes || !inactive_pipe) exit(EXIT_FAILURE); for (i = 1; i < argc; i++) { @@ -46,15 +78,28 @@ main(int argc, char **argv) { } } argc--; - + while(!feof(stdin) && (!ferror(stdin))) { r = fread(buf, sizeof(char), BUFSIZ, stdin); for(i = 0; i < argc; i++) { - if (fwrite(buf, sizeof(char), r, pipes[i]) != r) { - fprintf(stderr, "Write error to `%s\'\n", argv[i + 1]); + if (inactive_pipe[i]) + continue; + + if (fwrite(buf, sizeof(char), r, pipes[i]) == r) + continue; + + inactive_pipes++; + + if (!ignore_write_error) + fprintf(stderr, "Write error to `%s\'\n", + argv[i + 1]); + + if (!ignore_write_error || (inactive_pipes == argc)) { close_pipes(pipes, argc); exit(EXIT_FAILURE); } + + inactive_pipe[i] = 1; } } exit(close_pipes(pipes, argc)); diff --git a/pee.docbook b/pee.docbook index f554ad0..4030629 100644 --- a/pee.docbook +++ b/pee.docbook @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., <firstname>Joey</firstname> <surname>Hess</surname> </author> - <date>2006-03-14</date> + <date>2016-12-20</date> </refentryinfo> <refmeta> @@ -51,6 +51,8 @@ with this program; if not, write to the Free Software Foundation, Inc., <refsynopsisdiv> <cmdsynopsis> <command>pee</command> + <arg>--[no-]ignore-sigpipe</arg> + <arg>--[no-]ignore-write-errors</arg> <group choice="opt"> <arg rep="repeat"><replaceable>"command"</replaceable></arg> </group> @@ -70,6 +72,41 @@ with this program; if not, write to the Free Software Foundation, Inc., to stdout, like tee does. If that is desired, use <command>pee cat ...</command></para> </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term><option>--ignore-sigpipe</option></term> + <term><option>--no-ignore-sigpipe</option></term> + <listitem> + <para>Do (not) ignore SIGPIPE. Any command started + by <command>pee</command> might cause a SIGPIPE + when it exists. If you ignore SIGPIPE, you + probably also want to ignore write errors (see + below). Ignoring SIGPIPE is the default + behaviour.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--ignore-write-errors</option></term> + <term><option>--no-ignore-write-errors</option></term> + <listitem> + <para>Do (not) ignore write errors. When a command + started by <command>pee</command> is no more + accepting data via the pipe between itself and + <command>pee</command>, a write error occurs in + <command>pee</command>. If this error is not + ignored, <command>pee</command> is going to + terminate all child processes and exists. Ignoring + write errors is the default behaviour.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> <refsect1> <title>SEE ALSO</title> @@ -77,6 +114,9 @@ with this program; if not, write to the Free Software Foundation, Inc., <para> <citerefentry> <refentrytitle>tee</refentrytitle><manvolnum>1</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>pipe</refentrytitle><manvolnum>7</manvolnum> </citerefentry> </para> |