summaryrefslogtreecommitdiff
path: root/src/tee.c
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2015-02-16 12:55:40 +0000
committerPádraig Brady <P@draigBrady.com>2015-02-24 18:12:22 +0000
commitfdd6ebf0ae786681e99afdaf1ef44bb750e66b2d (patch)
tree522b47c895184ea102345caa3bbea280dd223255 /src/tee.c
parent19d34d244b46227e83ec43969f99c5e3b23ea01d (diff)
downloadcoreutils-fdd6ebf0ae786681e99afdaf1ef44bb750e66b2d.tar.xz
tee: add --write-error to control handling of closed pipes
tee is very often used with pipes and this gives better control when writing to them. There are 3 classes of file descriptors that tee can write to: files(1), pipes(2), and early close pipes(3). Handling write errors to 1 & 2 is supported at present with the caveat that failure writing to any pipe will terminate tee immediately. Handling write errors to type 3 is not currently supported. To improve the supported combinations we add these options: --write-error=warn Warn if error writing any output including pipes. Allows continued writing to still open files/pipes. Exit status is failure if any output had error. --write-error=warn-nopipe, -p Warn if error writing any output except pipes. Allows continued writing to still open files/pipes. Exit status is failure if any non pipe output had error. --write-error=exit Exit if error writing any output including pipes. --write-error=exit-nopipe Exit if error writing any output except pipes. Use the "nopipe" variants when files are of types 1 and 3, otherwise use the standard variants with types 1 and 2. A caveat with the above scheme is that a combination of pipe types (2 & 3) is not supported robustly. I.e. if you use the "nopipe" variants when using both type 2 and 3 pipes, then any "real" errors on type 2 pipes will not be diagnosed. Note also a general issue with type 3 pipes that are not on tee's stdout, is that shell constructs don't allow to distinguish early close from real failures. For example `tee >(head -n1) | grep -m1 ..` can't distinguish between an error or an early close in "head" pipe, while the fail on the grep part of the pipe is distinguished independently from the resulting pipe errors. This is a general issue with the >() construct, rather than with tee itself. * NEWS: Mention the new feature. * doc/coreutils.texi (tee invocation): Describe the new option. * src/tee.c (usage): Likewise. (main): With --write-error ignore SIGPIPE, and handle the various exit, diagnostics combinations. * tests/misc/tee.sh: Tess all the new options. Fixes http://bugs.gnu.org/11540
Diffstat (limited to 'src/tee.c')
-rw-r--r--src/tee.c65
1 files changed, 62 insertions, 3 deletions
diff --git a/src/tee.c b/src/tee.c
index 27bd2a45d..c163184cb 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -22,6 +22,7 @@
#include <getopt.h>
#include "system.h"
+#include "argmatch.h"
#include "error.h"
#include "fadvise.h"
#include "stdio--.h"
@@ -43,15 +44,38 @@ static bool append;
/* If true, ignore interrupts. */
static bool ignore_interrupts;
+enum write_error
+ {
+ write_error_sigpipe, /* traditional behavior, sigpipe enabled. */
+ write_error_warn, /* warn on EPIPE, but continue. */
+ write_error_warn_nopipe, /* ignore EPIPE, continue. */
+ write_error_exit, /* exit on any write error. */
+ write_error_exit_nopipe /* exit on any write error except EPIPE. */
+ };
+
+static enum write_error write_error;
+
static struct option const long_options[] =
{
{"append", no_argument, NULL, 'a'},
{"ignore-interrupts", no_argument, NULL, 'i'},
+ {"write-error", optional_argument, NULL, 'p'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
+static char const *const write_error_args[] =
+{
+ "warn", "warn-nopipe", "exit", "exit-nopipe", NULL
+};
+static enum write_error const write_error_types[] =
+{
+ write_error_warn, write_error_warn_nopipe,
+ write_error_exit, write_error_exit_nopipe
+};
+ARGMATCH_VERIFY (write_error_args, write_error_types);
+
void
usage (int status)
{
@@ -66,8 +90,23 @@ Copy standard input to each FILE, and also to standard output.\n\
-a, --append append to the given FILEs, do not overwrite\n\
-i, --ignore-interrupts ignore interrupt signals\n\
"), stdout);
+ fputs (_("\
+ -p, --write-error[=MODE] behavior on write error. See MODE details below\n\
+"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\
+\n\
+MODE determines behavior with write errors on the outputs:\n\
+ 'warn' diagnose errors writing to any output\n\
+ 'warn-nopipe' diagnose errors writing to any output not a pipe\n\
+ 'exit' exit on error writing to any output\n\
+ 'exit-nopipe' exit on error writing to any output not a pipe\n\
+The default MODE for the -p option is 'warn-nopipe'.\n\
+The default operation when --write-error is not specified, is to\n\
+exit immediately on error writing to a pipe, and diagnose errors\n\
+writing to non pipe outputs.\n\
+"), stdout);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -90,7 +129,7 @@ main (int argc, char **argv)
append = false;
ignore_interrupts = false;
- while ((optc = getopt_long (argc, argv, "ai", long_options, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "aip", long_options, NULL)) != -1)
{
switch (optc)
{
@@ -102,6 +141,14 @@ main (int argc, char **argv)
ignore_interrupts = true;
break;
+ case 'p':
+ if (optarg)
+ write_error = XARGMATCH ("--write-error", optarg, write_error_args,
+ write_error_types);
+ else
+ write_error = write_error_warn_nopipe;
+ break;
+
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -114,6 +161,9 @@ main (int argc, char **argv)
if (ignore_interrupts)
signal (SIGINT, SIG_IGN);
+ if (write_error != write_error_sigpipe)
+ signal (SIGPIPE, SIG_IGN);
+
/* Do *not* warn if tee is given no file arguments.
POSIX requires that it work when given no arguments. */
@@ -193,11 +243,20 @@ tee_files (int nfiles, const char **files)
if (descriptors[i]
&& fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)
{
- error (0, errno, "%s", files[i]);
+ int w_errno = errno;
+ bool fail = errno != EPIPE || (write_error == write_error_exit
+ || write_error == write_error_warn);
if (descriptors[i] == stdout)
clearerr (stdout); /* Avoid redundant close_stdout diagnostic. */
+ if (fail)
+ {
+ error (write_error == write_error_exit
+ || write_error == write_error_exit_nopipe,
+ w_errno, "%s", files[i]);
+ }
descriptors[i] = NULL;
- ok = false;
+ if (fail)
+ ok = false;
n_outputs--;
}