mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-24 10:23:31 +08:00
tail: display file headers correctly with inotify
* src/tail.c (tail_forever_inotify): Use the fspec pointer to distinguish previously output files, rather than a descriptor from the inotify event. That event descriptor was that of the parent directory when files were created or renamed etc. (check_fspec): Adjust for the new comparison. Also show the header when the file is truncated, since we show data in this case also. * tests/tail-2/F-headers.sh: A new test case. * tests/local.mk: Reference the new test. * NEWS: Mention the bug fix.
This commit is contained in:
parent
8f2231aa24
commit
6878ae412d
4
NEWS
4
NEWS
@ -56,6 +56,10 @@ GNU coreutils NEWS -*- outline -*-
|
||||
tail --follow consistently outputs all data for a truncated file.
|
||||
[bug introduced in the beginning]
|
||||
|
||||
tail --follow=name correctly outputs headers for multiple files
|
||||
when those files are being created or renamed.
|
||||
[bug introduced in coreutils-7.5]
|
||||
|
||||
** New features
|
||||
|
||||
chroot accepts the new --skip-chdir option to not change the working directory
|
||||
|
22
src/tail.c
22
src/tail.c
@ -1046,7 +1046,9 @@ recheck (struct File_spec *f, bool blocking)
|
||||
{
|
||||
/* This happens when one iteration finds the file missing,
|
||||
then the preceding <dev,inode> pair is reused as the
|
||||
file is recreated. */
|
||||
file is recreated. Note this also means the file is redisplayed
|
||||
in --follow=name mode if renamed away from and back to
|
||||
a monitored name. */
|
||||
new_file = true;
|
||||
}
|
||||
else
|
||||
@ -1321,7 +1323,7 @@ wd_comparator (const void *e1, const void *e2)
|
||||
|
||||
/* Output (new) data for FSPEC->fd. */
|
||||
static void
|
||||
check_fspec (struct File_spec *fspec, int wd, int *prev_wd)
|
||||
check_fspec (struct File_spec *fspec, struct File_spec **prev_fspec)
|
||||
{
|
||||
struct stat stats;
|
||||
char const *name;
|
||||
@ -1347,7 +1349,6 @@ check_fspec (struct File_spec *fspec, int wd, int *prev_wd)
|
||||
if (S_ISREG (fspec->mode) && stats.st_size < fspec->size)
|
||||
{
|
||||
error (0, 0, _("%s: file truncated"), name);
|
||||
*prev_wd = wd;
|
||||
xlseek (fspec->fd, 0, SEEK_SET, name);
|
||||
fspec->size = 0;
|
||||
}
|
||||
@ -1355,11 +1356,11 @@ check_fspec (struct File_spec *fspec, int wd, int *prev_wd)
|
||||
&& timespec_cmp (fspec->mtime, get_stat_mtime (&stats)) == 0)
|
||||
return;
|
||||
|
||||
if (wd != *prev_wd)
|
||||
if (fspec != *prev_fspec)
|
||||
{
|
||||
if (print_headers)
|
||||
write_header (name);
|
||||
*prev_wd = wd;
|
||||
*prev_fspec = fspec;
|
||||
}
|
||||
|
||||
uintmax_t bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
|
||||
@ -1391,7 +1392,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
|
||||
bool found_unwatchable_dir = false;
|
||||
bool no_inotify_resources = false;
|
||||
bool writer_is_dead = false;
|
||||
int prev_wd;
|
||||
struct File_spec *prev_fspec;
|
||||
size_t evlen = 0;
|
||||
char *evbuf;
|
||||
size_t evbuf_off = 0;
|
||||
@ -1492,7 +1493,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
|
||||
if (follow_mode == Follow_descriptor && !found_watchable_file)
|
||||
return false;
|
||||
|
||||
prev_wd = f[n_files - 1].wd;
|
||||
prev_fspec = &(f[n_files - 1]);
|
||||
|
||||
/* Check files again. New files or data can be available since last time we
|
||||
checked and before they are watched by inotify. */
|
||||
@ -1524,7 +1525,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
|
||||
}
|
||||
|
||||
/* check for new data. */
|
||||
check_fspec (&f[i], f[i].wd, &prev_wd);
|
||||
check_fspec (&f[i], &prev_fspec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1703,7 +1704,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
|
||||
|
||||
continue;
|
||||
}
|
||||
check_fspec (fspec, ev->wd, &prev_wd);
|
||||
check_fspec (fspec, &prev_fspec);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2316,7 +2317,8 @@ main (int argc, char **argv)
|
||||
|
||||
FIXME-maybe: inotify has a watch descriptor per inode, and hence with
|
||||
our current hash implementation will only --follow data for one
|
||||
of the names when multiple hardlinked files are specified. */
|
||||
of the names when multiple hardlinked files are specified, or
|
||||
for one name when a name is specified multiple times. */
|
||||
if (!disable_inotify && (tailable_stdin (F, n_files)
|
||||
|| any_remote_file (F, n_files)
|
||||
|| any_symlinks (F, n_files)
|
||||
|
@ -172,6 +172,7 @@ all_tests = \
|
||||
tests/tail-2/inotify-hash-abuse2.sh \
|
||||
tests/tail-2/F-vs-missing.sh \
|
||||
tests/tail-2/F-vs-rename.sh \
|
||||
tests/tail-2/F-headers.sh \
|
||||
tests/tail-2/descriptor-vs-rename.sh \
|
||||
tests/tail-2/inotify-rotate.sh \
|
||||
tests/tail-2/inotify-rotate-resources.sh \
|
||||
|
59
tests/tail-2/F-headers.sh
Executable file
59
tests/tail-2/F-headers.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
# Ensure tail -F distinguishes output with the correct headers
|
||||
# Between coreutils 7.5 and 8.23 inclusive, 'tail -F ...' would
|
||||
# not output headers for or created/renamed files in certain cases.
|
||||
|
||||
# Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
|
||||
print_ver_ tail
|
||||
|
||||
check_tail_output()
|
||||
{
|
||||
local delay="$1"
|
||||
grep "$tail_re" out ||
|
||||
{ sleep $delay; return 1; }
|
||||
}
|
||||
|
||||
# Terminate any background tail process
|
||||
cleanup_() { kill $pid 2>/dev/null && wait $pid; }
|
||||
|
||||
# Speedup the non inotify case
|
||||
fastpoll='-s.1 --max-unchanged-stats=1'
|
||||
|
||||
for mode in '' '---disable-inotify'; do
|
||||
rm -f a b out
|
||||
|
||||
tail $mode -F $fastpoll a b > out 2>&1 & pid=$!
|
||||
|
||||
# Wait up to 12.7s for tail to start.
|
||||
tail_re="cannot open 'b'" retry_delay_ check_tail_output .1 7 ||
|
||||
{ cat out; fail=1; }
|
||||
|
||||
echo x > a
|
||||
# Wait up to 12.7s for a's header to appear in the output:
|
||||
tail_re='==> a <==' retry_delay_ check_tail_output .1 7 ||
|
||||
{ echo "$0: a: unexpected delay?"; cat out; fail=1; }
|
||||
|
||||
echo y > b
|
||||
# Wait up to 12.7s for b's header to appear in the output:
|
||||
tail_re='==> b <==' retry_delay_ check_tail_output .1 7 ||
|
||||
{ echo "$0: b: unexpected delay?"; cat out; fail=1; }
|
||||
|
||||
cleanup_
|
||||
done
|
||||
|
||||
Exit $fail
|
Loading…
Reference in New Issue
Block a user