>From 7730599b6984d670760dd8fcacae54d7ed0a1496 Mon Sep 17 00:00:00 2001 From: =?utf-8?q?P=C3=A1draig=20Brady?=
Date: Tue, 12 Oct 2010 01:39:58 +0100 Subject: [PATCH] tail: fix checking of currently unavailable directories * src/tail.c (tail_forever_inotify): Handle the case where tail --follow=name with inotify, is not able to add a watch on a specified directory. This may happen due to inotify resource limits or if the directory is currently missing or inaccessible. In all these cases, revert to polling which will try to reopen the file later. * tests/tail-2/F-vs-rename: Fix the endless loop triggered by the above issue. * tests/tail-2/inotify-hash-abuse: Likewise. * tests/tail-2/F-vs-missing: A new test for this failure mode which was until not just triggered on older buggy linux kernels which returned ENOSPC constantly from inotify_add_watch(). * NEWS: Mention the fix. --- NEWS | 3 ++ src/tail.c | 38 ++++++++++++++++++++--------- tests/Makefile.am | 1 + tests/tail-2/F-vs-missing | 49 +++++++++++++++++++++++++++++++++++++++ tests/tail-2/F-vs-rename | 35 ++++++++++++--------------- tests/tail-2/inotify-hash-abuse | 27 ++++++++++++--------- 6 files changed, 110 insertions(+), 43 deletions(-) create mode 100755 tests/tail-2/F-vs-missing diff --git a/NEWS b/NEWS index 7ee18cd..7cbe7bb 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ GNU coreutils NEWS -*- outline -*- tac would perform a double-free when given an input line longer than 16KiB. [bug introduced in coreutils-8.3] + tail -F again notices later changes to a currently unavailable directory. + [bug introduced in coreutils-7.5] + tr now consistently handles case conversion character classes. In some locales, valid conversion specifications caused tr to abort, while in all locales, some invalid specifications were undiagnosed. diff --git a/src/tail.c b/src/tail.c index 75e9d53..14c17de 100644 --- a/src/tail.c +++ b/src/tail.c @@ -1300,9 +1300,10 @@ check_fspec (struct File_spec *fspec, int wd, int *prev_wd) error (EXIT_FAILURE, errno, _("write error")); } -/* Tail N_FILES files forever, or until killed. - Check modifications using the inotify events system. */ -static void +/* Attempt to tail N_FILES files forever, or until killed. + Check modifications using the inotify events system. + Return false on error, or true to revert to polling. */ +static bool tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, double sleep_interval) { @@ -1311,7 +1312,9 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, /* Map an inotify watch descriptor to the name of the file it's watching. */ Hash_table *wd_to_name; - bool found_watchable = false; + bool found_watchable_file = false; + bool found_unwatchable_dir = false; + bool no_inotify_resources = false; bool writer_is_dead = false; int prev_wd; size_t evlen = 0; @@ -1357,7 +1360,10 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, { error (0, errno, _("cannot watch parent directory of %s"), quote (f[i].name)); - continue; + found_unwatchable_dir = true; + /* We revert to polling below. Note invalid uses + of the inotify API will still be diagnosed. */ + break; } } @@ -1367,18 +1373,28 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, { if (errno != f[i].errnum) error (0, errno, _("cannot watch %s"), quote (f[i].name)); + no_inotify_resources |= (errno == ENOSPC); continue; } if (hash_insert (wd_to_name, &(f[i])) == NULL) xalloc_die (); - found_watchable = true; + found_watchable_file = true; } } - if (follow_mode == Follow_descriptor && !found_watchable) - return; + /* Linux kernel 2.6.24 at least has a bug where eventually, ENOSPC is always + returned by inotify_add_watch. In any case we should revert to polling + when there are no inotify resources. Also a specified directory may not + be currently present or accessible, so revert to polling. */ + if (no_inotify_resources || found_unwatchable_dir) + { + /* FIXME: release hash and inotify resources allocated above. */ + return true; + } + if (follow_mode == Follow_descriptor && !found_watchable_file) + return false; prev_wd = f[n_files - 1].wd; @@ -2157,10 +2173,8 @@ main (int argc, char **argv) if (fflush (stdout) != 0) error (EXIT_FAILURE, errno, _("write error")); - tail_forever_inotify (wd, F, n_files, sleep_interval); - - /* The only way the above returns is upon failure. */ - exit (EXIT_FAILURE); + if (!tail_forever_inotify (wd, F, n_files, sleep_interval)) + exit (EXIT_FAILURE); } } #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index a6a0594..41e0cbc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -84,6 +84,7 @@ TESTS = \ cp/link-heap \ tail-2/inotify-hash-abuse \ tail-2/inotify-hash-abuse2 \ + tail-2/F-vs-missing \ tail-2/F-vs-rename \ tail-2/inotify-rotate \ chmod/no-x \ diff --git a/tests/tail-2/F-vs-missing b/tests/tail-2/F-vs-missing new file mode 100755 index 0000000..8daffe6 --- /dev/null +++ b/tests/tail-2/F-vs-missing @@ -0,0 +1,49 @@ +#!/bin/sh +# demonstrate that tail -F works for currently missing dirs +# Before coreutils-8.6, tail -F missing/file would not +# notice any subsequent availability of the missing/file. + +# Copyright (C) 2010 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see