bug-coreutils
[Top][All Lists]
Advanced

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

Re: inotify back end for tail -f on linux


From: Giuseppe Scrivano
Subject: Re: inotify back end for tail -f on linux
Date: Sat, 30 May 2009 19:12:54 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.94 (gnu/linux)

Jim Meyering <address@hidden> writes:

> Note what happens whey you truncate a tracked file.
> With your version, nothing.
>
> With the original, it reports this:
>
>   tail: F: file truncated
>
> Likewise, when tailing with e.g., "tail -F FILE &"
> if you unlink FILE, you get this:
>
>   tail: `FILE' has become inaccessible: No such file or directory
>
> and if you then recreate it:
>
>   tail: `FILE' has appeared;  following end of new file
>
> You get none of that with your patch.

Thank you for the quick review.  I added some messages that are possible
in `Follow_descriptor' mode but I had to rollback to the old polling
mechanism when `Follow_name' is used.  It is not straightforward to
emulate it using inotify because it is not possible to watch a file that
doesn't exist yet or a file that is removed and re-created.
It is possible to watch the parent directory if new files are created
but then the problem is moved on the parent directory that can be
removed too and so on (or am I misunderstanding something with
inotify?).
I tried `inotify_add_watch' with a file that doesn't exist and a file
that exists, removed and re-created, registering them with
IN_ALL_EVENTS.  In the former case I get an error, in the latter I don't
get any event when it is re-created as it is silently dropped from the
watch list when the file is removed.

Is there a way to monitor a path that doesn't exist yet and be alerted
when it is created without polling?

Giuseppe


>From b5cad2d1e51781b18e53eac7b102922319909f5b Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Sat, 30 May 2009 13:31:58 +0200
Subject: [PATCH]     tail: Use inotify if it is available.

    * NEWS: Document the new feature
    * configure.ac: Check if inotify is present.
    * src/tail.c (main): Use the tail_forever inotify version if it
    is possible.
    (tail_forever_inotify): Added new function.
---
 NEWS         |    2 +
 configure.ac |    2 +
 src/tail.c   |  126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 29b09a0..c5a2ef5 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,8 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   sort accepts a new option, --human-numeric-sort (-h): sort numbers
   while honoring human readable suffixes like KiB and MB etc.
 
+  tail uses inotify if it is present.
+
 
 * Noteworthy changes in release 7.4 (2009-05-07) [stable]
 
diff --git a/configure.ac b/configure.ac
index 4eb640e..a63ac0c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -410,6 +410,8 @@ AM_GNU_GETTEXT_VERSION([0.15])
 # For a test of uniq: it uses the $LOCALE_FR envvar.
 gt_LOCALE_FR
 
+AC_CHECK_FUNCS([inotify_init], [AC_DEFINE([HAVE_INOTIFY], [1], [Check for 
libinotify])])
+
 AC_CONFIG_FILES(
   Makefile
   doc/Makefile
diff --git a/src/tail.c b/src/tail.c
index fe34600..3f77ba9 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1,5 +1,5 @@
 /* tail -- output the last part of file(s)
-   Copyright (C) 1989, 90, 91, 1995-2006, 2008 Free Software Foundation, Inc.
+   Copyright (C) 1989, 90, 91, 1995-2006, 2008, 2009 Free Software Foundation, 
Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -45,6 +45,10 @@
 #include "xstrtol.h"
 #include "xstrtod.h"
 
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
+
 /* The official name of this program (e.g., no `g' prefix).  */
 #define PROGRAM_NAME "tail"
 
@@ -1116,6 +1120,114 @@ tail_forever (struct File_spec *f, int nfiles, double 
sleep_interval)
     }
 }
 
+#ifdef HAVE_INOTIFY
+/* Tail NFILES files forever, or until killed.
+   Check modifications using the inotify events system.  */
+static void
+tail_forever_inotify (int wd, struct File_spec *f, int nfiles)
+{
+  /* Create an index to access File_spec by its watch descriptor
+   * in costant time.  */
+  struct File_spec **wd_index;
+  int i;
+  int *watch_fd = xmalloc (sizeof (int) * nfiles);
+  int min_wd = -1;
+  int max_wd = -1;
+  int watched = 0;
+  size_t last;
+
+  for (i = 0; i < nfiles; i++)
+    {
+      watch_fd[i] = -1;
+
+      if (!f[i].ignore)
+        {
+          watch_fd[i] = inotify_add_watch (wd, f[i].name, IN_MODIFY|IN_ATTRIB);
+
+          if (watch_fd[i] < 0)
+            {
+              error (0, errno, _("cannot watch %s"), quote (f[i].name));
+              continue;
+            }
+
+          watched++;
+
+          if (watch_fd[i] > max_wd || max_wd < 0)
+              max_wd = watch_fd[i];
+
+          if (watch_fd[i] < min_wd || min_wd < 0)
+              min_wd = watch_fd[i];
+        }
+    }
+
+  if (!watched)
+    return;
+
+  wd_index = xmalloc (sizeof (struct File_spec**) * (max_wd - min_wd + 1));
+
+  for (i = 0; i < nfiles; i++)
+    if (watch_fd[i] > 0)
+      wd_index[watch_fd[i] - min_wd] = &(f[i]);
+
+  free (watch_fd);
+
+  last = max_wd + 1;
+
+  while (1)
+    {
+      size_t len;
+      struct inotify_event ev;
+      char const *name;
+      struct File_spec *fspec;
+      uintmax_t bytes_read;
+      struct stat stats;
+
+      len = read (wd, &ev, sizeof (struct inotify_event));
+      if (!len && errno == EINTR)
+        continue;
+
+      if (!len)
+        error (EXIT_FAILURE, errno, _("error reading inotify event"));
+
+      fspec = wd_index [ev.wd - min_wd];
+      name = pretty_name (fspec);
+
+      if (ev.mask & IN_ATTRIB)
+        recheck (fspec, false);
+
+      if (!(ev.mask & IN_MODIFY))
+        continue;
+
+      if (fstat (fspec->fd, &stats) != 0)
+        {
+          fspec->fd = -1;
+          fspec->errnum = errno;
+          error (0, errno, "%s", name);
+          continue;
+        }
+
+      if (S_ISREG (fspec->mode) && stats.st_size < fspec->size)
+        {
+          error (0, 0, _("%s: file truncated"), name);
+          last = ev.wd;
+          xlseek (fspec->fd, stats.st_size, SEEK_SET, name);
+          fspec->size = stats.st_size;
+        }
+
+      if (ev.wd != last)
+        {
+          if (print_headers)
+            write_header (name);
+          last = ev.wd;
+        }
+
+      bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
+      fspec->size += bytes_read;
+    }
+
+}
+#endif
+
 /* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
    Return true if successful.  */
 
@@ -1690,7 +1802,17 @@ main (int argc, char **argv)
     ok &= tail_file (&F[i], n_units);
 
   if (forever)
-    tail_forever (F, n_files, sleep_interval);
+    {
+#ifdef HAVE_INOTIFY
+      if (follow_mode == Follow_descriptor)
+        {
+          int wd = inotify_init ();
+          if (wd > 0)
+            tail_forever_inotify (wd, F, n_files);
+        }
+#endif
+      tail_forever (F, n_files, sleep_interval);
+    }
 
   if (have_read_stdin && close (STDIN_FILENO) < 0)
     error (EXIT_FAILURE, errno, "-");
-- 
1.6.3.1





reply via email to

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