From 49efaeaaaba9f055da9fadbfe111c1d51efae3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?=
Date: Thu, 24 Sep 2026 19:33:23 +0100 Subject: [PATCH] tail: without inotify, ensure prompt output This was seen with a NetBSD failure in the pipe-f2.sh test due to it clearing the EOF indication on a fifo, and thus blocking in the subsequent read. That was discussed at: https://lists.gnu.org/r/coreutils/2026-04/msg00129.html https://bugs.gnu.org/81923 Note the NetBSD issue may be addressed in NetBSD 12 as discussed in the bug above. This is more defensive/consistent in the case where a subsequent read on a file blocks. We might be operating on non fifos after all. * src/tail.c (tail_forever): Flush output before we might block. * tests/tail/pipe-f2.sh: Test non inotify path on all platforms. * NEWS: Mention the improvement. --- NEWS | 4 ++++ src/tail.c | 4 ++++ tests/tail/pipe-f2.sh | 21 ++++++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index ed4bbffdd..d2fb7e7b3 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ GNU coreutils NEWS -*- outline -*- 'od' allocates half the memory when -v,--output-duplicates is specified. + 'tail' ensures prompt output when inotify is not being used, and when + initial reads finish, but subsequent reads might block, which was seen + e.g. with fifos on NetBSD 10. + ** Build-related gzip-compressed tarballs are no longer distributed, diff --git a/src/tail.c b/src/tail.c index 2ca27e835..aa612e5e4 100644 --- a/src/tail.c +++ b/src/tail.c @@ -1177,6 +1177,10 @@ tail_forever (struct File_spec *f, int n_files, double sleep_interval) static bool debugged; + /* Flush initial output, in case reading blocks below. */ + if (fflush (stdout) < 0) + write_error (); + while (true) { /* Use blocking I/O as an optimization, when it's easy. */ diff --git a/tests/tail/pipe-f2.sh b/tests/tail/pipe-f2.sh index 6d26c422a..28dd92eb5 100755 --- a/tests/tail/pipe-f2.sh +++ b/tests/tail/pipe-f2.sh @@ -21,7 +21,6 @@ print_ver_ tail mkfifo_or_skip_ fifo -echo 1 > fifo & echo 1 > exp || framework_failure_ # Terminate any background tail process @@ -30,18 +29,22 @@ 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() { sleep $1; test -s out; } -# Wait 12.7s for tail to write something. -retry_delay_ check_tail_output .1 7 || fail=1 +for mode in '' '---disable-inotify'; do + rm -f out || framework_failure_ + echo 1 > fifo & + timeout 10 tail $fastpoll -f $mode fifo > out & pid=$! -compare exp out || fail=1 + # Wait 12.7s for tail to write something. + retry_delay_ check_tail_output .1 7 || fail=1 -# Ensure tail is still running -kill -0 $pid || fail=1 + compare exp out || fail=1 -cleanup_ + # Ensure tail is still running + kill -0 $pid || fail=1 + + cleanup_ +done Exit $fail