diff options
author | Jim Meyering <jim@meyering.net> | 1998-03-17 23:31:18 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1998-03-17 23:31:18 +0000 |
commit | fcbfefa5131a12d1f4e2acc821bbf9e1cbe2ca4a (patch) | |
tree | d99a174f7ff9af2d7056cd17bbf5509993e1076e /src | |
parent | fcf6405f2dbe1cb7e942edd4d78b7b360d786c9b (diff) | |
download | coreutils-fcbfefa5131a12d1f4e2acc821bbf9e1cbe2ca4a.tar.xz |
*** empty log message ***
Diffstat (limited to 'src')
-rw-r--r-- | src/tail.c | 223 |
1 files changed, 165 insertions, 58 deletions
diff --git a/src/tail.c b/src/tail.c index c61f9a84a..04cbf91de 100644 --- a/src/tail.c +++ b/src/tail.c @@ -83,22 +83,51 @@ # define BUFSIZ (512 * 8) #endif +enum Follow_mode +{ + /* FIXME */ + follow_name, + + /* FIXME */ + follow_descriptor, +}; + +struct File_spec +{ + /* The actual file name, or "-" for stdin. */ + char *name; + + /* The actual file name, or "standard input" for stdin. */ + char *pretty_name; + + /* File descriptor on which the file is open; -1 if it's not open. */ + int fd; + + /* The size of the file the last time we checked. */ + off_t size; + + /* The device and inode of the file the last time we checked. */ + dev_t dev; + ino_t ino; + + unsigned int no_change_counter; +}; + /* If nonzero, interpret the numeric argument as the number of lines. Otherwise, interpret it as the number of bytes. */ static int count_lines; +/* Whether we follow the name of each file or the file descriptor + that is initially associated with each name. */ +/* FIXME: make follow_name the default? */ +static enum Follow_mode follow_mode; + /* If nonzero, read from the end of one file until killed. */ static int forever; /* If nonzero, read from the end of multiple files until killed. */ static int forever_multiple; -/* Array of file descriptors if forever_multiple is 1. */ -static int *file_descs; - -/* Array of file sizes if forever_multiple is 1. */ -static off_t *file_sizes; - /* If nonzero, count from start of file instead of end. */ static int from_start; @@ -111,6 +140,9 @@ enum header_mode multiple_files, always, never }; +/* FIXME: rename, document, and use this -- add option */ +static unsigned long max_no_change_count = 5; + int safe_read (); /* The name this program was run with. */ @@ -132,6 +164,8 @@ static struct option const long_options[] = { {"bytes", required_argument, NULL, 'c'}, {"follow", no_argument, NULL, 'f'}, + {"follow-descriptor", no_argument, NULL, 12}, + {"follow-name", no_argument, NULL, 13}, {"lines", required_argument, NULL, 'n'}, {"quiet", no_argument, NULL, 'q'}, {"silent", no_argument, NULL, 'q'}, @@ -566,15 +600,14 @@ output: return total; } -/* Tail NFILES (>1) files forever until killed. The file names are in - NAMES. The open file descriptors are in `file_descs', and the size - at which we stopped tailing them is in `file_sizes'. We loop over - each of them, doing an fstat to see if they have changed size. If - none of them have changed size in one iteration, we sleep for a - second and try again. We do this until the user interrupts us. */ +/* Tail NFILES (>1) files forever until killed. + The pertinent information for each file is stored in an entry of F. + Loop over each of them, doing an fstat to see if they have changed size. + If none of them have changed size in one iteration, sleep for a + while and try again. Continue until the user interrupts us. */ static void -tail_forever (char **names, int nfiles) +tail_forever (struct File_spec *f, int nfiles) { int last; @@ -590,38 +623,99 @@ tail_forever (char **names, int nfiles) { struct stat stats; - if (file_descs[i] < 0) + if (f[i].fd < 0) continue; - if (fstat (file_descs[i], &stats) < 0) + if (fstat (f[i].fd, &stats) < 0) { - error (0, errno, "%s", names[i]); - file_descs[i] = -1; + error (0, errno, "%s", f[i].pretty_name); + f[i].fd = -1; + continue; + } + + if (stats.st_size == f[i].size) + { + if (++f[i].no_change_counter > max_no_change_count + && follow_mode == follow_name) + { + /* open/fstat the file and announce if dev/ino + have changed */ + struct stat new_stats; + int fd = open (f[i].name, O_RDONLY); + int fail = 0; + + if (fd == -1 || fstat (fd, &new_stats) < 0) + { + fail = 1; + error (0, errno, "%s", f[i].pretty_name); + } + else if (!S_ISREG (new_stats.st_mode)) + { + fail = 1; + error (0, 0, + _("%s has been replaced with a non-regular file; \ +cannot follow end of non-regular file"), + f[i].pretty_name); + } + + if (fail) + { + if (f[i].fd != STDIN_FILENO) + close (f[i].fd); + f[i].fd = -1; + } + else if (f[i].ino != new_stats.st_ino + || f[i].dev != new_stats.st_dev) + { + /* Close the old one. */ + if (f[i].fd != STDIN_FILENO) + close (f[i].fd); + + /* File has been replaced (e.g., via log rotation) -- + tail the new one. */ + error (0, 0, + _("%s has been replaced; follow end of new file"), + f[i].pretty_name); + f[i].fd = fd; + f[i].size = new_stats.st_size; + f[i].dev = new_stats.st_dev; + f[i].ino = new_stats.st_ino; + f[i].no_change_counter = 0; + } + + /* NEW options: --follow-fd vs. --follow-file */ + /* File has been removed -- continue tailing?. */ + /* File has been replaced (e.g., log rotation) -- + continue tailing old or open the new one?. */ + + f[i].no_change_counter = 0; + }; continue; } - if (stats.st_size == file_sizes[i]) - continue; /* This file has changed size. Print out what we can, and then keep looping. */ changed = 1; - if (stats.st_size < file_sizes[i]) + /* reset counter */ + f[i].no_change_counter = 0; + + if (stats.st_size < f[i].size) { - write_header (names[i], _("file truncated")); + write_header (f[i].pretty_name, _("file truncated")); last = i; - lseek (file_descs[i], stats.st_size, SEEK_SET); - file_sizes[i] = stats.st_size; + lseek (f[i].fd, stats.st_size, SEEK_SET); + f[i].size = stats.st_size; continue; } if (i != last) { if (print_headers) - write_header (names[i], NULL); + write_header (f[i].pretty_name, NULL); last = i; } - file_sizes[i] += dump_remainder (names[i], file_descs[i]); + f[i].size += dump_remainder (f[i].pretty_name, f[i].fd); } /* If none of the files changed size, sleep. */ @@ -755,73 +849,75 @@ tail (const char *pretty_filename, int fd, off_t n_units) return tail_bytes (pretty_filename, fd, n_units); } -/* Display the last N_UNITS units of file FILENAME. - "-" for FILENAME means the standard input. - FILENUM is this file's index in the list of files the user gave. +/* Display the last N_UNITS units of the file described by F. Return 0 if successful, 1 if an error occurred. */ static int -tail_file (const char *filename, off_t n_units, int filenum) +tail_file (struct File_spec *f, off_t n_units) { int fd, errors; struct stat stats; - int is_stdin = (STREQ (filename, "-")); - char const *pretty_filename; + + int is_stdin = (STREQ (f->name, "-")); if (is_stdin) { have_read_stdin = 1; - pretty_filename = _("standard input"); + f->pretty_name = _("standard input"); fd = STDIN_FILENO; } else { - pretty_filename = filename; - fd = open (filename, O_RDONLY); + f->pretty_name = f->name; + fd = open (f->name, O_RDONLY); } if (fd == -1) { if (forever_multiple) - file_descs[filenum] = -1; - error (0, errno, "%s", pretty_filename); + f->fd = -1; + error (0, errno, "%s", f->pretty_name); errors = 1; } else { if (print_headers) - write_header (pretty_filename, NULL); - errors = tail (pretty_filename, fd, n_units); + write_header (f->pretty_name, NULL); + errors = tail (f->pretty_name, fd, n_units); if (forever_multiple) { + /* FIXME: duplicate code */ if (fstat (fd, &stats) < 0) { - error (0, errno, "%s", pretty_filename); + error (0, errno, "%s", f->pretty_name); errors = 1; } else if (!S_ISREG (stats.st_mode)) { error (0, 0, _("%s: cannot follow end of non-regular file"), - pretty_filename); + f->pretty_name); errors = 1; } if (errors) { if (!is_stdin) close (fd); - file_descs[filenum] = -1; + f->fd = -1; } else { - file_descs[filenum] = fd; - file_sizes[filenum] = stats.st_size; + f->fd = fd; + f->size = stats.st_size; + f->dev = stats.st_dev; + f->ino = stats.st_ino; + f->no_change_counter = 0; } } else { if (!is_stdin && close (fd)) { - error (0, errno, "%s", pretty_filename); + error (0, errno, "%s", f->pretty_name); errors = 1; } } @@ -1032,6 +1128,14 @@ parse_options (int argc, char **argv, forever = 1; break; + case 12: + follow_mode = follow_descriptor; + break; + + case 13: + follow_mode = follow_name; + break; + case 'q': *header_mode = never; break; @@ -1071,6 +1175,8 @@ main (int argc, char **argv) off_t n_units = DEFAULT_N_LINES; int n_files; char **file; + struct File_spec *F; + int i; program_name = argv[0]; setlocale (LC_ALL, ""); @@ -1122,27 +1228,28 @@ main (int argc, char **argv) { forever_multiple = 1; forever = 0; - file_descs = (int *) xmalloc (n_files * sizeof (int)); - file_sizes = (off_t *) xmalloc (n_files * sizeof (off_t)); } + if (n_files == 0) + { + static char *dummy_stdin = "-"; + n_files = 1; + file = &dummy_stdin; + } + + F = (struct File_spec *) xmalloc (n_files * sizeof (F[0])); + for (i = 0; i < n_files; i++) + F[i].name = file[i]; + if (header_mode == always || (header_mode == multiple_files && n_files > 1)) print_headers = 1; - if (n_files == 0) - { - exit_status |= tail_file ("-", n_units, 0); - } - else - { - int i; - for (i = 0; i < n_files; i++) - exit_status |= tail_file (file[i], n_units, i); + for (i = 0; i < n_files; i++) + exit_status |= tail_file (&F[i], n_units); - if (forever_multiple) - tail_forever (file, n_files); - } + if (forever_multiple) + tail_forever (F, n_files); if (have_read_stdin && close (0) < 0) error (EXIT_FAILURE, errno, "-"); |