summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schier <nicolas@fjasle.eu>2017-12-31 15:26:35 +0100
committerJoey Hess <joeyh@joeyh.name>2017-12-31 11:29:15 -0400
commit5083c319d130bbf5ef1a071d178f1f30aa049e58 (patch)
treee8fba3f62d68ce5384c2098799a9334623c99e51
parent19d1bde3491f737c2d3babd94de0de8eb9d9b1c9 (diff)
downloadmoreutils-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.c53
-rw-r--r--pee.docbook42
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>