Dear Ole, > I'm quite sure the issue is that 'echo' close the stream immediately, > causing a SIGPIPE that kills the pee process. > > [...] > > I'm sure there are cases where one wants the SIGPIPE to abort the > rest of pipes, but there should at least be an option to ignore it.
thanks for your valueable pointers! Reading pipe(7) confirms your statement about SIGPIPE. If there wasn't the backward-compatibility issue, I would like to see 'pee' to always ignore SIGPIPE and write errors to pipes. I am going to forward a patch suggestion to Joey (upstream). Kind regards, Nicolas
From 5dbbbee5d1e2cccf1d1ed9920459cde6254512e8 Mon Sep 17 00:00:00 2001 From: Nicolas Schier <n.sch...@avm.de> Date: Tue, 20 Dec 2016 08:03:24 +0100 Subject: [PATCH v1] pee: ignore SIGPIPE and write errors (Closes: #697052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without ignoring SIGPIPE, any command run by 'pee' that exists 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 to lines. Thanks to Ole Jørgen Brønner. Signed-off-by: Nicolas Schier <n.sch...@avm.de> --- pee.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pee.c b/pee.c index a8565c0..fe312d2 100644 --- a/pee.c +++ b/pee.c @@ -30,10 +30,18 @@ int main(int argc, char **argv) { size_t i, r; FILE **pipes; + int *inactive_pipe; + int inactive_pipes = 0; char buf[BUFSIZ]; + if ((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++) { @@ -50,11 +58,18 @@ main(int argc, char **argv) { 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; + + if (++inactive_pipes == argc) { close_pipes(pipes, argc); exit(EXIT_FAILURE); } + + inactive_pipe[i] = 1; } } exit(close_pipes(pipes, argc)); -- 2.10.2
From 1bee9b1ec034fa2d12a02517fad1a6b2dafbe9d5 Mon Sep 17 00:00:00 2001 From: Nicolas Schier <n.sch...@avm.de> Date: Tue, 20 Dec 2016 08:03:24 +0100 Subject: [PATCH v2] pee: ignore SIGPIPE and write errors (Closes: #697052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without ignoring SIGPIPE, any command run by 'pee' that exists 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 to 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 <n.sch...@avm.de> --- pee.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- pee.docbook | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/pee.c b/pee.c index a8565c0..9243f4e 100644 --- a/pee.c +++ b/pee.c @@ -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> -- 2.10.2
signature.asc
Description: Digital signature