diff options
Diffstat (limited to 'src/tail.c')
| -rw-r--r-- | src/tail.c | 146 |
1 files changed, 88 insertions, 58 deletions
diff --git a/src/tail.c b/src/tail.c index f0dbf5d5e..f3fe6a341 100644 --- a/src/tail.c +++ b/src/tail.c @@ -201,6 +201,10 @@ static bool have_read_stdin; more expensive) code unconditionally. Intended solely for testing. */ static bool presume_input_pipe; +/* If nonzero then don't use inotify even if available. + Intended solely for testing. */ +static bool disable_inotify; + /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum @@ -209,7 +213,8 @@ enum MAX_UNCHANGED_STATS_OPTION, PID_OPTION, PRESUME_INPUT_PIPE_OPTION, - LONG_FOLLOW_OPTION + LONG_FOLLOW_OPTION, + DISABLE_INOTIFY_OPTION }; static struct option const long_options[] = @@ -218,6 +223,8 @@ static struct option const long_options[] = {"follow", optional_argument, NULL, LONG_FOLLOW_OPTION}, {"lines", required_argument, NULL, 'n'}, {"max-unchanged-stats", required_argument, NULL, MAX_UNCHANGED_STATS_OPTION}, + {"-disable-inotify", no_argument, NULL, + DISABLE_INOTIFY_OPTION}, /* do not document */ {"pid", required_argument, NULL, PID_OPTION}, {"-presume-input-pipe", no_argument, NULL, PRESUME_INPUT_PIPE_OPTION}, /* do not document */ @@ -283,7 +290,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ name, i.e., with --follow=name\n\ "), stdout); fputs (_("\ - -s, --sleep-interval=S with -f, sleep for approximately S seconds\n\ + -s, --sleep-interval=N with -f, sleep for approximately N seconds\n\ (default 1.0) between iterations\n\ -v, --verbose always output headers giving file names\n\ "), stdout); @@ -310,7 +317,7 @@ rotation). Use --follow=name in that case. That causes tail to track the\n\ named file by reopening it periodically to see if it has been removed and\n\ recreated by some other program.\n\ "), stdout); - emit_bug_reporting_address (); + emit_ancillary_info (); } exit (status); } @@ -351,7 +358,7 @@ record_open_fd (struct File_spec *f, int fd, f->mode = st->st_mode; f->blocking = blocking; f->n_unchanged_stats = 0; - f->ignore = 0; + f->ignore = false; } /* Close the file with descriptor FD and name FILENAME. */ @@ -1119,7 +1126,7 @@ tail_forever (struct File_spec *f, size_t n_files, double sleep_interval) break; } - if ((!any_input | blocking) && fflush (stdout) != 0) + if ((!any_input || blocking) && fflush (stdout) != 0) error (EXIT_FAILURE, errno, _("write error")); /* If nothing was read, sleep and/or check for dead writers. */ @@ -1128,9 +1135,6 @@ tail_forever (struct File_spec *f, size_t n_files, double sleep_interval) if (writer_is_dead) break; - if (xnanosleep (sleep_interval)) - error (EXIT_FAILURE, errno, _("cannot read realtime clock")); - /* Once the writer is dead, read the files once more to avoid a race condition. */ writer_is_dead = (pid != 0 @@ -1139,6 +1143,10 @@ tail_forever (struct File_spec *f, size_t n_files, double sleep_interval) signal to the writer, so kill fails and sets errno to EPERM. */ && errno != EPERM); + + if (!writer_is_dead && xnanosleep (sleep_interval)) + error (EXIT_FAILURE, errno, _("cannot read realtime clock")); + } } } @@ -1172,6 +1180,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, Hash_table *wd_table; bool found_watchable = false; + bool writer_is_dead = false; int prev_wd; size_t evlen = 0; char *evbuf; @@ -1259,30 +1268,30 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, indefinetely. */ if (pid) { - fd_set rfd; - struct timeval select_timeout; - int n_descriptors; - - FD_ZERO (&rfd); - FD_SET (wd, &rfd); + if (writer_is_dead) + exit (EXIT_SUCCESS); - select_timeout.tv_sec = (time_t) sleep_interval; - select_timeout.tv_usec = 1000000 * (sleep_interval - - select_timeout.tv_sec); + writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM); - n_descriptors = select (wd + 1, &rfd, NULL, NULL, &select_timeout); + struct timeval delay; /* how long to wait for file changes. */ + 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); + } - if (n_descriptors == -1) - error (EXIT_FAILURE, errno, _("error monitoring inotify event")); + fd_set rfd; + FD_ZERO (&rfd); + FD_SET (wd, &rfd); - if (n_descriptors == 0) - { - /* See if the process we are monitoring is still alive. */ - if (kill (pid, 0) != 0 && errno != EPERM) - exit (EXIT_SUCCESS); + int file_change = select (wd + 1, &rfd, NULL, NULL, &delay); - continue; - } + if (file_change == 0) + continue; + else if (file_change == -1) + error (EXIT_FAILURE, errno, _("error monitoring inotify event")); } if (len <= evbuf_off) @@ -1392,7 +1401,6 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, if (fflush (stdout) != 0) error (EXIT_FAILURE, errno, _("write error")); } - } #endif @@ -1794,6 +1802,10 @@ parse_options (int argc, char **argv, } break; + case DISABLE_INOTIFY_OPTION: + disable_inotify = true; + break; + case PID_OPTION: { strtol_error s_err; @@ -1912,28 +1924,6 @@ main (int argc, char **argv) static char *dummy_stdin = (char *) "-"; n_files = 1; file = &dummy_stdin; - - /* POSIX says that -f is ignored if no file operand is specified - and standard input is a pipe. However, the GNU coding - standards say that program behavior should not depend on - device type, because device independence is an important - principle of the system's design. - - Follow the POSIX requirement only if POSIXLY_CORRECT is set. */ - - if (forever && getenv ("POSIXLY_CORRECT")) - { - struct stat st; - int is_a_fifo_or_pipe = - (fstat (STDIN_FILENO, &st) != 0 ? -1 - : S_ISFIFO (st.st_mode) ? 1 - : HAVE_FIFO_PIPES == 1 ? 0 - : isapipe (STDIN_FILENO)); - if (is_a_fifo_or_pipe < 0) - error (EXIT_FAILURE, errno, _("standard input")); - if (is_a_fifo_or_pipe) - forever = false; - } } { @@ -1969,18 +1959,58 @@ main (int argc, char **argv) for (i = 0; i < n_files; i++) ok &= tail_file (&F[i], n_units); - if (forever) + /* When there is no FILE operand and stdin is a pipe or FIFO + POSIX requires that tail ignore the -f option. + Since we allow multiple FILE operands, we extend that to say: + ignore any "-" operand that corresponds to a pipe or FIFO. */ + size_t n_viable = 0; + for (i = 0; i < n_files; i++) { -#if HAVE_INOTIFY - int wd = inotify_init (); - if (wd < 0) - error (0, errno, _("inotify cannot be used, reverting to polling")); + bool is_a_fifo_or_pipe = + (STREQ (F[i].name, "-") + && !F[i].ignore + && 0 <= F[i].fd + && (S_ISFIFO (F[i].mode) + || (HAVE_FIFO_PIPES != 1 && isapipe (F[i].fd)))); + if (is_a_fifo_or_pipe) + F[i].ignore = true; else + ++n_viable; + } + + if (forever && n_viable) + { +#if HAVE_INOTIFY + /* If the user specifies stdin via a command line argument of "-", + or implicitly by providing no arguments, we won't use inotify. + Technically, on systems with a working /dev/stdin, we *could*, + but would it be worth it? Verifying that it's a real device + and hooked up to stdin is not trivial, while reverting to + non-inotify-based tail_forever is easy and portable. */ + bool stdin_cmdline_arg = false; + + for (i = 0; i < n_files; i++) + if (!F[i].ignore && STREQ (F[i].name, "-")) + stdin_cmdline_arg = true; + + if (!disable_inotify && !stdin_cmdline_arg) { - tail_forever_inotify (wd, F, n_files, sleep_interval); + int wd = inotify_init (); + if (wd < 0) + error (0, errno, _("inotify cannot be used, reverting to polling")); + else + { + /* Flush any output from tail_file, now, since + tail_forever_inotify flushes only after writing, + not before reading. */ + if (fflush (stdout) != 0) + error (EXIT_FAILURE, errno, _("write error")); - /* The only way the above returns is upon failure. */ - exit (EXIT_FAILURE); + tail_forever_inotify (wd, F, n_files, sleep_interval); + + /* The only way the above returns is upon failure. */ + exit (EXIT_FAILURE); + } } #endif tail_forever (F, n_files, sleep_interval); |
