/* If true, read from the ends of all specified files until killed. */
static bool forever;
+/* If true, monitor output so we exit if pipe reader terminates. */
+static bool monitor_output;
+
/* If true, count from start of file instead of end. */
static bool from_start;
exit (status);
}
+/* If the output has gone away, then terminate
+ as we would if we had written to this output. */
+static void
+check_output_alive (void)
+{
+ if (! monitor_output)
+ return;
+
+ struct timeval delay;
+ delay.tv_sec = delay.tv_usec = 0;
+
+ fd_set rfd;
+ FD_ZERO (&rfd);
+ FD_SET (STDOUT_FILENO, &rfd);
+
+ /* readable event on STDOUT is equivalent to POLLERR,
+ and implies an error condition on output like broken pipe. */
+ if (select (STDOUT_FILENO + 1, &rfd, NULL, NULL, &delay) == 1)
+ raise (SIGPIPE);
+}
+
static bool
valid_file_spec (struct File_spec const *f)
{
if ((!any_input || blocking) && fflush (stdout) != 0)
die (EXIT_FAILURE, errno, _("write error"));
+ check_output_alive ();
+
/* If nothing was read, sleep and/or check for dead writers. */
if (!any_input)
{
/* When watching a PID, ensure that a read from WD will not block
indefinitely. */
- if (pid && (len <= evbuf_off))
+ while (len <= evbuf_off)
{
- if (writer_is_dead)
- exit (EXIT_SUCCESS);
-
- writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);
-
struct timeval delay; /* how long to wait for file changes. */
- if (writer_is_dead)
- delay.tv_sec = delay.tv_usec = 0;
- else
+
+ if (pid)
{
- delay.tv_sec = (time_t) sleep_interval;
- delay.tv_usec = 1000000 * (sleep_interval - delay.tv_sec);
+ if (writer_is_dead)
+ exit (EXIT_SUCCESS);
+
+ writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);
+
+ if (writer_is_dead)
+ delay.tv_sec = delay.tv_usec = 0;
+ else
+ {
+ delay.tv_sec = (time_t) sleep_interval;
+ delay.tv_usec = 1000000 * (sleep_interval - delay.tv_sec);
+ }
}
fd_set rfd;
FD_ZERO (&rfd);
FD_SET (wd, &rfd);
+ if (monitor_output)
+ FD_SET (STDOUT_FILENO, &rfd);
- int file_change = select (wd + 1, &rfd, NULL, NULL, &delay);
+ int file_change = select (MAX (wd, STDOUT_FILENO) + 1,
+ &rfd, NULL, NULL, pid ? &delay: NULL);
if (file_change == 0)
continue;
else if (file_change == -1)
- die (EXIT_FAILURE, errno, _("error monitoring inotify event"));
+ die (EXIT_FAILURE, errno,
+ _("error waiting for inotify and output events"));
+ else if (FD_ISSET (STDOUT_FILENO, &rfd))
+ {
+ /* readable event on STDOUT is equivalent to POLLERR,
+ and implies an error on output like broken pipe. */
+ raise (SIGPIPE);
+ }
+ else
+ break;
}
if (len <= evbuf_off)
if (forever && ignore_fifo_and_pipe (F, n_files))
{
+ /* If stdout is a fifo or pipe, then monitor it
+ so that we exit if the reader goes away.
+ Note select() on a regular file is always readable. */
+ struct stat out_stat;
+ if (fstat (STDOUT_FILENO, &out_stat) < 0)
+ die (EXIT_FAILURE, errno, _("standard output"));
+ monitor_output = (S_ISFIFO (out_stat.st_mode)
+ || (HAVE_FIFO_PIPES != 1 && isapipe (STDOUT_FILENO)));
+
#if HAVE_INOTIFY
/* tailable_stdin() checks if the user specifies stdin via "-",
or implicitly by providing no arguments. If so, we won't use inotify.
#!/bin/sh
-# ensure that :|tail -f doesn't hang, per POSIX
+# ensure that tail -f doesn't hang in various cases
# Copyright (C) 2009-2017 Free Software Foundation, Inc.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail
+# Speedup the non inotify case
+fastpoll='-s.1 --max-unchanged-stats=1'
+
+for mode in '' '---disable-inotify'; do
+# Ensure :|tail -f doesn't hang, per POSIX
echo oo > exp || framework_failure_
-echo foo | timeout 10 tail -f -c3 > out || fail=1
+echo foo | timeout 10 tail -f $mode $fastpoll -c3 > out || fail=1
compare exp out || fail=1
-
cat <<\EOF > exp || framework_failure_
==> standard input <==
ar
EOF
-echo bar | returns_ 1 timeout 10 tail -f -c3 - missing > out || fail=1
+echo bar | returns_ 1 \
+ timeout 10 tail -f $mode $fastpoll -c3 - missing > out || fail=1
compare exp out || fail=1
+# This would wait indefinitely before v8.28 due to no EPIPE being
+# generated due to no data written after the first small amount
+timeout 10 tail -f $mode $fastpoll out | sleep .1 || fail=1
+
+# This would wait indefinitely before v8.28 (until first write)
+returns_ 1 timeout 10 tail -f $mode $fastpoll /dev/null >&- || fail=1
+done
+
Exit $fail