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 13:50:42 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.94 (gnu/linux)

Hi Jim,

Jim Meyering <address@hidden> writes:

> Thanks for the suggestion.
> Yes, this has been discussed, e.g., in
>
>     http://thread.gmane.org/gmane.comp.gnu.coreutils.bugs/15031/focus=15052
>
> I like the idea and outlined how I'd like to see this functionality
> make it's way into the coreutils.

I read this discussion but looking at the tail source code I didn't see
any difficulty to use inotify.

These are some tests results:

test -e /tmp/files  || mkdir /tmp/files; 
     for i in $(seq 2000); do touch /tmp/files/$i; done

my version:
./timeout -sINT 30s env time ./tail -f /tmp/files/*
0.02user 0.09system 0:30.00elapsed 0%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+232minor)pagefaults 0swaps

system version:
./timeout -sINT 30s env time tail -f /tmp/files/*
0.05user 0.16system 0:29.99elapsed 0%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+236minor)pagefaults 0swaps


I am sure running the test for a longer time would give better
results.  The initial time using inotify is higher as there is need to
create watch descriptors and an index I use for access to the File_spec
in costant time.

What do you think of the attached patch?

Cheers,
Giuseppe


>From aa815abdd3cbf2ad15d769a1f4cb6100e3a975e5 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   |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 105 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..bb0950e 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,93 @@ 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);
+
+          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;
+
+      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.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 +1781,15 @@ main (int argc, char **argv)
     ok &= tail_file (&F[i], n_units);
 
   if (forever)
-    tail_forever (F, n_files, sleep_interval);
+    {
+#ifdef HAVE_INOTIFY
+      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]