coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] tee: recover from EAGAIN returned from fwrite()


From: Kamil Dudka
Subject: [PATCH] tee: recover from EAGAIN returned from fwrite()
Date: Thu, 6 Sep 2018 16:28:27 +0200

tee expects the output descriptors to operate in blocking mode but this
assumption might be invalidated by other programs connected to the same
terminal, as in the following example:

$ telnet ... | tee log_file

telnet calls ioctl(stdin, FIONBIO, [1]), which causes the O_NONBLOCK
flag to be set on tee's output, which is connected to the same terminal.

This patch has zero impact unless EAGAIN returns from fwrite().  In that
case we try to reset the O_NONBLOCK flag on the output file descriptor
and retry the call to fwrite().
---
 src/tee.c | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/src/tee.c b/src/tee.c
index dae0f1e50..aa9d5caef 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -176,6 +176,43 @@ main (int argc, char **argv)
   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
+/* wrapper of fwrite() that tries to recover from EAGAIN caused by externally
+   set O_NONBLOCK flag on the output file descriptor */
+static bool
+write_buf (const char *buf, ssize_t size, FILE *f)
+{
+  int fd;
+  int flags;
+
+  if (fwrite (buf, size, 1, f) == 1)
+    /* successful write */
+    return true;
+  if (errno != EAGAIN)
+    /* non-recoverable write error */
+    return false;
+
+  /* read file descriptor flags */
+  fd = fileno (f);
+  if (fd == -1)
+    goto fail;
+  flags = fcntl (fd, F_GETFL);
+  if (flags == -1)
+    goto fail;
+
+  /* reset the O_NONBLOCK flag on the file descriptor */
+  flags &= ~O_NONBLOCK;
+  if (fcntl (fd, F_SETFL, flags) == -1)
+    goto fail;
+
+  /* ... and try fwrite() once again in blocking mode */
+  return (fwrite (buf, size, 1, f) == 1);
+
+fail:
+  /* preserve the original errno value in case our recovery attempt failed */
+  errno = EAGAIN;
+  return false;
+}
+
 /* Copy the standard input into each of the NFILES files in FILES
    and into the standard output.  As a side effect, modify FILES[-1].
    Return true if successful.  */
@@ -238,7 +275,7 @@ tee_files (int nfiles, char **files)
          Standard output is the first one.  */
       for (i = 0; i <= nfiles; i++)
         if (descriptors[i]
-            && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)
+            && !write_buf (buffer, bytes_read, descriptors[i]))
           {
             int w_errno = errno;
             bool fail = errno != EPIPE || (output_error == output_error_exit
-- 
2.17.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]