diff options
author | Jim Meyering <jim@meyering.net> | 1998-03-22 20:55:34 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1998-03-22 20:55:34 +0000 |
commit | eeef5c523d04483b534d3025dff735f11f4fbb9d (patch) | |
tree | 65147d9958264ff1573cce2b5d642bf803529c0f /src | |
parent | e8ab1c28f8bee2ef96efd5e26e20fdd5fdea653a (diff) | |
download | coreutils-eeef5c523d04483b534d3025dff735f11f4fbb9d.tar.xz |
merge variables: forever_multiple and forever
(recheck): New function.
Begin to support --allow-missing option.
Diffstat (limited to 'src')
-rw-r--r-- | src/tail.c | 244 |
1 files changed, 150 insertions, 94 deletions
diff --git a/src/tail.c b/src/tail.c index 3ed3c1350..b2885b9c9 100644 --- a/src/tail.c +++ b/src/tail.c @@ -75,6 +75,17 @@ } \ while (0) +#define CLOSE_FD(Fd, Name) \ + do \ + { \ + if ((Fd) != -1 && (Fd) != STDIN_FILENO && close (Fd)) \ + { \ + error (0, errno, "closing %s (fd=%d)", (Name), (Fd)); \ + } \ + } \ + while (0) + + /* Number of items to tail. */ #define DEFAULT_N_LINES 10 @@ -85,10 +96,10 @@ enum Follow_mode { - /* FIXME */ + /* FIXME: describe */ follow_name, - /* FIXME */ + /* FIXME: describe */ follow_descriptor, }; @@ -110,9 +121,16 @@ struct File_spec dev_t dev; ino_t ino; + /* FIXME: describe */ unsigned int no_change_counter; + + /* FIXME: describe */ + int missing; }; +/* FIXME: describe */ +static int allow_missing; + /* If nonzero, interpret the numeric argument as the number of lines. Otherwise, interpret it as the number of bytes. */ static int count_lines; @@ -122,12 +140,9 @@ static int count_lines; /* FIXME: make follow_name the default? */ static enum Follow_mode follow_mode; -/* If nonzero, read from the end of one file until killed. */ +/* If nonzero, read from the ends of all specified files until killed. */ static int forever; -/* If nonzero, read from the end of multiple files until killed. */ -static int forever_multiple; - /* If nonzero, count from start of file instead of end. */ static int from_start; @@ -162,6 +177,7 @@ static int show_version; static struct option const long_options[] = { + {"allow-missing", required_argument, NULL, 11}, {"bytes", required_argument, NULL, 'c'}, {"follow", no_argument, NULL, 'f'}, {"follow-descriptor", no_argument, NULL, 12}, @@ -252,6 +268,7 @@ file_lines (const char *pretty_filename, int fd, long int n_lines, off_t pos) /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all reads will be on block boundaries, which might increase efficiency. */ pos -= bytes_read; + /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); bytes_read = safe_read (fd, buffer, bytes_read); if (bytes_read == -1) @@ -283,10 +300,12 @@ file_lines (const char *pretty_filename, int fd, long int n_lines, off_t pos) if (pos == 0) { /* Not enough lines in the file; print the entire file. */ + /* FIXME: check lseek return value */ lseek (fd, (off_t) 0, SEEK_SET); return 0; } pos -= BUFSIZ; + /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); } while ((bytes_read = safe_read (fd, buffer, BUFSIZ)) > 0); @@ -566,8 +585,7 @@ start_lines (const char *pretty_filename, int fd, long int n_lines) } /* Display file FILENAME from the current position in FD to the end. - If `forever' is nonzero, keep reading from the end of the file - until killed. Return the number of bytes read from the file. */ + Return the number of bytes read from the file. */ static long dump_remainder (const char *pretty_filename, int fd) @@ -577,7 +595,6 @@ dump_remainder (const char *pretty_filename, int fd) long total; total = 0; -output: while ((bytes_read = safe_read (fd, buffer, BUFSIZ)) > 0) { XWRITE (STDOUT_FILENO, buffer, bytes_read); @@ -585,24 +602,94 @@ output: } if (bytes_read == -1) error (EXIT_FAILURE, errno, "%s", pretty_filename); + if (forever) + fflush (stdout); + + return total; +} + +/* FIXME: describe */ + +static void +recheck (struct File_spec *f) +{ + /* open/fstat the file and announce if dev/ino + have changed */ + struct stat new_stats; + int fd; + int fail = 0; + int is_stdin = (STREQ (f->name, "-")); + + fd = (is_stdin ? STDIN_FILENO : open (f->name, O_RDONLY)); + + /* If the open fails because the file doesn't exist, + then mark the file as missing. */ + f->missing = (allow_missing && fd == -1 && errno == ENOENT); + + if (fd == -1 || fstat (fd, &new_stats) < 0) { - fflush (stdout); - sleep (sleep_interval); - goto output; + fail = 1; + error (0, errno, "%s", f->pretty_name); } - else + else if (!S_ISREG (new_stats.st_mode) + && !S_ISFIFO (new_stats.st_mode)) { - if (forever_multiple) - fflush (stdout); + fail = 1; + error (0, 0, + _("%s has been replaced with a non-regular file; \ +cannot follow end of non-regular file"), + f->pretty_name); } - return total; + if (fail) + { + CLOSE_FD (fd, f->pretty_name); + CLOSE_FD (f->fd, f->pretty_name); + f->fd = -1; + } + else if (f->ino != new_stats.st_ino || f->dev != new_stats.st_dev) + { + /* Close the old one. */ + CLOSE_FD (f->fd, f->pretty_name); + + /* File has been replaced (e.g., via log rotation) -- + tail the new one. */ + error (0, 0, + _("%s has been replaced; following end of new file"), + f->pretty_name); + + f->fd = fd; + f->size = new_stats.st_size; + f->dev = new_stats.st_dev; + f->ino = new_stats.st_ino; + f->no_change_counter = 0; + /* FIXME: check lseek return value */ + lseek (f->fd, new_stats.st_size, SEEK_SET); + } + else if (f->missing) + { + error (0, 0, _("%s has reappeared"), f->pretty_name); + f->missing = 0; + + f->fd = fd; + f->size = new_stats.st_size; + f->dev = new_stats.st_dev; + f->ino = new_stats.st_ino; + f->no_change_counter = 0; + /* FIXME: check lseek return value */ + lseek (f->fd, new_stats.st_size, SEEK_SET); + } + else + { + CLOSE_FD (fd, f->pretty_name); + } } -/* Tail NFILES (>1) files forever until killed. +/* Tail NFILES files forever, or 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. + Loop over each of them, doing an fstat to see if they have changed size, + and an occasional open/fstat to see if any dev/ino pair has changed. If none of them have changed size in one iteration, sleep for a while and try again. Continue until the user interrupts us. */ @@ -616,15 +703,20 @@ tail_forever (struct File_spec *f, int nfiles) while (1) { int i; - int changed; + int any_changed; + int any_live_files; - changed = 0; + any_live_files = 0; + any_changed = 0; for (i = 0; i < nfiles; i++) { struct stat stats; - if (f[i].fd < 0) + if (f[i].fd < 0 && !f[i].missing) continue; + + any_live_files = 1; + if (fstat (f[i].fd, &stats) < 0) { error (0, errno, "%s", f[i].pretty_name); @@ -637,70 +729,21 @@ tail_forever (struct File_spec *f, int nfiles) 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; - int fail = 0; - int is_stdin = (STREQ (f[i].name, "-")); - - fd = (is_stdin ? STDIN_FILENO : open (f[i].name, O_RDONLY)); - - 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 (fd != STDIN_FILENO) - close (fd); - 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; following 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; - lseek (f[i].fd, new_stats.st_size, SEEK_SET); - } - else - { - close (fd); - } - + recheck (&f[i]); f[i].no_change_counter = 0; - }; + } + continue; } + /* FIXME-now: + Otherwise, a file that's unlinked or moved aside, yet always + growing will never be recognized has having been renamed. */ + /* This file has changed size. Print out what we can, and then keep looping. */ - changed = 1; + any_changed = 1; /* reset counter */ f[i].no_change_counter = 0; @@ -709,6 +752,7 @@ cannot follow end of non-regular file"), { write_header (f[i].pretty_name, _("file truncated")); last = i; + /* FIXME: check lseek return value */ lseek (f[i].fd, stats.st_size, SEEK_SET); f[i].size = stats.st_size; continue; @@ -723,8 +767,14 @@ cannot follow end of non-regular file"), f[i].size += dump_remainder (f[i].pretty_name, f[i].fd); } + if (!any_live_files /* FIXME-now: && ! allow_missing */ ) + { + error (0, 0, _("no files remaining")); + break; + } + /* If none of the files changed size, sleep. */ - if (! changed) + if (!any_changed) sleep (sleep_interval); } } @@ -750,9 +800,14 @@ tail_bytes (const char *pretty_filename, int fd, off_t n_bytes) if (from_start) { if (S_ISREG (stats.st_mode)) - lseek (fd, n_bytes, SEEK_CUR); + { + /* FIXME: check lseek return value */ + lseek (fd, n_bytes, SEEK_CUR); + } else if (start_bytes (pretty_filename, fd, n_bytes)) - return 1; + { + return 1; + } dump_remainder (pretty_filename, fd); } else @@ -782,12 +837,14 @@ tail_bytes (const char *pretty_filename, int fd, off_t n_bytes) more bytes than have been requested. So reposition the file pointer to the incoming current position and print everything after that. */ + /* FIXME: check lseek return value */ lseek (fd, current_pos, SEEK_SET); } else { /* There are more bytes remaining than were requested. Back up. */ + /* FIXME: check lseek return value */ lseek (fd, -n_bytes, SEEK_END); } dump_remainder (pretty_filename, fd); @@ -877,9 +934,11 @@ tail_file (struct File_spec *f, off_t n_units) fd = open (f->name, O_RDONLY); } + f->missing = (allow_missing && fd == -1 && errno == ENOENT); + if (fd == -1) { - if (forever_multiple) + if (forever) f->fd = -1; error (0, errno, "%s", f->pretty_name); errors = 1; @@ -889,7 +948,7 @@ tail_file (struct File_spec *f, off_t n_units) if (print_headers) write_header (f->pretty_name, NULL); errors = tail (f->pretty_name, fd, n_units); - if (forever_multiple) + if (forever) { /* FIXME: duplicate code */ if (fstat (fd, &stats) < 0) @@ -897,7 +956,7 @@ tail_file (struct File_spec *f, off_t n_units) error (0, errno, "%s", f->pretty_name); errors = 1; } - else if (!S_ISREG (stats.st_mode)) + else if (!S_ISREG (stats.st_mode) && !S_ISFIFO (stats.st_mode)) { error (0, 0, _("%s: cannot follow end of non-regular file"), f->pretty_name); @@ -905,8 +964,7 @@ tail_file (struct File_spec *f, off_t n_units) } if (errors) { - if (!is_stdin) - close (fd); + CLOSE_FD (fd, f->pretty_name); f->fd = -1; } else @@ -1090,7 +1148,7 @@ parse_options (int argc, char **argv, int c; count_lines = 1; - forever = forever_multiple = from_start = print_headers = 0; + forever = from_start = print_headers = 0; while ((c = getopt_long (argc, argv, "c:n:fqs:v", long_options, NULL)) != -1) { @@ -1133,6 +1191,10 @@ parse_options (int argc, char **argv, forever = 1; break; + case 11: + allow_missing = 1; + break; + case 12: follow_mode = follow_descriptor; break; @@ -1229,12 +1291,6 @@ main (int argc, char **argv) n_files = argc - optind; file = argv + optind; - if (n_files > 1 && forever) - { - forever_multiple = 1; - forever = 0; - } - if (n_files == 0) { static char *dummy_stdin = "-"; @@ -1253,7 +1309,7 @@ main (int argc, char **argv) for (i = 0; i < n_files; i++) exit_status |= tail_file (&F[i], n_units); - if (forever_multiple) + if (forever) tail_forever (F, n_files); if (have_read_stdin && close (0) < 0) |