diff options
author | Landry Breuil <landry@cvs.openbsd.org> | 2008-10-17 11:38:21 +0000 |
---|---|---|
committer | Landry Breuil <landry@cvs.openbsd.org> | 2008-10-17 11:38:21 +0000 |
commit | 9b4733da35992ec07dab56f95becc8bdcf1bc474 (patch) | |
tree | 8d89f401bf46f39ab7cdf5b2ce75586b9b6e738a /usr.bin | |
parent | 4fb9aef39732f61018ea5bc215cc514328861680 (diff) |
Permit tail -f to follow multiple files, useful when you want to monitor
several logfiles in a single terminal. split forward() and move the
corresponding -rewritten- code to follow().
Closes PR 5092.
ok millert@ gilles@ sobrado@
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/tail/extern.h | 3 | ||||
-rw-r--r-- | usr.bin/tail/forward.c | 219 | ||||
-rw-r--r-- | usr.bin/tail/tail.c | 50 |
3 files changed, 189 insertions, 83 deletions
diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h index 5ab91243f51..1cda5b85cfd 100644 --- a/usr.bin/tail/extern.h +++ b/usr.bin/tail/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.9 2004/02/16 19:48:21 otto Exp $ */ +/* $OpenBSD: extern.h,v 1.10 2008/10/17 11:38:20 landry Exp $ */ /* $NetBSD: extern.h,v 1.3 1994/11/23 07:42:00 jtc Exp $ */ /*- @@ -40,6 +40,7 @@ enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE }; void forward(FILE *, enum STYLE, off_t, struct stat *); void reverse(FILE *, enum STYLE, off_t, struct stat *); +void follow(char **, int, enum STYLE, off_t); int bytes(FILE *, off_t); int lines(FILE *, off_t); diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c index 40e5f324058..e36498d9285 100644 --- a/usr.bin/tail/forward.c +++ b/usr.bin/tail/forward.c @@ -1,4 +1,4 @@ -/* $OpenBSD: forward.c,v 1.23 2007/10/22 01:14:26 deraadt Exp $ */ +/* $OpenBSD: forward.c,v 1.24 2008/10/17 11:38:20 landry Exp $ */ /* $NetBSD: forward.c,v 1.7 1996/02/13 16:49:10 ghudson Exp $ */ /*- @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; #endif -static char rcsid[] = "$OpenBSD: forward.c,v 1.23 2007/10/22 01:14:26 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: forward.c,v 1.24 2008/10/17 11:38:20 landry Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -48,6 +48,7 @@ static char rcsid[] = "$OpenBSD: forward.c,v 1.23 2007/10/22 01:14:26 deraadt Ex #include <stdio.h> #include <string.h> #include <unistd.h> +#include <stdlib.h> #include "extern.h" @@ -79,9 +80,6 @@ void forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) { int ch; - struct stat nsb; - int kq, queue; - struct kevent ke; switch(style) { case FBYTES: @@ -160,83 +158,188 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) break; } + while (!feof(fp) && (ch = getc(fp)) != EOF) + if (putchar(ch) == EOF) + oerr(); + if (ferror(fp)) { + ierr(); + return; + } + (void)fflush(stdout); +} + +struct file_info { + FILE *fp; + char *fname; + struct stat fst; +}; + +/* + * follow one or multiple files, i.e don't stop when end-of-file is reached, + * but rather wait for additional data to be appended to the input. + * this implements -f switch. + */ +void +follow(char **fnames, int nbfiles, enum STYLE style, off_t off) +{ + int ch, first, i; + int kq; + struct kevent ke; + struct stat cst; + FILE *fp; + struct file_info *files; + kq = -1; -kq_retry: - if (fflag && ((kq = kqueue()) >= 0)) { + if ((kq = kqueue()) < 0) + err(2, "kqueue() failed"); + + if ((files = calloc(nbfiles, sizeof(struct file_info))) == NULL) + err(1, "calloc() failed"); + + for (first = 1, i = 0; (files[i].fname = *fnames++); i++) { + if ((fp = fopen(files[i].fname, "r")) == NULL || + fstat(fileno(fp), &(files[i].fst))) { + warn("%s",files[i].fname); + nbfiles--; + i--; + continue; + } + if (S_ISDIR(files[i].fst.st_mode)) { + warnx("%s is a directory, skipping.",files[i].fname); + nbfiles--; + i--; + continue; + } + files[i].fp = fp; + if (nbfiles > 1) { + (void)printf("%s==> %s <==\n", + first ? "" : "\n", files[i].fname); + first = 0; + } + + /* print from the given offset to the end */ + if (off != 0) { + if (style == RBYTES) { + if (S_ISREG(files[i].fst.st_mode)) { + if (files[i].fst.st_size >= off && + fseeko(fp, -off, SEEK_END) == -1) { + ierr(); + goto cleanup; + } + } + if (bytes(fp, off)) + goto cleanup; + } else if (rlines(fp, off, &(files[i].fst)) != 0) + lines(fp, off); + } + (void)fflush(stdout); + + /* one event to see if there is data to read */ + ke.ident = fileno(fp); EV_SET(&ke, fileno(fp), EVFILT_READ, EV_ENABLE | EV_ADD | EV_CLEAR, - 0, - 0, NULL); - if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { - close(kq); - kq = -1; - } else if (S_ISREG(sbp->st_mode)) { + NULL, 0, NULL); + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) + goto cleanup; + else if (S_ISREG(files[i].fst.st_mode)) { + /* one event to detect if inode changed */ EV_SET(&ke, fileno(fp), EVFILT_VNODE, EV_ENABLE | EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE, 0, NULL); - if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { - close(kq); - kq = -1; - } + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) + goto cleanup; } } + /* no files to read */ + if (nbfiles == 0) + goto cleanup; + + if (fp == NULL) + fp = files[nbfiles - 1].fp; + for (;;) { while (!feof(fp) && (ch = getc(fp)) != EOF) if (putchar(ch) == EOF) oerr(); - if (ferror(fp)) { - ierr(); - if (kq != -1) - close(kq); - return; - } + if (ferror(fp)) + goto cleanup; + (void)fflush(stdout); - if (!fflag) - break; clearerr(fp); - queue = 1; - if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { - queue = 0; + /* give it a chance to fail.. */ + if (kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { sleep(1); - } else if (ke.filter == EVFILT_READ) { continue; - } else if ((ke.fflags & NOTE_TRUNCATE) == 0) { - /* - * File was renamed or deleted. - * - * Continue to look at it until a new file reappears - * with the same name. - * Fall back to the old algorithm for that. - */ - close(kq); - kq = -1; - } + } else { + /* an event occured on file #i */ + for (i = 0 ; i < nbfiles ; i++) + if (fileno(files[i].fp) == ke.ident) + break; - if (is_stdin || stat(fname, &nsb) != 0) - continue; - /* Reopen file if the inode changes or file was truncated */ - if (nsb.st_ino != sbp->st_ino) { - warnx("%s has been replaced, reopening.", fname); - if ((fp = freopen(fname, "r", fp)) == NULL) { - ierr(); - if (kq >= 0) - close(kq); - return; + /* EVFILT_READ event, check that it's on the current fp */ + if (ke.filter == EVFILT_READ) { + if (fp != files[i].fp) { + (void)printf("\n==> %s <==\n",files[i].fname); + fp = files[i].fp; + clearerr(fp); + } + /* EVFILT_VNODE event and File was renamed or deleted */ + } else if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) { + /* file didn't reappear */ + if (stat(files[i].fname, &cst) != 0) { + warnx("%s has been renamed or deleted.", files[i].fname); + if (--nbfiles == 0) + goto cleanup; + /* overwrite with the latest file_info */ + fp = files[nbfiles].fp; + (void)memcpy(&files[i], &files[nbfiles], sizeof(struct file_info)); + } else { + /* Reopen file if the inode changed */ + if (cst.st_ino != files[i].fst.st_ino) { + warnx("%s has been replaced, reopening.", files[i].fname); + if ((fp = freopen(files[i].fname, "r", files[i].fp)) == NULL) { + ierr(); + goto cleanup; + } + /* + * on freopen(), events corresponding to the fp + * were deleted from kqueue, we readd them + */ + ke.ident = fileno(fp); + EV_SET(&ke, fileno(fp), EVFILT_READ, + EV_ENABLE | EV_ADD | EV_CLEAR, + NULL, 0, NULL); + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) + goto cleanup; + else if (S_ISREG(files[i].fst.st_mode)) { + EV_SET(&ke, fileno(fp), EVFILT_VNODE, + EV_ENABLE | EV_ADD | EV_CLEAR, + NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE, + 0, NULL); + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) + goto cleanup; + } + files[i].fp = fp; + } + (void)memcpy(&(files[i].fst), &cst, sizeof(cst)); + } + } else if (ke.fflags & NOTE_TRUNCATE) { + /* reset file if it was truncated */ + warnx("%s has been truncated, resetting.", files[i].fname); + fpurge(files[i].fp); + rewind(files[i].fp); + continue; } - (void)memcpy(sbp, &nsb, sizeof(nsb)); - goto kq_retry; - } else if ((queue && (ke.fflags & NOTE_TRUNCATE)) || - (!queue && nsb.st_size < sbp->st_size)) { - warnx("%s has been truncated, resetting.", fname); - fpurge(fp); - rewind(fp); } - (void)memcpy(sbp, &nsb, sizeof(nsb)); } + +cleanup: if (kq >= 0) close(kq); + free(files); + return; } /* diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c index 2246e7b77db..10d164290cd 100644 --- a/usr.bin/tail/tail.c +++ b/usr.bin/tail/tail.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tail.c,v 1.14 2007/10/31 16:29:50 jmc Exp $ */ +/* $OpenBSD: tail.c,v 1.15 2008/10/17 11:38:20 landry Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -42,7 +42,7 @@ static char copyright[] = #if 0 static char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; #endif -static char rcsid[] = "$OpenBSD: tail.c,v 1.14 2007/10/31 16:29:50 jmc Exp $"; +static char rcsid[] = "$OpenBSD: tail.c,v 1.15 2008/10/17 11:38:20 landry Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -132,10 +132,6 @@ main(int argc, char *argv[]) } argc -= optind; argv += optind; - - if (fflag && argc > 1) - errx(1, "-f option only appropriate for a single file"); - /* * If displaying in reverse, don't permit follow option, and convert * style values. @@ -162,27 +158,33 @@ main(int argc, char *argv[]) style = RLINES; } } + + if (*argv) { + if (fflag) { + follow(argv, argc, style, off); + } + else { + for (first = 1; (fname = *argv++);) { + if ((fp = fopen(fname, "r")) == NULL || + fstat(fileno(fp), &sb)) { + ierr(); + continue; + } + if (argc > 1) { + (void)printf("%s==> %s <==\n", + first ? "" : "\n", fname); + first = 0; + (void)fflush(stdout); + } - if (*argv) - for (first = 1; (fname = *argv++);) { - if ((fp = fopen(fname, "r")) == NULL || - fstat(fileno(fp), &sb)) { - ierr(); - continue; + if (rflag) + reverse(fp, style, off, &sb); + else + forward(fp, style, off, &sb); + (void)fclose(fp); } - if (argc > 1) { - (void)printf("%s==> %s <==\n", - first ? "" : "\n", fname); - first = 0; - (void)fflush(stdout); - } - - if (rflag) - reverse(fp, style, off, &sb); - else - forward(fp, style, off, &sb); - (void)fclose(fp); } + } else { fname = "stdin"; is_stdin = 1; |