coreutils
[Top][All Lists]
Advanced

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

[RFC] have tail always detect broken pipes


From: Pádraig Brady
Subject: [RFC] have tail always detect broken pipes
Date: Wed, 7 Jun 2017 01:00:13 -0700
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0

The following will hang indefinitely, due to no further data
being written through the pipe, and thus no SIGPIPE generated:

  $ tail -f /etc/hosts | sleep 1

A more practical use case might be:

  tail -f file.log | grep -q trigger &&
  process_immediately

Another case where one might notice this is that sometimes
(depending on how slow the reader is) the following will hang,
and you may not notice the issue until data arrives

  tail -f file.log | grep_typo filter

Below is a quick hack to support this,
and tail now terminates promptly for the above cases.

The implementation is a proof on concept done in a few minutes
(and may change from poll() to select() if possible).
A disadvantage is the extra syscalls to do the polling.
Actually I may be able to combine the existing select()
with this new poll(), and thus wait for changes in the
output as well as the inotify descriptor.

cheers,
Pádraig.


diff --git a/src/tail.c b/src/tail.c
index 3582321..7dca1d5 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <getopt.h>
+#include <poll.h>
 #include <sys/types.h>
 #include <signal.h>

@@ -327,6 +328,16 @@ named file in a way that accommodates renaming, removal 
and creation.\n\
   exit (status);
 }

+/* If the output has gone away, then terminate
+   as we would if we had written to this output.  */
+static void
+check_output (void)
+{
+  struct pollfd pfd = {.fd = STDOUT_FILENO, .events = POLLERR};
+  if (poll (&pfd, 1, 0) >= 0 && (pfd.revents & POLLERR))
+    raise (SIGPIPE);
+}
+
 static bool
 valid_file_spec (struct File_spec const *f)
 {
@@ -1244,6 +1255,8 @@ tail_forever (struct File_spec *f, size_t n_files, double 
sleep_interval)
       if ((!any_input || blocking) && fflush (stdout) != 0)
         die (EXIT_FAILURE, errno, _("write error"));

+      check_output ();
+
       /* If nothing was read, sleep and/or check for dead writers.  */
       if (!any_input)
         {
@@ -1577,12 +1590,15 @@ tail_forever_inotify (int wd, struct File_spec *f, 
size_t n_files,

       /* When watching a PID, ensure that a read from WD will not block
          indefinitely.  */
-      if (pid && (len <= evbuf_off))
+      while (len <= evbuf_off)
         {
-          if (writer_is_dead)
-            exit (EXIT_SUCCESS);
+          if (pid)
+            {
+              if (writer_is_dead)
+                exit (EXIT_SUCCESS);

-          writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);
+              writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);
+            }

           struct timeval delay; /* how long to wait for file changes.  */
           if (writer_is_dead)
@@ -1600,9 +1616,14 @@ tail_forever_inotify (int wd, struct File_spec *f, 
size_t n_files,
            int file_change = select (wd + 1, &rfd, NULL, NULL, &delay);

            if (file_change == 0)
-             continue;
+             {
+               check_output ();
+               continue;
+             }
            else if (file_change == -1)
              die (EXIT_FAILURE, errno, _("error monitoring inotify event"));
+           else
+             break;
         }

       if (len <= evbuf_off)



reply via email to

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