670 lines
22 KiB
Diff
670 lines
22 KiB
Diff
diff -urNp coreutils-7.6-orig/src/tail.c coreutils-7.6/src/tail.c
|
|
--- coreutils-7.6-orig/src/tail.c 2010-01-12 15:26:31.000000000 +0100
|
|
+++ coreutils-7.6/src/tail.c 2010-01-12 15:27:18.000000000 +0100
|
|
@@ -1,6 +1,5 @@
|
|
/* tail -- output the last part of file(s)
|
|
- Copyright (C) 1989, 90, 91, 1995-2006, 2008-2009 Free Software
|
|
- Foundation, Inc.
|
|
+ Copyright (C) 1989-1991, 1995-2006, 2008-2010 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
|
|
@@ -52,6 +51,12 @@
|
|
# include <sys/inotify.h>
|
|
/* `select' is used by tail_forever_inotify. */
|
|
# include <sys/select.h>
|
|
+
|
|
+/* inotify needs to know if a file is local. */
|
|
+# include "fs.h"
|
|
+# if HAVE_SYS_STATFS_H
|
|
+# include <sys/statfs.h>
|
|
+# endif
|
|
#endif
|
|
|
|
/* The official name of this program (e.g., no `g' prefix). */
|
|
@@ -105,9 +110,6 @@ struct File_spec
|
|
/* The actual file name, or "-" for stdin. */
|
|
char *name;
|
|
|
|
- /* File descriptor on which the file is open; -1 if it's not open. */
|
|
- int fd;
|
|
-
|
|
/* Attributes of the file the last time we checked. */
|
|
off_t size;
|
|
struct timespec mtime;
|
|
@@ -115,24 +117,27 @@ struct File_spec
|
|
ino_t ino;
|
|
mode_t mode;
|
|
|
|
- /* 1 if O_NONBLOCK is clear, 0 if set, -1 if not known. */
|
|
- int blocking;
|
|
-
|
|
/* The specified name initially referred to a directory or some other
|
|
type for which tail isn't meaningful. Unlike for a permission problem
|
|
(tailable, below) once this is set, the name is not checked ever again. */
|
|
bool ignore;
|
|
|
|
- /* See description of DEFAULT_MAX_N_... below. */
|
|
- uintmax_t n_unchanged_stats;
|
|
+ /* See the description of fremote. */
|
|
+ bool remote;
|
|
|
|
/* A file is tailable if it exists, is readable, and is of type
|
|
IS_TAILABLE_FILE_TYPE. */
|
|
bool tailable;
|
|
|
|
+ /* File descriptor on which the file is open; -1 if it's not open. */
|
|
+ int fd;
|
|
+
|
|
/* The value of errno seen last time we checked this file. */
|
|
int errnum;
|
|
|
|
+ /* 1 if O_NONBLOCK is clear, 0 if set, -1 if not known. */
|
|
+ int blocking;
|
|
+
|
|
#if HAVE_INOTIFY
|
|
/* The watch descriptor used by inotify. */
|
|
int wd;
|
|
@@ -144,6 +149,9 @@ struct File_spec
|
|
/* Offset in NAME of the basename part. */
|
|
size_t basename_start;
|
|
#endif
|
|
+
|
|
+ /* See description of DEFAULT_MAX_N_... below. */
|
|
+ uintmax_t n_unchanged_stats;
|
|
};
|
|
|
|
#if HAVE_INOTIFY
|
|
@@ -201,8 +209,7 @@ 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. */
|
|
+/* If nonzero then don't use inotify even if available. */
|
|
static bool disable_inotify;
|
|
|
|
/* For long options that have no equivalent short option, use a
|
|
@@ -260,8 +267,8 @@ With no FILE, or when FILE is -, read st
|
|
Mandatory arguments to long options are mandatory for short options too.\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
- -c, --bytes=K output the last K bytes; alternatively, use +K to\n\
|
|
- output bytes starting with the Kth of each file\n\
|
|
+ -c, --bytes=K output the last K bytes; alternatively, use -c +K\n\
|
|
+ to output bytes starting with the Kth of each file\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
-f, --follow[={name|descriptor}]\n\
|
|
@@ -272,7 +279,7 @@ Mandatory arguments to long options are
|
|
"), stdout);
|
|
printf (_("\
|
|
-n, --lines=K output the last K lines, instead of the last %d;\n\
|
|
- or use +K to output lines starting with the Kth\n\
|
|
+ or use -n +K to output lines starting with the Kth\n\
|
|
--max-unchanged-stats=N\n\
|
|
with --follow=name, reopen a FILE which has not\n\
|
|
changed size after N (default %d) iterations\n\
|
|
@@ -308,14 +315,10 @@ GB 1000*1000*1000, G 1024*1024*1024, and
|
|
fputs (_("\
|
|
With --follow (-f), tail defaults to following the file descriptor, which\n\
|
|
means that even if a tail'ed file is renamed, tail will continue to track\n\
|
|
-its end. \
|
|
-"), stdout);
|
|
- fputs (_("\
|
|
-This default behavior is not desirable when you really want to\n\
|
|
+its end. This default behavior is not desirable when you really want to\n\
|
|
track the actual name of the file, not the file descriptor (e.g., log\n\
|
|
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\
|
|
+named file in a way that accommodates renaming, removal and creation.\n\
|
|
"), stdout);
|
|
emit_bug_reporting_address ();
|
|
}
|
|
@@ -865,6 +868,50 @@ start_lines (const char *pretty_filename
|
|
}
|
|
}
|
|
|
|
+#if HAVE_INOTIFY
|
|
+/* Without inotify support, always return false. Otherwise, return false
|
|
+ when FD is open on a file known to reside on a local file system.
|
|
+ If fstatfs fails, give a diagnostic and return true.
|
|
+ If fstatfs cannot be called, return true. */
|
|
+static bool
|
|
+fremote (int fd, const char *name)
|
|
+{
|
|
+ bool remote = true; /* be conservative (poll by default). */
|
|
+
|
|
+# if HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE && defined __linux__
|
|
+ struct statfs buf;
|
|
+ int err = fstatfs (fd, &buf);
|
|
+ if (err != 0)
|
|
+ {
|
|
+ error (0, errno, _("cannot determine location of %s. "
|
|
+ "reverting to polling"), quote (name));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ switch (buf.f_type)
|
|
+ {
|
|
+ case S_MAGIC_CODA:
|
|
+ case S_MAGIC_FUSECTL:
|
|
+ case S_MAGIC_LUSTRE:
|
|
+ case S_MAGIC_NCP:
|
|
+ case S_MAGIC_NFS:
|
|
+ case S_MAGIC_NFSD:
|
|
+ case S_MAGIC_SMB:
|
|
+ break;
|
|
+ default:
|
|
+ remote = false;
|
|
+ }
|
|
+ }
|
|
+# endif
|
|
+
|
|
+ return remote;
|
|
+}
|
|
+#else
|
|
+/* Without inotify support, whether a file is remote is irrelevant.
|
|
+ Always return "false" in that case. */
|
|
+# define fremote(fd, name) false
|
|
+#endif
|
|
+
|
|
/* FIXME: describe */
|
|
|
|
static void
|
|
@@ -921,6 +968,15 @@ recheck (struct File_spec *f, bool block
|
|
quote (pretty_name (f)));
|
|
f->ignore = true;
|
|
}
|
|
+ else if (!disable_inotify && fremote (fd, pretty_name (f)))
|
|
+ {
|
|
+ ok = false;
|
|
+ f->errnum = -1;
|
|
+ error (0, 0, _("%s has been replaced with a remote file. "
|
|
+ "giving up on this name"), quote (pretty_name (f)));
|
|
+ f->ignore = true;
|
|
+ f->remote = true;
|
|
+ }
|
|
else
|
|
{
|
|
f->errnum = 0;
|
|
@@ -1126,7 +1182,7 @@ tail_forever (struct File_spec *f, size_
|
|
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. */
|
|
@@ -1135,9 +1191,6 @@ tail_forever (struct File_spec *f, size_
|
|
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
|
|
@@ -1146,12 +1199,44 @@ tail_forever (struct File_spec *f, size_
|
|
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"));
|
|
+
|
|
}
|
|
}
|
|
}
|
|
|
|
#if HAVE_INOTIFY
|
|
|
|
+/* Return true if any of the N_FILES files in F is remote, i.e., has
|
|
+ an open file descriptor and is on a network file system. */
|
|
+
|
|
+static bool
|
|
+any_remote_file (const struct File_spec *f, size_t n_files)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < n_files; i++)
|
|
+ if (0 <= f[i].fd && f[i].remote)
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* Return true if any of the N_FILES files in F represents
|
|
+ stdin and is tailable. */
|
|
+
|
|
+static bool
|
|
+tailable_stdin (const struct File_spec *f, size_t n_files)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < n_files; i++)
|
|
+ if (!f[i].ignore && STREQ (f[i].name, "-"))
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
static size_t
|
|
wd_hasher (const void *entry, size_t tabsize)
|
|
{
|
|
@@ -1167,31 +1252,73 @@ wd_comparator (const void *e1, const voi
|
|
return spec1->wd == spec2->wd;
|
|
}
|
|
|
|
+/* Helper function used by `tail_forever_inotify'. */
|
|
+static void
|
|
+check_fspec (struct File_spec *fspec, int wd, int *prev_wd)
|
|
+{
|
|
+ struct stat stats;
|
|
+ char const *name = pretty_name (fspec);
|
|
+
|
|
+ if (fstat (fspec->fd, &stats) != 0)
|
|
+ {
|
|
+ close_fd (fspec->fd, name);
|
|
+ fspec->fd = -1;
|
|
+ fspec->errnum = errno;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (S_ISREG (fspec->mode) && stats.st_size < fspec->size)
|
|
+ {
|
|
+ error (0, 0, _("%s: file truncated"), name);
|
|
+ *prev_wd = wd;
|
|
+ xlseek (fspec->fd, stats.st_size, SEEK_SET, name);
|
|
+ fspec->size = stats.st_size;
|
|
+ }
|
|
+ else if (S_ISREG (fspec->mode) && stats.st_size == fspec->size
|
|
+ && timespec_cmp (fspec->mtime, get_stat_mtime (&stats)) == 0)
|
|
+ return;
|
|
+
|
|
+ if (wd != *prev_wd)
|
|
+ {
|
|
+ if (print_headers)
|
|
+ write_header (name);
|
|
+ *prev_wd = wd;
|
|
+ }
|
|
+
|
|
+ uintmax_t bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
|
|
+ fspec->size += bytes_read;
|
|
+
|
|
+ if (fflush (stdout) != 0)
|
|
+ error (EXIT_FAILURE, errno, _("write error"));
|
|
+}
|
|
+
|
|
/* Tail N_FILES files forever, or until killed.
|
|
Check modifications using the inotify events system. */
|
|
-
|
|
static void
|
|
tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
|
|
double sleep_interval)
|
|
{
|
|
- size_t i;
|
|
unsigned int max_realloc = 3;
|
|
- Hash_table *wd_table;
|
|
+
|
|
+ /* Map an inotify watch descriptor to the name of the file it's watching. */
|
|
+ Hash_table *wd_to_name;
|
|
|
|
bool found_watchable = false;
|
|
+ bool writer_is_dead = false;
|
|
int prev_wd;
|
|
size_t evlen = 0;
|
|
char *evbuf;
|
|
size_t evbuf_off = 0;
|
|
size_t len = 0;
|
|
|
|
- wd_table = hash_initialize (n_files, NULL, wd_hasher, wd_comparator, NULL);
|
|
- if (! wd_table)
|
|
+ wd_to_name = hash_initialize (n_files, NULL, wd_hasher, wd_comparator, NULL);
|
|
+ if (! wd_to_name)
|
|
xalloc_die ();
|
|
|
|
/* Add an inotify watch for each watched file. If -F is specified then watch
|
|
its parent directory too, in this way when they re-appear we can add them
|
|
again to the watch list. */
|
|
+ size_t i;
|
|
for (i = 0; i < n_files; i++)
|
|
{
|
|
if (!f[i].ignore)
|
|
@@ -1235,7 +1362,7 @@ tail_forever_inotify (int wd, struct Fil
|
|
continue;
|
|
}
|
|
|
|
- if (hash_insert (wd_table, &(f[i])) == NULL)
|
|
+ if (hash_insert (wd_to_name, &(f[i])) == NULL)
|
|
xalloc_die ();
|
|
|
|
found_watchable = true;
|
|
@@ -1247,6 +1374,14 @@ tail_forever_inotify (int wd, struct Fil
|
|
|
|
prev_wd = f[n_files - 1].wd;
|
|
|
|
+ /* Check files again. New data can be available since last time we checked
|
|
+ and before they are watched by inotify. */
|
|
+ for (i = 0; i < n_files; i++)
|
|
+ {
|
|
+ if (!f[i].ignore)
|
|
+ check_fspec (&f[i], f[i].wd, &prev_wd);
|
|
+ }
|
|
+
|
|
evlen += sizeof (struct inotify_event) + 1;
|
|
evbuf = xmalloc (evlen);
|
|
|
|
@@ -1255,41 +1390,37 @@ tail_forever_inotify (int wd, struct Fil
|
|
This loop sleeps on the `safe_read' call until a new event is notified. */
|
|
while (1)
|
|
{
|
|
- char const *name;
|
|
struct File_spec *fspec;
|
|
- uintmax_t bytes_read;
|
|
- struct stat stats;
|
|
-
|
|
struct inotify_event *ev;
|
|
|
|
/* When watching a PID, ensure that a read from WD will not block
|
|
- indefinetely. */
|
|
+ indefinitely. */
|
|
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)
|
|
@@ -1315,41 +1446,59 @@ tail_forever_inotify (int wd, struct Fil
|
|
ev = (struct inotify_event *) (evbuf + evbuf_off);
|
|
evbuf_off += sizeof (*ev) + ev->len;
|
|
|
|
- if (ev->len)
|
|
+ if (ev->len) /* event on ev->name in watched directory */
|
|
{
|
|
- for (i = 0; i < n_files; i++)
|
|
+ size_t j;
|
|
+ for (j = 0; j < n_files; j++)
|
|
{
|
|
/* With N=hundreds of frequently-changing files, this O(N^2)
|
|
process might be a problem. FIXME: use a hash table? */
|
|
- if (f[i].parent_wd == ev->wd
|
|
- && STREQ (ev->name, f[i].name + f[i].basename_start))
|
|
+ if (f[j].parent_wd == ev->wd
|
|
+ && STREQ (ev->name, f[j].name + f[j].basename_start))
|
|
break;
|
|
}
|
|
|
|
/* It is not a watched file. */
|
|
- if (i == n_files)
|
|
+ if (j == n_files)
|
|
continue;
|
|
|
|
- f[i].wd = inotify_add_watch (wd, f[i].name, inotify_wd_mask);
|
|
-
|
|
- if (f[i].wd < 0)
|
|
+ /* It's fine to add the same file more than once. */
|
|
+ int new_wd = inotify_add_watch (wd, f[j].name, inotify_wd_mask);
|
|
+ if (new_wd < 0)
|
|
{
|
|
- error (0, errno, _("cannot watch %s"), quote (f[i].name));
|
|
+ error (0, errno, _("cannot watch %s"), quote (f[j].name));
|
|
continue;
|
|
}
|
|
|
|
- fspec = &(f[i]);
|
|
- if (hash_insert (wd_table, fspec) == NULL)
|
|
+ fspec = &(f[j]);
|
|
+
|
|
+ /* Remove `fspec' and re-add it using `new_fd' as its key. */
|
|
+ hash_delete (wd_to_name, fspec);
|
|
+ fspec->wd = new_wd;
|
|
+
|
|
+ /* If the file was moved then inotify will use the source file wd for
|
|
+ the destination file. Make sure the key is not present in the
|
|
+ table. */
|
|
+ struct File_spec *prev = hash_delete (wd_to_name, fspec);
|
|
+ if (prev && prev != fspec)
|
|
+ {
|
|
+ if (follow_mode == Follow_name)
|
|
+ recheck (prev, false);
|
|
+ prev->wd = -1;
|
|
+ close_fd (prev->fd, pretty_name (prev));
|
|
+ }
|
|
+
|
|
+ if (hash_insert (wd_to_name, fspec) == NULL)
|
|
xalloc_die ();
|
|
|
|
if (follow_mode == Follow_name)
|
|
- recheck (&(f[i]), false);
|
|
+ recheck (fspec, false);
|
|
}
|
|
else
|
|
{
|
|
struct File_spec key;
|
|
key.wd = ev->wd;
|
|
- fspec = hash_lookup (wd_table, &key);
|
|
+ fspec = hash_lookup (wd_to_name, &key);
|
|
}
|
|
|
|
if (! fspec)
|
|
@@ -1357,53 +1506,23 @@ tail_forever_inotify (int wd, struct Fil
|
|
|
|
if (ev->mask & (IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF))
|
|
{
|
|
- /* For IN_DELETE_SELF, we always want to remove the watch.
|
|
- However, for IN_MOVE_SELF (the file we're watching has
|
|
- been clobbered via a rename), when tailing by NAME, we
|
|
- must continue to watch the file. It's only when following
|
|
- by file descriptor that we must remove the watch. */
|
|
- if ((ev->mask & IN_DELETE_SELF)
|
|
- || ((ev->mask & IN_MOVE_SELF) && follow_mode == Follow_descriptor))
|
|
+ /* For IN_DELETE_SELF, we always want to remove the watch.
|
|
+ However, for IN_MOVE_SELF (the file we're watching has
|
|
+ been clobbered via a rename), when tailing by NAME, we
|
|
+ must continue to watch the file. It's only when following
|
|
+ by file descriptor that we must remove the watch. */
|
|
+ if ((ev->mask & IN_DELETE_SELF)
|
|
+ || ((ev->mask & IN_MOVE_SELF) && follow_mode == Follow_descriptor))
|
|
{
|
|
- inotify_rm_watch (wd, f[i].wd);
|
|
- hash_delete (wd_table, &(f[i]));
|
|
+ inotify_rm_watch (wd, fspec->wd);
|
|
+ hash_delete (wd_to_name, fspec);
|
|
}
|
|
if (follow_mode == Follow_name)
|
|
recheck (fspec, false);
|
|
|
|
continue;
|
|
}
|
|
-
|
|
- name = pretty_name (fspec);
|
|
-
|
|
- if (fstat (fspec->fd, &stats) != 0)
|
|
- {
|
|
- close_fd (fspec->fd, name);
|
|
- fspec->fd = -1;
|
|
- fspec->errnum = errno;
|
|
- continue;
|
|
- }
|
|
-
|
|
- if (S_ISREG (fspec->mode) && stats.st_size < fspec->size)
|
|
- {
|
|
- error (0, 0, _("%s: file truncated"), name);
|
|
- prev_wd = ev->wd;
|
|
- xlseek (fspec->fd, stats.st_size, SEEK_SET, name);
|
|
- fspec->size = stats.st_size;
|
|
- }
|
|
-
|
|
- if (ev->wd != prev_wd)
|
|
- {
|
|
- if (print_headers)
|
|
- write_header (name);
|
|
- prev_wd = ev->wd;
|
|
- }
|
|
-
|
|
- bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
|
|
- fspec->size += bytes_read;
|
|
-
|
|
- if (fflush (stdout) != 0)
|
|
- error (EXIT_FAILURE, errno, _("write error"));
|
|
+ check_fspec (fspec, ev->wd, &prev_wd);
|
|
}
|
|
}
|
|
#endif
|
|
@@ -1602,7 +1721,7 @@ tail_file (struct File_spec *f, uintmax_
|
|
/* Before the tail function provided `read_pos', there was
|
|
a race condition described in the URL below. This sleep
|
|
call made the window big enough to exercise the problem. */
|
|
- sleep (1);
|
|
+ xnanosleep (1);
|
|
#endif
|
|
f->errnum = ok - 1;
|
|
if (fstat (fd, &stats) < 0)
|
|
@@ -1632,6 +1751,7 @@ tail_file (struct File_spec *f, uintmax_
|
|
to avoid a race condition described by Ken Raeburn:
|
|
http://mail.gnu.org/archive/html/bug-textutils/2003-05/msg00007.html */
|
|
record_open_fd (f, fd, read_pos, &stats, (is_stdin ? -1 : 1));
|
|
+ f->remote = fremote (fd, pretty_name (f));
|
|
}
|
|
}
|
|
else
|
|
@@ -1872,6 +1992,35 @@ parse_options (int argc, char **argv,
|
|
}
|
|
}
|
|
|
|
+/* Mark as '.ignore'd each member of F that corresponds to a
|
|
+ pipe or fifo, and return the number of non-ignored members. */
|
|
+static size_t
|
|
+ignore_fifo_and_pipe (struct File_spec *f, size_t n_files)
|
|
+{
|
|
+ /* 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: with -f,
|
|
+ ignore any "-" operand that corresponds to a pipe or FIFO. */
|
|
+ size_t n_viable = 0;
|
|
+
|
|
+ size_t i;
|
|
+ for (i = 0; i < n_files; i++)
|
|
+ {
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ return n_viable;
|
|
+}
|
|
+
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
@@ -1963,41 +2112,31 @@ main (int argc, char **argv)
|
|
for (i = 0; i < n_files; i++)
|
|
ok &= tail_file (&F[i], n_units);
|
|
|
|
- /* 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++)
|
|
- {
|
|
- 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 (forever && ignore_fifo_and_pipe (F, n_files))
|
|
{
|
|
#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.
|
|
+ /* tailable_stdin() checks if the user specifies stdin via "-",
|
|
+ or implicitly by providing no arguments. If so, 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;
|
|
+ non-inotify-based tail_forever is easy and portable.
|
|
|
|
- for (i = 0; i < n_files; i++)
|
|
- if (!F[i].ignore && STREQ (F[i].name, "-"))
|
|
- stdin_cmdline_arg = true;
|
|
+ any_remote_file() checks if the user has specified any
|
|
+ files that reside on remote file systems. inotify is not used
|
|
+ in this case because it would miss any updates to the file
|
|
+ that were not initiated from the local system.
|
|
+
|
|
+ FIXME: inotify doesn't give any notification when a new
|
|
+ (remote) file or directory is mounted on top a watched file.
|
|
+ When follow_mode == Follow_name we would ideally like to detect that.
|
|
+ Note if there is a change to the original file then we'll
|
|
+ recheck it and follow the new file, or ignore it if the
|
|
+ file has changed to being remote. */
|
|
+ if (tailable_stdin (F, n_files) || any_remote_file (F, n_files))
|
|
+ disable_inotify = true;
|
|
|
|
- if (!disable_inotify && !stdin_cmdline_arg)
|
|
+ if (!disable_inotify)
|
|
{
|
|
int wd = inotify_init ();
|
|
if (wd < 0)
|