diff options
Diffstat (limited to 'tests/tail-2')
28 files changed, 829 insertions, 327 deletions
diff --git a/tests/tail-2/F-headers.sh b/tests/tail-2/F-headers.sh new file mode 100755 index 00000000..31d0fa6b --- /dev/null +++ b/tests/tail-2/F-headers.sh @@ -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 diff --git a/tests/tail-2/F-vs-missing.sh b/tests/tail-2/F-vs-missing.sh index d47b3022..54fe30a8 100755 --- a/tests/tail-2/F-vs-missing.sh +++ b/tests/tail-2/F-vs-missing.sh @@ -3,7 +3,7 @@ # Before coreutils-8.6, tail -F missing/file would not # notice any subsequent availability of the missing/file. -# Copyright (C) 2010-2014 Free Software Foundation, Inc. +# Copyright (C) 2010-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 @@ -21,10 +21,6 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -debug='---disable-inotify' -debug= -tail $debug -F -s.1 missing/file > out 2>&1 & pid=$! - check_tail_output() { local delay="$1" @@ -32,33 +28,32 @@ check_tail_output() { sleep $delay; return 1; } } -# Wait up to 6.3s for tail to start with diagnostic: -# tail: cannot open 'missing/file' for reading: No such file or directory -tail_re='cannot open' retry_delay_ check_tail_output .1 7 || fail=1 +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -mkdir missing || fail=1 -(cd missing && echo x > file) +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -# Wait up to 6.3s for this to appear in the output: -# "tail: '...' has appeared; following end of new file" -tail_re='has appeared' retry_delay_ check_tail_output .1 7 || - { echo "$0: file: unexpected delay?"; cat out; fail=1; } +for mode in '' '---disable-inotify'; do + rm -rf out missing -kill -HUP $pid + tail $mode -F $fastpoll missing/file > out 2>&1 & pid=$! -cleanup() -{ - local delay="$1" - rm -rf missing || - { sleep $delay; return 1; } -} + # Wait up to 12.7s for tail to start with diagnostic: + # tail: cannot open 'missing/file' for reading: No such file or directory + tail_re='cannot open' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } + + mkdir missing || framework_failure_ + (cd missing && echo x > file) || framework_failure_ + + # Wait up to 12.7s for this to appear in the output: + # "tail: '...' has appeared; following new file" + tail_re='has appeared' retry_delay_ check_tail_output .1 7 || + { echo "$0: file: unexpected delay?"; cat out; fail=1; } + + cleanup_ +done -# Try repeatedly to remove the temporary directory. -# This is normally unnecessary, because the containing directory will -# be removed by code from init.sh. However, when this particular test -# is run on an NFS-mounted volume, sometimes init.sh's cleanup code -# fails because the directory is not yet really empty, perhaps because -# the tail process (reading missing/file) is not yet killed. -retry_delay_ cleanup .1 6 Exit $fail diff --git a/tests/tail-2/F-vs-rename.sh b/tests/tail-2/F-vs-rename.sh index 3e31ec8c..06733fb4 100755 --- a/tests/tail-2/F-vs-rename.sh +++ b/tests/tail-2/F-vs-rename.sh @@ -1,9 +1,9 @@ #!/bin/sh -# demonstrate that tail -F works when renaming the tailed files -# Before coreutils-8.3, tail -F a b would stop tracking additions to b -# after "mv a b". +# Demonstrate that tail -F works when renaming the tailed files. +# Between coreutils 7.5 and 8.2 inclusive, 'tail -F a b' would +# stop tracking additions to b after 'mv a b'. -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -21,55 +21,63 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -touch a b || framework_failure_ - -debug='---disable-inotify' -debug= -tail $debug -F -s.1 a b > out 2>&1 & pid=$! - check_tail_output() { local delay="$1" - grep "$tail_re" out > /dev/null || - { sleep $delay; return 1; } -} - -# Wait up to 6.3s for tail to start -echo x > a -tail_re='^x$' retry_delay_ check_tail_output .1 7 || fail=1 - -mv a b || fail=1 - -# Wait 6.3s for this diagnostic: -# tail: 'a' has become inaccessible: No such file or directory -tail_re='inaccessible' retry_delay_ check_tail_output .1 7 || fail=1 - -echo x > a -# Wait up to 6.3s for this to appear in the output: -# "tail: '...' has appeared; following end of new file" -tail_re='has appeared' retry_delay_ check_tail_output .1 7 || - { echo "$0: a: unexpected delay?"; cat out; fail=1; } - -echo y >> b -# Wait up to 6.3s for "y" to appear in the output: -tail_f_vs_rename_2() { - local delay="$1" - tr '\n' @ < out | grep '@@==> b <==@y@$' > /dev/null || - { sleep $delay; return 1; } -} -retry_delay_ tail_f_vs_rename_2 .1 7 || - { echo "$0: b: unexpected delay?"; cat out; fail=1; } - -echo z >> a -# Wait up to 6.3s for "z" to appear in the output: -tail_f_vs_rename_3() { - local delay="$1" - tr '\n' @ < out | grep '@@==> a <==@z@$' > /dev/null || + grep "$tail_re" out || { sleep $delay; return 1; } } -retry_delay_ tail_f_vs_rename_3 .1 7 || - { echo "$0: a: unexpected delay?"; cat out; fail=1; } -kill -HUP $pid +# 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 + touch a b || framework_failure_ + + tail $mode -F $fastpoll a b > out 2>&1 & pid=$! + + # Wait up to 12.7s for tail to start. + echo x > a + tail_re='^x$' retry_delay_ check_tail_output .1 7 || { cat out; fail=1; } + + mv a b || framework_failure_ + + # Wait 12.7s for this diagnostic: + # tail: 'a' has become inaccessible: No such file or directory + tail_re='inaccessible' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } + + echo x > a + # Wait up to 12.7s for this to appear in the output: + # "tail: '...' has appeared; following new file" + tail_re='has appeared' 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 "y" to appear in the output: + tail_f_vs_rename_2() { + local delay="$1" + tr '\n' @ < out | grep '@@==> b <==@y@$' > /dev/null || + { sleep $delay; return 1; } + } + retry_delay_ tail_f_vs_rename_2 .1 7 || + { echo "$0: b: unexpected delay?"; cat out; fail=1; } + + echo z >> a + # Wait up to 12.7s for "z" to appear in the output: + tail_f_vs_rename_3() { + local delay="$1" + tr '\n' @ < out | grep '@@==> a <==@z@$' > /dev/null || + { sleep $delay; return 1; } + } + retry_delay_ tail_f_vs_rename_3 .1 7 || + { echo "$0: a: unexpected delay?"; cat out; fail=1; } + + cleanup_ +done Exit $fail diff --git a/tests/tail-2/append-only.sh b/tests/tail-2/append-only.sh index 7414da3e..2ea5df1f 100755 --- a/tests/tail-2/append-only.sh +++ b/tests/tail-2/append-only.sh @@ -2,7 +2,7 @@ # Ensure that tail -f works on an append-only file # Requires root access to do chattr +a, as well as an ext[23] or xfs file system -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006-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 @@ -21,6 +21,9 @@ print_ver_ tail require_root_ +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + chattr_a_works=1 touch f chattr +a f 2>/dev/null || chattr_a_works=0 @@ -32,10 +35,10 @@ if test $chattr_a_works = 0; then fi -for inotify in ---disable-inotify ''; do - sleep 1 & - pid=$! - tail --pid=$pid -f $inotify f || fail=1 +for mode in '' '---disable-inotify'; do + sleep 1 & pid=$! + tail --pid=$pid -f $mode f || fail=1 + cleanup_ done chattr -a f 2>/dev/null diff --git a/tests/tail-2/assert-2.sh b/tests/tail-2/assert-2.sh index 930e422a..67804fea 100755 --- a/tests/tail-2/assert-2.sh +++ b/tests/tail-2/assert-2.sh @@ -1,9 +1,9 @@ #!/bin/sh -# This variant of 'assert' would get a UMR reliably in 2.0.9. +# This variant of 'assert' would get a Uninit Mem Read reliably in 2.0.9. # Due to a race condition in the test, the 'assert' script would get # the UMR on Solaris only some of the time, and not at all on Linux/GNU. -# Copyright (C) 2000-2014 Free Software Foundation, Inc. +# Copyright (C) 2000-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 @@ -21,29 +21,37 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -# Not "expensive" per se, but sleeping for so long is annoying. -very_expensive_ +check_tail_output() +{ + local delay="$1" + grep "$tail_re" out || + { sleep $delay; return 1; } +} -ok='ok ok ok' +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -touch a -tail --follow=name a foo > err 2>&1 & -tail_pid=$! -# Arrange for the tail process to die after 12 seconds. -(sleep 12; kill $tail_pid) & -echo $ok > f -echo sleeping for 7 seconds... -sleep 7 -mv f foo +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -# echo waiting.... -wait +for mode in '' '---disable-inotify'; do + rm -f a foo out + touch a || framework_failure_ -case "$(cat err)" in - *$ok) ;; - *) fail=1;; -esac + tail $mode --follow=name $fastpoll a foo > out 2>&1 & pid=$! -test $fail = 1 && cat err + # Wait up to 12.7s for tail to start. + echo x > a || framework_failure_ + tail_re='^x$' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; break; } + + # Wait up to 12.7s for tail to notice new foo file + ok='ok ok ok' + echo "$ok" > foo || framework_failure_ + tail_re="^$ok$" retry_delay_ check_tail_output .1 7 || + { echo "$0: foo: unexpected delay?"; cat out; fail=1; break; } + + cleanup_ +done Exit $fail diff --git a/tests/tail-2/assert.sh b/tests/tail-2/assert.sh index 35a10da2..43bd1e14 100755 --- a/tests/tail-2/assert.sh +++ b/tests/tail-2/assert.sh @@ -1,7 +1,7 @@ #!/bin/sh # Test for assertion failure in "test". -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-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 @@ -26,36 +26,43 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -# Not "expensive" per se, but sleeping for so long is annoying. -very_expensive_ +check_tail_output() +{ + local delay="$1" + grep "$tail_re" out || + { sleep $delay; return 1; } +} -ok='ok ok ok' +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -touch a foo -tail --follow=name a foo > err 2>&1 & -tail_pid=$! -# Arrange for the tail process to die after 12 seconds. -(sleep 12; kill $tail_pid) & +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -echo sleeping for 7 seconds... +for mode in '' '---disable-inotify'; do + rm -f a foo out + touch a foo || framework_failure_ -# Give the backgrounded 'tail' a chance to start before removing foo. -# Otherwise, without --retry, tail wouldn't try to open 'foo' again. -sleep 1 + tail $mode --follow=name $fastpoll a foo > out 2>&1 & pid=$! -rm -f foo -sleep 6 -echo $ok > f -mv f foo + # Wait up to 12.7s for tail to start. + echo x > a || framework_failure_ + tail_re='^x$' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; break; } -# echo waiting.... -wait + # Wait 12.7s for this diagnostic: + # tail: foo: No such file or directory + rm foo || framework_failure_ + tail_re='No such file' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; break; } -case "$(cat err)" in - *$ok) ;; - *) fail=1;; -esac + # Wait up to 12.7s for tail to notice new foo file + ok='ok ok ok' + echo "$ok" > foo || framework_failure_ + tail_re="^$ok$" retry_delay_ check_tail_output .1 7 || + { echo "$0: foo: unexpected delay?"; cat out; fail=1; break; } -test $fail = 1 && cat err + cleanup_ +done Exit $fail diff --git a/tests/tail-2/big-4gb.sh b/tests/tail-2/big-4gb.sh index cfd867b7..ac07a022 100755 --- a/tests/tail-2/big-4gb.sh +++ b/tests/tail-2/big-4gb.sh @@ -2,7 +2,7 @@ # Demonstrate a bug in 'tail -cN' when operating on files of size 4G and larger # Fixed in coreutils-4.5.2. -# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# Copyright (C) 2002-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 diff --git a/tests/tail-2/descriptor-vs-rename.sh b/tests/tail-2/descriptor-vs-rename.sh new file mode 100755 index 00000000..083d7144 --- /dev/null +++ b/tests/tail-2/descriptor-vs-rename.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Demonstrate that tail -f works when renaming the tailed files. +# Between coreutils 7.5 and 8.23 inclusive, 'tail -f a' would +# stop tracking additions to b after 'mv a b'. + +# 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 out + touch a || framework_failure_ + + tail $mode $fastpoll -f a > out 2>&1 & pid=$! + + # Wait up to 12.7s for tail to start. + echo x > a + tail_re='^x$' retry_delay_ check_tail_output .1 7 || { cat out; fail=1; } + + mv a b || framework_failure_ + + echo y >> b + # Wait up to 12.7s for "y" to appear in the output: + tail_re='^y$' retry_delay_ check_tail_output .1 7 || { cat out; fail=1; } + + cleanup_ +done + +Exit $fail diff --git a/tests/tail-2/flush-initial.sh b/tests/tail-2/flush-initial.sh index fd14eae9..a25bbeb1 100755 --- a/tests/tail-2/flush-initial.sh +++ b/tests/tail-2/flush-initial.sh @@ -1,7 +1,7 @@ #!/bin/sh # inotify-based tail -f didn't flush its initial output before blocking -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -19,13 +19,18 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -echo line > in || fail=1 +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' + +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +echo line > in || framework_failure_ # Output should be buffered since we're writing to file # so we're depending on the flush to write out -tail -f in > out & -tail_pid=$! +tail $fastpoll -f in > out & pid=$! -# Wait for 1.5s for the file to be flushed. +# Wait for 3.1s for the file to be flushed. tail_flush() { local delay="$1" @@ -35,6 +40,6 @@ tail_flush() } retry_delay_ tail_flush .1 5 || fail=1 -kill $tail_pid +cleanup_ Exit $fail diff --git a/tests/tail-2/follow-name.sh b/tests/tail-2/follow-name.sh index f70baec3..be68479e 100755 --- a/tests/tail-2/follow-name.sh +++ b/tests/tail-2/follow-name.sh @@ -1,7 +1,7 @@ #!/bin/sh # ensure that --follow=name does not imply --retry -# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# Copyright (C) 2011-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 diff --git a/tests/tail-2/follow-stdin.sh b/tests/tail-2/follow-stdin.sh index 1769ee2c..f50dd726 100755 --- a/tests/tail-2/follow-stdin.sh +++ b/tests/tail-2/follow-stdin.sh @@ -1,7 +1,7 @@ #!/bin/sh # tail -f - would fail with the initial inotify implementation -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 diff --git a/tests/tail-2/inotify-hash-abuse.sh b/tests/tail-2/inotify-hash-abuse.sh index 6492049d..6333699c 100755 --- a/tests/tail-2/inotify-hash-abuse.sh +++ b/tests/tail-2/inotify-hash-abuse.sh @@ -1,7 +1,7 @@ #!/bin/sh # Exercise an abort-inducing flaw in inotify-enabled tail -F. -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -23,10 +23,6 @@ print_ver_ tail n=9 seq $n | xargs touch || framework_failure_ -debug='---disable-inotify' -debug= -tail $debug -s.1 -qF $(seq $n) > out 2>&1 & pid=$! - check_tail_output() { local delay="$1" @@ -34,30 +30,42 @@ check_tail_output() { sleep $delay; return 1; } } -# Wait up to 6.3s for tail to start -echo x > $n -tail_re='^x$' retry_delay_ check_tail_output .1 7 || fail=1 +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -mv 1 f || fail=1 +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -# Wait 6.3s for this diagnostic: -# tail: '1' has become inaccessible: No such file or directory -tail_re='inaccessible' retry_delay_ check_tail_output .1 7 || fail=1 +for mode in '' '---disable-inotify'; do + rm -f out -# Trigger the bug. Before the fix, this would provoke the abort. -echo a > 1 || fail=1 + tail $mode $fastpoll -qF $(seq $n) > out 2>&1 & pid=$! -# Wait up to 2s for the buggy tail to die, -# or for the "tail: '1' has appeared; following end of new file" output -for i in $(seq 10); do - kill -0 $pid || break - grep 'has appeared;' out > /dev/null && break - sleep .2 -done + # Wait up to 12.7s for tail to start + echo x > $n + tail_re='^x$' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } + + mv 1 f || framework_failure_ + + # Wait 12.7s for this diagnostic: + # tail: '1' has become inaccessible: No such file or directory + tail_re='inaccessible' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } -# Kill the working tail, or fail if it has already aborted -kill $pid || fail=1 + # Trigger the bug. Before the fix, this would provoke the abort. + echo a > 1 || framework_failure_ + + # Wait up to 6.3s for the "tail: '1' has appeared; ..." message + # (or for the buggy tail to die) + tail_re='has appeared' retry_delay_ check_tail_output .1 6 || + { cat out; fail=1; } + + # Double check that tail hasn't aborted + kill -0 $pid || fail=1 + + cleanup_ +done -cat out Exit $fail diff --git a/tests/tail-2/inotify-hash-abuse2.sh b/tests/tail-2/inotify-hash-abuse2.sh index 6018448f..d641c241 100755 --- a/tests/tail-2/inotify-hash-abuse2.sh +++ b/tests/tail-2/inotify-hash-abuse2.sh @@ -2,7 +2,7 @@ # Exercise an abort-inducing flaw in inotify-enabled tail -F. # Like inotify-hash-abuse, but without a hard-coded "9". -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -20,20 +20,27 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -touch f || framework_failure_ +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -debug='---disable-inotify -s .001' -debug= -tail $debug -F f & pid=$! -cleanup_() { kill $pid; } +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -for i in $(seq 200); do - kill -0 $pid || break; - mv f g - touch f -done +for mode in '' '---disable-inotify'; do + touch f || framework_failure_ + + tail $mode $fastpoll -F f & pid=$! + + for i in $(seq 200); do + kill -0 $pid || break; + mv f g + touch f + done -# Kill the working tail, or fail if it has already aborted -kill $pid || fail=1 + # Ensure tail hasn't aborted + kill -0 $pid || fail=1 + + cleanup_ +done Exit $fail diff --git a/tests/tail-2/inotify-race.sh b/tests/tail-2/inotify-race.sh index c25f354a..4ce05083 100755 --- a/tests/tail-2/inotify-race.sh +++ b/tests/tail-2/inotify-race.sh @@ -5,7 +5,7 @@ # indefinitely if no *other* data is appended, but it would be printed as # soon as any additional appended data is detected. -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -23,10 +23,11 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -# Don't run this test by default because sometimes it's skipped as noted below. -# Also gdb has a bug in Debian's gdb-6.8-3 at least that causes it to not -# cleanup and exit correctly when it receives a SIGTERM, thus hanging the test. -very_expensive_ +# Terminate any background gdb/tail process +cleanup_() { + kill $pid 2>/dev/null && wait $pid + kill $sleep 2>/dev/null && wait $sleep +} touch file || framework_failure_ touch tail.out || framework_failure_ @@ -37,37 +38,61 @@ case $(cat gdb.out) in *) skip_ "can't run gdb";; esac -break_src="$abs_top_builddir/src/tail.c" +# Break on a line rather than a symbol, to cater for inline functions +break_src="$abs_top_srcdir/src/tail.c" break_line=$(grep -n ^tail_forever_inotify "$break_src") || framework_failure_ break_line=$(echo "$break_line" | cut -d: -f1) || framework_failure_ + +# Note we get tail to monitor a background sleep process +# rather than using timeout(1), as timeout sends SIGCONT +# signals to its monitored process, and gdb (7.9 at least) +# has _intermittent_ issues with this. +# Sending SIGCONT resulted in either delayed child termination, +# or no child termination resulting in a hung test. +# See https://sourceware.org/bugzilla/show_bug.cgi?id=18364 + +env sleep 10 & sleep=$! + # See if gdb works and # tail_forever_inotify is compiled and run -timeout 10s gdb -nx --batch-silent \ +gdb -nx --batch-silent \ --eval-command="break $break_line" \ - --eval-command='run -f file' \ + --eval-command="run --pid=$sleep -f file" \ --eval-command='quit' \ - tail < /dev/null > gdb.out 2>&1 || skip_ 'breakpoint not hit' + tail < /dev/null > gdb.out 2>&1 + +kill $sleep || skip_ 'breakpoint not hit' +wait $sleep # FIXME: The above is seen to _intermittently_ fail with: # warning: .dynamic section for "/lib/libc.so.6" is not at the expected address # warning: difference appears to be caused by prelink, adjusting expectations compare /dev/null gdb.out || skip_ "can't set breakpoints in tail" +env sleep 10 & sleep=$! + # Run "tail -f file", stopping to append a line just before # inotify initialization, and then continue. Before the fix, # that just-appended line would never be output. -timeout 10s gdb -nx --batch-silent \ +gdb -nx --batch-silent \ --eval-command="break $break_line" \ - --eval-command='run -f file >> tail.out' \ + --eval-command="run --pid=$sleep -f file >> tail.out" \ --eval-command='shell echo never-seen-with-tail-7.5 >> file' \ --eval-command='continue' \ --eval-command='quit' \ - tail < /dev/null > /dev/null 2>&1 & -pid=$! + tail < /dev/null > /dev/null 2>&1 & pid=$! + +tail --pid=$pid -f tail.out | (read REPLY; kill $pid) + +# gdb has a bug in Debian's gdb-6.8-3 at least that causes it to not +# cleanup and exit correctly when it receives a SIGTERM, but +# killing sleep, should cause the tail process and thus gdb to exit. +kill $sleep +wait $sleep -tail --pid=$pid -f tail.out | (read; kill $pid) +wait $pid -test -s tail.out || fail=1 +compare /dev/null tail.out && fail=1 Exit $fail diff --git a/tests/tail-2/inotify-race2.sh b/tests/tail-2/inotify-race2.sh new file mode 100755 index 00000000..1e01d505 --- /dev/null +++ b/tests/tail-2/inotify-race2.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# Ensure that tail does not ignore a tailed-forever file that has been +# replaced between tail's initial read-to-EOF, and when the inotify watches +# are established in tail_forever_inotify. That new file would be ignored +# indefinitely. + +# 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 + +# Terminate any background gdb/tail process +cleanup_() { + kill $pid 2>/dev/null && wait $pid + kill $sleep 2>/dev/null && wait $sleep +} + +touch file || framework_failure_ +touch tail.out || framework_failure_ + +( timeout 10s gdb --version ) > gdb.out 2>&1 +case $(cat gdb.out) in + *'GNU gdb'*) ;; + *) skip_ "can't run gdb";; +esac + +# Break on a line rather than a symbol, to cater for inline functions +break_src="$abs_top_srcdir/src/tail.c" +break_line=$(grep -n ^tail_forever_inotify "$break_src") || framework_failure_ +break_line=$(echo "$break_line" | cut -d: -f1) || framework_failure_ + + +# Note we get tail to monitor a background sleep process +# rather than using timeout(1), as timeout sends SIGCONT +# signals to its monitored process, and gdb (7.9 at least) +# has _intermittent_ issues with this. +# Sending SIGCONT resulted in either delayed child termination, +# or no child termination resulting in a hung test. +# See https://sourceware.org/bugzilla/show_bug.cgi?id=18364 + +env sleep 10 & sleep=$! + +# See if gdb works and +# tail_forever_inotify is compiled and run +gdb -nx --batch-silent \ + --eval-command="break $break_line" \ + --eval-command="run --pid=$sleep -f file" \ + --eval-command='quit' \ + tail < /dev/null > gdb.out 2>&1 + +kill $sleep || skip_ 'breakpoint not hit' +wait $sleep + +# FIXME: The above is seen to _intermittently_ fail with: +# warning: .dynamic section for "/lib/libc.so.6" is not at the expected address +# warning: difference appears to be caused by prelink, adjusting expectations +compare /dev/null gdb.out || skip_ "can't set breakpoints in tail" + +env sleep 10 & sleep=$! + +echo never-seen-with-tail-8.23 > file.new || framework_failure_ + +# Run "tail -F file", stopping to replace with a new file before +# inotify initialization, and then continue. Before the fix, +# changes to the new file would effectively be ignored. +gdb -nx --batch-silent \ + --eval-command="break $break_line" \ + --eval-command="run --pid=$sleep -F file 2>tail.err >>tail.out" \ + --eval-command='shell mv file.new file' \ + --eval-command='continue' \ + --eval-command='quit' \ + tail < /dev/null > /dev/null 2>&1 & pid=$! + +# Note even updating the watched 'file' wouldn't have output +# anything between coreutils 7.5 and 8.23 inclusive as +# The old file descriptor (still held open by tail) was being fstat(). + +tail --pid=$pid -f tail.out | (read REPLY; kill $pid) + +# gdb has a bug in Debian's gdb-6.8-3 at least that causes it to not +# cleanup and exit correctly when it receives a SIGTERM, but +# killing sleep, should cause the tail process and thus gdb to exit. +kill $sleep +wait $sleep + +wait $pid + +compare /dev/null tail.out && { cat tail.err; fail=1; } + +Exit $fail diff --git a/tests/tail-2/inotify-rotate-resources.sh b/tests/tail-2/inotify-rotate-resources.sh new file mode 100755 index 00000000..fe5d9127 --- /dev/null +++ b/tests/tail-2/inotify-rotate-resources.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# ensure that tail -F doesn't leak inotify resources + +# 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 + +grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null \ + || skip_ 'inotify required' + +require_strace_ 'inotify_add_watch,inotify_rm_watch' + +# Quickly skip on remote file systems +is_local_dir_ . || skip_ 'inotify not used on remote file system' + +check_tail_output() +{ + local delay="$1" + grep "$tail_re" out > /dev/null || + { sleep $delay; return 1; } +} + +# Wait up to 25.5 seconds for grep REGEXP 'out' to succeed. +grep_timeout() { tail_re="$1" retry_delay_ check_tail_output .1 8; } + +check_strace() +{ + local delay="$1" + grep "$strace_re" strace.out > /dev/null || + { sleep $delay; return 1; } +} + +cleanup_fail() +{ + cat out + warn_ $1 + fail=1 +} + +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +fastpoll='-s.1 --max-unchanged-stats=1' + +touch k || framework_failure_ + +# Note the timeout guard isn't strictly necessary here, +# however without it strace will ignore SIGTERM. +# strace does always honor SIGTERM with the -I2 option, +# though that's not available on RHEL6 for example. +timeout 180 strace -e inotify_add_watch,inotify_rm_watch -o strace.out \ + tail -F $fastpoll k >> out 2>&1 & pid=$! + +reverted_to_polling_=0 +for i in $(seq 2); do + echo $i + + echo 'tailed' > k; + + # Wait for watch on (new) file + strace_re='inotify_add_watch.*MODIFY' retry_delay_ check_strace .1 8 || + no_watch_=1 + + # Assume this is not because we're leaking + # (resources may already be depleted) + # The explicit check for inotify_rm_watch should confirm that. + grep -F 'reverting to polling' out >/dev/null && skip_ 'inotify unused' + + # Otherwise failure is unknown + test "$no_watch_" && { cat out; framework_failure_ 'no inotify_add_watch'; } + + mv k k.tmp + # wait for tail to detect the rename + grep_timeout 'inaccessible' || + { cleanup_fail 'failed to detect rename'; break; } + + # Note we strace here rather than consuming all available watches + # to be more efficient, but more importantly avoid depleting resources. + # Note also available resources can currently be tuned with: + # sudo sysctl -w fs.inotify.max_user_watches=$smallish_number + # However that impacts all processes for the current user, and also + # may not be supported in future, instead being auto scaled to RAM + # like the Linux epoll resources were. + if test "$i" -gt 1; then + strace_re='inotify_rm_watch' retry_delay_ check_strace .1 8 || + { cleanup_fail 'failed to find inotify_rm_watch syscall'; break; } + fi + + >out && >strace.out || framework_failure_ 'failed to reset output files' +done + +cleanup_ + +Exit $fail diff --git a/tests/tail-2/inotify-rotate.sh b/tests/tail-2/inotify-rotate.sh index 1c942cc3..638a3bbb 100755 --- a/tests/tail-2/inotify-rotate.sh +++ b/tests/tail-2/inotify-rotate.sh @@ -1,7 +1,7 @@ #!/bin/sh # ensure that tail -F handles rotation -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -16,55 +16,62 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -if test "$VERBOSE" = yes; then - set -x - tail --version -fi - . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src -expensive_ +print_ver_ tail + +grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null \ + || expensive_ + +check_tail_output() +{ + local delay="$1" + grep "$tail_re" out > /dev/null || + { sleep $delay; return 1; } +} + +# Wait up to 25.5 seconds for grep REGEXP 'out' to succeed. +grep_timeout() { tail_re="$1" retry_delay_ check_tail_output .1 8; } -# Wait several seconds for grep REGEXP FILE to succeed. -# Usage: grep_timeout REGEXP FILE -grep_timeout() +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +cleanup_fail() { - local j - for j in $(seq 150); do - grep $1 $2 > /dev/null && return 0 - sleep 0.1 - done - return 1 + cat out + warn_ $1 + cleanup_ + fail=1 } -# For details, see -# http://lists.gnu.org/archive/html/bug-coreutils/2009-11/msg00213.html +# Speedup the non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' # Perform at least this many iterations, because on multi-core systems # the offending sequence of events can be surprisingly uncommon. +# See: http://lists.gnu.org/archive/html/bug-coreutils/2009-11/msg00213.html for i in $(seq 50); do echo $i - rm -rf k x out + rm -f k x out + # Normally less than a second is required here, but with heavy load # and a lot of disk activity, even 20 seconds is insufficient, which # leads to this timeout killing tail before the "ok" is written below. >k && >x || framework_failure_ failed to initialize files - timeout 40 tail -F k > out 2>&1 & - pid=$! - sleep .1 - echo b > k; - # wait for b to appear in out - grep_timeout b out || fail_ failed to find b in out - while :; do grep b out > /dev/null && break; done + timeout 60 tail $fastpoll -F k > out 2>&1 & pid=$! + + echo 'tailed' > k; + # wait for 'tailed' to appear in out + grep_timeout 'tailed' || { cleanup_fail 'failed to find "tailed"'; break; } + mv x k # wait for tail to detect the rename - grep_timeout tail: out || { cat out; fail_ failed to detect rename; } + grep_timeout 'tail:' || { cleanup_fail 'failed to detect rename'; break; } + echo ok >> k - found=0 - # wait up to 10 seconds for "ok" to appear in out - grep_timeout ok out && found=1 - kill $pid - test $found = 0 && { cat out; fail_ failed to detect echoed '"ok"'; } + # wait for "ok" to appear in 'out' + grep_timeout 'ok' || { cleanup_fail 'failed to detect echoed ok'; break; } + + cleanup_ done -wait Exit $fail diff --git a/tests/tail-2/pid.sh b/tests/tail-2/pid.sh index ef440818..b410478e 100755 --- a/tests/tail-2/pid.sh +++ b/tests/tail-2/pid.sh @@ -1,7 +1,7 @@ #!/bin/sh # Test the --pid option of tail. -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-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 @@ -22,29 +22,29 @@ getlimits_ touch empty here || framework_failure_ +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } -for inotify in ---disable-inotify ''; do +for mode in '' '---disable-inotify'; do # Use tail itself to create a background process to monitor, # which will auto exit when "here" is removed. - tail -f $inotify here & - bg_pid=$! + tail -f $mode here & pid=$! # Ensure that tail --pid=PID does not exit when PID is alive. - timeout 1 tail -f -s.1 --pid=$bg_pid $inotify here + timeout 1 tail -f -s.1 --pid=$pid $mode here test $? = 124 || fail=1 - # Cleanup background process - kill $bg_pid + cleanup_ # Ensure that tail --pid=PID exits with success status when PID is dead. # Use an unlikely-to-be-live PID - timeout 10 tail -f -s.1 --pid=$PID_T_MAX $inotify empty + timeout 10 tail -f -s.1 --pid=$PID_T_MAX $mode empty ret=$? test $ret = 124 && skip_ "pid $PID_T_MAX present or tail too slow" test $ret = 0 || fail=1 # Ensure tail doesn't wait for data when PID is dead - timeout 10 tail -f -s10 --pid=$PID_T_MAX $inotify empty + timeout 10 tail -f -s10 --pid=$PID_T_MAX $mode empty test $? = 124 && fail=1 done diff --git a/tests/tail-2/pipe-f.sh b/tests/tail-2/pipe-f.sh index eaa928fb..22850c49 100755 --- a/tests/tail-2/pipe-f.sh +++ b/tests/tail-2/pipe-f.sh @@ -1,7 +1,7 @@ #!/bin/sh # ensure that :|tail -f doesn't hang, per POSIX -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 diff --git a/tests/tail-2/pipe-f2.sh b/tests/tail-2/pipe-f2.sh index 13ccd0b3..71a081c3 100755 --- a/tests/tail-2/pipe-f2.sh +++ b/tests/tail-2/pipe-f2.sh @@ -1,7 +1,7 @@ #!/bin/sh # Ensure that "tail -f fifo" tails indefinitely. -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-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 @@ -24,7 +24,13 @@ mkfifo_or_skip_ fifo echo 1 > fifo & echo 1 > exp || framework_failure_ -timeout 10 tail -f fifo > out & pid=$! +# 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' + +timeout 10 tail $fastpoll -f fifo > out & pid=$! check_tail_output() { @@ -32,12 +38,14 @@ check_tail_output() test -s out || { sleep $n_sec; return 1; } } -# Wait 6.3s for tail to write something. +# Wait 12.7s for tail to write something. retry_delay_ check_tail_output .1 7 || fail=1 compare exp out || fail=1 -# Kill the still-running tail, or fail if it's gone. -kill $pid || fail=1 +# Ensure tail is still running +kill -0 $pid || fail=1 + +cleanup_ Exit $fail diff --git a/tests/tail-2/proc-ksyms.sh b/tests/tail-2/proc-ksyms.sh index 3f799649..cd007744 100755 --- a/tests/tail-2/proc-ksyms.sh +++ b/tests/tail-2/proc-ksyms.sh @@ -1,7 +1,7 @@ #!/bin/sh # Prior to textutils-2.0.17, 'tail /proc/ksyms' would segfault on Linux. -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-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 diff --git a/tests/tail-2/retry.sh b/tests/tail-2/retry.sh index dbe66a4e..a1497d90 100755 --- a/tests/tail-2/retry.sh +++ b/tests/tail-2/retry.sh @@ -1,7 +1,7 @@ #!/bin/sh # Exercise tail's behavior regarding missing files with/without --retry. -# Copyright (C) 2013-2014 Free Software Foundation, Inc. +# Copyright (C) 2013-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 @@ -19,6 +19,13 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail +# Function to count number of lines from tail +# while ignoring transient errors due to resource limits +countlines_ () +{ + grep -Ev 'inotify (resources exhausted|cannot be used)' out | wc -l +} + # Function to check the expected line count in 'out'. # Called via retry_delay_(). Sleep some time - see retry_delay_() - if the # line count is still smaller than expected. @@ -26,34 +33,41 @@ wait4lines_ () { local delay=$1 local elc=$2 # Expected line count. - [ "$( wc -l < out )" -ge "$elc" ] || { sleep $delay; return 1; } + [ "$(countlines_)" -ge "$elc" ] || { 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' + # === Test: # Retry without --follow results in a warning. touch file tail --retry file > out 2>&1 || fail=1 -[ $( wc -l < out ) = 1 ] || fail=1 -grep -F 'tail: warning: --retry ignored' out || fail=1 +[ "$(countlines_)" = 1 ] || { cat out; fail=1; } +grep -F 'tail: warning: --retry ignored' out || { cat out; fail=1; } # === Test: # The same with a missing file: expect error message and exit 1. tail --retry missing > out 2>&1 && fail=1 -[ $( wc -l < out ) = 2 ] || fail=1 -grep -F 'tail: warning: --retry ignored' out || fail=1 +[ "$(countlines_)" = 2 ] || { cat out; fail=1; } +grep -F 'tail: warning: --retry ignored' out || { cat out; fail=1; } # === Test: # Ensure that "tail --retry --follow=name" waits for the file to appear. # Clear 'out' so that we can check its contents without races >out || framework_failure_ -timeout 10 tail -s.1 --follow=name --retry missing >out 2>&1 & pid=$! -retry_delay_ wait4lines_ .1 6 1 || fail=1 # Wait for "cannot open" error. -echo "X" > missing || fail=1 -retry_delay_ wait4lines_ .1 6 3 || fail=1 # Wait for the expected output. -kill $pid -wait $pid +timeout 10 tail $fastpoll --follow=name --retry missing >out 2>&1 & pid=$! +# Wait for "cannot open" error. +retry_delay_ wait4lines_ .1 6 1 || { cat out; fail=1; } +echo "X" > missing || framework_failure_ +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 3 || { cat out; fail=1; } +cleanup_ # Expect 3 lines in the output file. -[ $( wc -l < out ) = 3 ] || { fail=1; cat out; } +[ "$(countlines_)" = 3 ] || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } grep -F 'has appeared' out || { fail=1; cat out; } grep '^X$' out || { fail=1; cat out; } @@ -62,14 +76,15 @@ rm -f missing out || framework_failure_ # === Test: # Ensure that "tail --retry --follow=descriptor" waits for the file to appear. # tail-8.21 failed at this (since the implementation of the inotify support). -timeout 10 tail -s.1 --follow=descriptor --retry missing >out 2>&1 & pid=$! -retry_delay_ wait4lines_ .1 6 2 || fail=1 # Wait for "cannot open" error. -echo "X" > missing || fail=1 -retry_delay_ wait4lines_ .1 6 4 || fail=1 # Wait for the expected output. -kill $pid -wait $pid +timeout 10 tail $fastpoll --follow=descriptor --retry missing >out 2>&1 & pid=$! +# Wait for "cannot open" error. +retry_delay_ wait4lines_ .1 6 2 || { cat out; fail=1; } +echo "X" > missing || framework_failure_ +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 4 || { cat out; fail=1; } +cleanup_ # Expect 4 lines in the output file. -[ $( wc -l < out ) = 4 ] || { fail=1; cat out; } +[ "$(countlines_)" = 4 ] || { fail=1; cat out; } grep -F 'retry only effective for the initial open' out \ || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } @@ -80,13 +95,15 @@ rm -f missing out || framework_failure_ # === Test: # Ensure that tail --follow=descriptor --retry exits when the file appears # untailable. Expect exit status 1. -timeout 10 tail -s.1 --follow=descriptor --retry missing >out 2>&1 & pid=$! -retry_delay_ wait4lines_ .1 6 2 || fail=1 # Wait for "cannot open" error. -mkdir missing || fail=1 # Create untailable 'missing'. -retry_delay_ wait4lines_ .1 6 4 || fail=1 # Wait for the expected output. +timeout 10 tail $fastpoll --follow=descriptor --retry missing >out 2>&1 & pid=$! +# Wait for "cannot open" error. +retry_delay_ wait4lines_ .1 6 2 || { cat out; fail=1; } +mkdir missing || framework_failure_ # Create untailable +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 4 || { cat out; fail=1; } wait $pid rc=$? -[ $( wc -l < out ) = 4 ] || { fail=1; cat out; } +[ "$(countlines_)" = 4 ] || { fail=1; cat out; } grep -F 'retry only effective for the initial open' out \ || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } @@ -100,14 +117,14 @@ rm -fd missing out || framework_failure_ # file to appear. Expect 2 lines in the output file ("cannot open" + # "no files remaining") and exit status 1. tail --follow=descriptor missing >out 2>&1 && fail=1 -[ $( wc -l < out ) = 2 ] || { fail=1; cat out; } +[ "$(countlines_)" = 2 ] || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } grep -F 'no files remaining' out || { fail=1; cat out; } # === Test: # Likewise for --follow=name (without --retry). tail --follow=name missing >out 2>&1 && fail=1 -[ $( wc -l < out ) = 2 ] || { fail=1; cat out; } +[ "$(countlines_)" = 2 ] || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } grep -F 'no files remaining' out || { fail=1; cat out; } diff --git a/tests/tail-2/start-middle.sh b/tests/tail-2/start-middle.sh index dbf9bae8..9c709aee 100755 --- a/tests/tail-2/start-middle.sh +++ b/tests/tail-2/start-middle.sh @@ -2,7 +2,7 @@ # Verify that tail works even when it's reading from a file # that is not at its beginning. Based on a report from John Roll. -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-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 diff --git a/tests/tail-2/symlink.sh b/tests/tail-2/symlink.sh index b21f9e15..b5dcbbd6 100755 --- a/tests/tail-2/symlink.sh +++ b/tests/tail-2/symlink.sh @@ -1,7 +1,7 @@ #!/bin/sh # Ensure tail tracks symlinks properly. -# Copyright (C) 2013-2014 Free Software Foundation, Inc. +# Copyright (C) 2013-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 @@ -19,6 +19,13 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail +# Function to count number of lines from tail +# while ignoring transient errors due to resource limits +countlines_ () +{ + grep -Ev 'inotify (resources exhausted|cannot be used)' out | wc -l +} + # Function to check the expected line count in 'out'. # Called via retry_delay_(). Sleep some time - see retry_delay_() - if the # line count is still smaller than expected. @@ -26,43 +33,52 @@ wait4lines_ () { local delay=$1 local elc=$2 # Expected line count. - [ "$( wc -l < out )" -ge "$elc" ] || { sleep $delay; return 1; } + [ "$(countlines_)" -ge "$elc" ] || { sleep $delay; return 1; } } +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +# speedup non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' + # Ensure changing targets of cli specified symlinks are handled. # Prior to v8.22, inotify would fail to recognize changes in the targets. # Clear 'out' so that we can check its contents without races. >out || framework_failure_ ln -nsf target symlink || framework_failure_ -timeout 10 tail -s.1 -F symlink >out 2>&1 & pid=$! -retry_delay_ wait4lines_ .1 6 1 || fail=1 # Wait for "cannot open..." -echo "X" > target || fail=1 -retry_delay_ wait4lines_ .1 6 3 || fail=1 # Wait for the expected output. -kill $pid -wait $pid +timeout 10 tail $fastpoll -F symlink >out 2>&1 & pid=$! +# Wait for "cannot open..." +retry_delay_ wait4lines_ .1 6 1 || { cat out; fail=1; } +echo "X" > target || framework_failure_ +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 3 || { cat out; fail=1; } +cleanup_ # Expect 3 lines in the output file. -[ $( wc -l < out ) = 3 ] || { fail=1; cat out; } +[ "$(countlines_)" = 3 ] || { fail=1; cat out; } grep -F 'cannot open' out || { fail=1; cat out; } grep -F 'has appeared' out || { fail=1; cat out; } grep '^X$' out || { fail=1; cat out; } rm -f target out || framework_failure_ # Ensure we correctly handle the source symlink itself changing. -# I.E. that we don't operate solely on the targets. +# I.e., that we don't operate solely on the targets. # Clear 'out' so that we can check its contents without races. >out || framework_failure_ echo "X1" > target1 || framework_failure_ ln -nsf target1 symlink || framework_failure_ -timeout 10 tail -s.1 -F symlink >out 2>&1 & pid=$! -retry_delay_ wait4lines_ .1 6 1 || fail=1 # Wait for the expected output. +timeout 10 tail $fastpoll -F symlink >out 2>&1 & pid=$! +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 1 || { cat out; fail=1; } ln -nsf target2 symlink || framework_failure_ -retry_delay_ wait4lines_ .1 6 2 || fail=1 # Wait for "become inaccess..." -echo "X2" > target2 || fail=1 -retry_delay_ wait4lines_ .1 6 4 || fail=1 # Wait for the expected output. -kill $pid -wait $pid +# Wait for "become inaccess..." +retry_delay_ wait4lines_ .1 6 2 || { cat out; fail=1; } +echo "X2" > target2 || framework_failure_ +# Wait for the expected output. +retry_delay_ wait4lines_ .1 6 4 || { cat out; fail=1; } +cleanup_ # Expect 4 lines in the output file. -[ $( wc -l < out ) = 4 ] || { fail=1; cat out; } +[ "$(countlines_)" = 4 ] || { fail=1; cat out; } grep -F 'become inacce' out || { fail=1; cat out; } grep -F 'has appeared' out || { fail=1; cat out; } grep '^X1$' out || { fail=1; cat out; } @@ -70,7 +86,7 @@ grep '^X2$' out || { fail=1; cat out; } rm -f target1 target2 out || framework_failure_ # Note other symlink edge cases are currently just diagnosed -# rather than being handled. I.E. if you specify a missing item, +# rather than being handled. I.e., if you specify a missing item, # or existing file that later change to a symlink, if inotify # is in use, you'll get a diagnostic saying that link will # no longer be tailed. diff --git a/tests/tail-2/infloop-1.sh b/tests/tail-2/tail-c.sh index 37d081f3..04ff782b 100755 --- a/tests/tail-2/infloop-1.sh +++ b/tests/tail-2/tail-c.sh @@ -1,7 +1,7 @@ #!/bin/sh -# This test would fail with tail from pre-1.22i textutils. +# exercise tail -c -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright 2014-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 @@ -19,28 +19,16 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail -yes > t & -yes_pid=$! -while :; do - test -s t \ - && break - sleep .1 -done -tail -n 1 t & -tail_pid=$! -kill $yes_pid - -# This test is racy, and can fail under unusual circumstances. -# On a very busy system, tail will fail to notice that $yes_pid is gone. -# Then the following kill will succeed and cause this test to fail. - -# Wait for up to 3 seconds for tail to detect the death of $yes_pid. -for i in $(seq 30); do - kill -0 $tail_pid || break - echo sleep 0.1s - sleep .1 -done +# Make sure it works on funny files in /proc and /sys. -kill $tail_pid && fail=1 || : +for file in /proc/version /sys/kernel/profiling; do + if test -r $file; then + cp -f $file copy && + tail -c -1 copy > exp1 || framework_failure_ + + tail -c -1 $file > out1 || fail=1 + compare exp1 out1 || fail=1 + fi +done Exit $fail diff --git a/tests/tail-2/tail-n0f.sh b/tests/tail-2/tail-n0f.sh index 1d020ac9..ba9f4071 100755 --- a/tests/tail-2/tail-n0f.sh +++ b/tests/tail-2/tail-n0f.sh @@ -2,7 +2,7 @@ # Make sure that 'tail -n0 -f' and 'tail -c0 -f' sleep # rather than doing what amounted to a busy-wait. -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-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 @@ -35,11 +35,13 @@ chmod 0 unreadable || framework_failure_ tail -c0 unreadable || fail=1 tail -n0 unreadable || fail=1 -for inotify in ---disable-inotify ''; do +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +for mode in '' '---disable-inotify'; do for file in empty nonempty; do for c_or_n in c n; do - tail --sleep=4 -${c_or_n} 0 -f $inotify $file & - pid=$! + tail --sleep=4 -${c_or_n} 0 -f $mode $file & pid=$! tail_sleeping() { local delay="$1"; sleep $delay @@ -52,7 +54,7 @@ for inotify in ---disable-inotify ''; do # Wait up to 1.5s for tail to sleep retry_delay_ tail_sleeping .1 4 || { echo $0: process in unexpected state: $state >&2; fail=1; } - kill $pid + cleanup_ done done done diff --git a/tests/tail-2/truncate.sh b/tests/tail-2/truncate.sh new file mode 100755 index 00000000..82720fda --- /dev/null +++ b/tests/tail-2/truncate.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Ensure all logs are output upon file truncation + +# 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 > /dev/null || + { 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 follow in '-f' '-F'; do + for mode in '' '---disable-inotify'; do + rm -f out + seq 10 > f || framework_failure_ + + tail $follow $mode $fastpoll f > out 2>&1 & pid=$! + + # Wait up to 12.7s for tail to start + tail_re='^10$' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } + + seq 11 15 > f || framework_failure_ + + # Wait up to 12.7s for new data + tail_re='^15$' retry_delay_ check_tail_output .1 7 || + { cat out; fail=1; } + + cleanup_ + done +done + +Exit $fail diff --git a/tests/tail-2/wait.sh b/tests/tail-2/wait.sh index 3dec55c0..2e58e821 100755 --- a/tests/tail-2/wait.sh +++ b/tests/tail-2/wait.sh @@ -2,7 +2,7 @@ # Make sure that 'tail -f' returns immediately if a file doesn't exist # while 'tail -F' waits for it to appear. -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-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 @@ -20,47 +20,60 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ tail +grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null \ + && HAVE_INOTIFY=1 + +inotify_failed_re='inotify (resources exhausted|cannot be used)' + touch here || framework_failure_ { touch unreadable && chmod a-r unreadable; } || framework_failure_ +# Terminate any background tail process +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +# speedup non inotify case +fastpoll='-s.1 --max-unchanged-stats=1' -for inotify in ---disable-inotify ''; do - timeout 10 tail -s0.1 -f $inotify not_here +for mode in '' '---disable-inotify'; do + timeout 10 tail $fastpoll -f $mode not_here test $? = 124 && fail=1 if test ! -r unreadable; then # can't test this when root - timeout 10 tail -s0.1 -f $inotify unreadable + timeout 10 tail $fastpoll -f $mode unreadable test $? = 124 && fail=1 fi - timeout 1 tail -s0.1 -f $inotify here 2>tail.err + timeout .1 tail $fastpoll -f $mode here 2>tail.err test $? = 124 || fail=1 # 'tail -F' must wait in any case. - timeout 1 tail -s0.1 -F $inotify here 2>>tail.err + timeout .1 tail $fastpoll -F $mode here 2>>tail.err test $? = 124 || fail=1 if test ! -r unreadable; then # can't test this when root - timeout 1 tail -s0.1 -F $inotify unreadable + timeout .1 tail $fastpoll -F $mode unreadable test $? = 124 || fail=1 fi - timeout 1 tail -s0.1 -F $inotify not_here + timeout .1 tail $fastpoll -F $mode not_here test $? = 124 || fail=1 - grep -Ev 'inotify (resources exhausted|cannot be used)' tail.err > x + grep -Ev "$inotify_failed_re" tail.err > x mv x tail.err compare /dev/null tail.err || fail=1 >tail.err +done +if test "$HAVE_INOTIFY"; then + # Ensure -F never follows a descriptor after rename + # either with tiny or significant delays between operations tail_F() { local delay="$1" touch k || framework_failure_ - tail -s.1 --max-unchanged-stats=2 -F $inotify k > tail.out & - pid=$! + tail $fastpoll -F $mode k >tail.out 2>tail.err & pid=$! sleep $delay mv k l sleep $delay @@ -69,12 +82,15 @@ for inotify in ---disable-inotify ''; do sleep $delay echo NO >> l sleep $delay - kill $pid + cleanup_ rm -f k l - test ! -s tail.out + test -s tail.out \ + && ! grep -E "$inotify_failed_re" tail.err >/dev/null } - retry_delay_ tail_F .1 4 || fail=1 -done + + retry_delay_ tail_F 0 1 && { cat tail.out; fail=1; } + retry_delay_ tail_F .2 1 && { cat tail.out; fail=1; } +fi Exit $fail |