diff options
author | paul <paul@FreeBSD.org> | 2004-11-04 19:18:19 +0000 |
---|---|---|
committer | paul <paul@FreeBSD.org> | 2004-11-04 19:18:19 +0000 |
commit | 4b813e782f86ea86dc503cbcdf096391e47023ed (patch) | |
tree | 7f9ec10647bbe930e464447c09ec4505e398b99c /usr.bin | |
parent | 174862f7bbe6e86b700b32335962130383967180 (diff) | |
download | FreeBSD-src-4b813e782f86ea86dc503cbcdf096391e47023ed.zip FreeBSD-src-4b813e782f86ea86dc503cbcdf096391e47023ed.tar.gz |
Add support for following more than one file i.e.
tail -f file1 file2
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/tail/extern.h | 11 | ||||
-rw-r--r-- | usr.bin/tail/forward.c | 248 | ||||
-rw-r--r-- | usr.bin/tail/tail.c | 37 |
3 files changed, 200 insertions, 96 deletions
diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h index 8ad21ec..9c45a1b 100644 --- a/usr.bin/tail/extern.h +++ b/usr.bin/tail/extern.h @@ -50,8 +50,17 @@ struct mapinfo { int fd; }; +struct file_info { + FILE *fp; + char *file_name; + struct stat st; +}; + +typedef struct file_info file_info_t; + enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE }; +void follow(file_info_t *, enum STYLE, off_t); void forward(FILE *, enum STYLE, off_t, struct stat *); void reverse(FILE *, enum STYLE, off_t, struct stat *); @@ -63,5 +72,5 @@ void oerr(void); int mapprint(struct mapinfo *, off_t, off_t); int maparound(struct mapinfo *, off_t); -extern int Fflag, fflag, rflag, rval; +extern int Fflag, fflag, rflag, rval, no_files; extern const char *fname; diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c index 6f9256f..29f486d 100644 --- a/usr.bin/tail/forward.c +++ b/usr.bin/tail/forward.c @@ -66,6 +66,10 @@ static void rlines(FILE *, off_t, struct stat *); #define USE_KQUEUE 1 #define ADD_EVENTS 2 +struct kevent *ev; +int action = USE_SLEEP; +int kq; + /* * forward -- display the file, from an offset, forward. * @@ -172,95 +176,14 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) break; } - if (fflag) { - kq = kqueue(); - if (kq < 0) - err(1, "kqueue"); - action = ADD_EVENTS; - } - - for (;;) { - while ((ch = getc(fp)) != EOF) - if (putchar(ch) == EOF) - oerr(); - if (ferror(fp)) { - ierr(); - return; - } - (void)fflush(stdout); - if (! fflag) - break; - clearerr(fp); - - switch (action) { - case ADD_EVENTS: - n = 0; - ts.tv_sec = 0; - ts.tv_nsec = 0; - - if (Fflag && fileno(fp) != STDIN_FILENO) { - EV_SET(&ev[n], fileno(fp), EVFILT_VNODE, - EV_ADD | EV_ENABLE | EV_CLEAR, - NOTE_DELETE | NOTE_RENAME, 0, 0); - n++; - } - EV_SET(&ev[n], fileno(fp), EVFILT_READ, - EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); - n++; - - if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { - action = USE_SLEEP; - } else { - action = USE_KQUEUE; - } - break; - - case USE_KQUEUE: - ts.tv_sec = 1; - ts.tv_nsec = 0; - /* - * In the -F case we set a timeout to ensure that - * we re-stat the file at least once every second. - */ - n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); - if (n < 0) - err(1, "kevent"); - if (n == 0) { - /* timeout */ - break; - } else if (ev->filter == EVFILT_READ && ev->data < 0) { - /* file shrank, reposition to end */ - if (fseeko(fp, (off_t)0, SEEK_END) == -1) { - ierr(); - return; - } - } - break; - - case USE_SLEEP: - (void) usleep(250000); - clearerr(fp); - break; - } - - if (Fflag && fileno(fp) != STDIN_FILENO) { - while (stat(fname, &sb2) != 0) - /* file was rotated, wait until it reappears */ - (void)sleep(1); - if (sb2.st_ino != sbp->st_ino || - sb2.st_dev != sbp->st_dev || - sb2.st_nlink == 0) { - fp = freopen(fname, "r", fp); - if (fp == NULL) { - ierr(); - return; - } else { - *sbp = sb2; - action = ADD_EVENTS; - } - } - } + while ((ch = getc(fp)) != EOF) + if (putchar(ch) == EOF) + oerr(); + if (ferror(fp)) { + ierr(); + return; } + (void)fflush(stdout); } /* @@ -316,3 +239,152 @@ rlines(fp, off, sbp) return; } } + +/* + * follow -- display the file, from an offset, forward. + * + */ + +void +show(file_info_t *file) +{ + int ch, first; + + first = 1; + while ((ch = getc(file->fp)) != EOF) { + if (first && no_files > 1) { + (void)printf("\n==> %s <==\n", file->file_name); + first = 0; + } + if (putchar(ch) == EOF) + oerr(); + } + (void)fflush(stdout); + if (ferror(file->fp)) { + file->fp = NULL; + ierr(); + } else + clearerr(file->fp); +} + +void +set_events(file_info_t *files) +{ + int i, n = 0; + file_info_t *file; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + action = USE_KQUEUE; + for (i = 0, file = files; i < no_files; i++, file++) { + if (! file->fp) + continue; + if (Fflag && fileno(file->fp) != STDIN_FILENO) { + EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_CLEAR, + NOTE_DELETE | NOTE_RENAME, 0, 0); + n++; + } + EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, + EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); + n++; + } + + if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { + action = USE_SLEEP; + } +} + +void +follow(file_info_t *files, enum STYLE style, off_t off) +{ + int active, i, n = -1; + struct stat sb2; + struct stat *sbp; + file_info_t *file; + long spin=1; + struct timespec ts; + + /* Position each of the files */ + + file = files; + active = 0; + n = 0; + for (i = 0; i < no_files; i++, file++) { + if (file->fp) { + active = 1; + n++; + if (no_files > 1) + (void)printf("\n==> %s <==\n", file->file_name); + forward(file->fp, style, off, &file->st); + if (Fflag && fileno(file->fp) != STDIN_FILENO) + n++; + } + } + if (! active) + return; + + kq = kqueue(); + if (kq < 0) + err(1, "kqueue"); + ev = malloc(n * sizeof(struct kevent)); + if (! ev) + err(1, "Couldn't allocate memory for kevents."); + set_events(files); + + for (;;) { + for (i = 0, file = files; i < no_files; i++, file++) { + if (! file->fp) + continue; + if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) { + if (stat(file->file_name, &sb2) != 0) { + /* file was rotated, skip it until it reappears */ + continue; + } + if (sb2.st_ino != file->st.st_ino || + sb2.st_dev != file->st.st_dev || + sb2.st_nlink == 0) { + file->fp = freopen(file->file_name, "r", file->fp); + if (file->fp == NULL) { + ierr(); + continue; + } else { + memcpy(&file->st, &sb2, sizeof(struct stat)); + set_events(files); + } + } + } + show(file); + } + + switch (action) { + case USE_KQUEUE: + ts.tv_sec = 1; + ts.tv_nsec = 0; + /* + * In the -F case we set a timeout to ensure that + * we re-stat the file at least once every second. + */ + n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); + if (n < 0) + err(1, "kevent"); + if (n == 0) { + /* timeout */ + break; + } else if (ev->filter == EVFILT_READ && ev->data < 0) { + /* file shrank, reposition to end */ + if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { + ierr(); + continue; + } + } + break; + + case USE_SLEEP: + (void) usleep(250000); + break; + } + } +} diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c index 2a3ee13..9162d17 100644 --- a/usr.bin/tail/tail.c +++ b/usr.bin/tail/tail.c @@ -60,9 +60,11 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; #include "extern.h" -int Fflag, fflag, rflag, rval; +int Fflag, fflag, rflag, rval, no_files; const char *fname; +file_info_t *files; + static void obsolete(char **); static void usage(void); @@ -73,7 +75,8 @@ main(int argc, char *argv[]) FILE *fp; off_t off; enum STYLE style; - int ch, first; + int i, ch, first; + file_info_t *file; char *p; /* @@ -138,8 +141,7 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - if (fflag && argc > 1) - errx(1, "-f option only appropriate for a single file"); + no_files = argc ? argc : 1; /* * If displaying in reverse, don't permit follow option, and convert @@ -168,7 +170,29 @@ main(int argc, char *argv[]) } } - if (*argv) + if (*argv && fflag) { + files = (struct file_info *) malloc(no_files * sizeof(struct file_info)); + if (! files) + err(1, "Couldn't malloc space for file descriptors."); + + for (file = files; (fname = *argv++); file++) { + file->file_name = malloc(strlen(fname)+1); + if (! file->file_name) + errx(1, "Couldn't malloc space for file name."); + strncpy(file->file_name, fname, strlen(fname)+1); + if ((file->fp = fopen(file->file_name, "r")) == NULL || + fstat(fileno(file->fp), &file->st)) { + file->fp = NULL; + ierr(); + continue; + } + } + follow(files, style, off); + for (i = 0, file = files; i < no_files; i++, file++) { + free(file->file_name); + } + free(files); + } else if (*argv) { for (first = 1; (fname = *argv++);) { if ((fp = fopen(fname, "r")) == NULL || fstat(fileno(fp), &sb)) { @@ -186,9 +210,8 @@ main(int argc, char *argv[]) reverse(fp, style, off, &sb); else forward(fp, style, off, &sb); - (void)fclose(fp); } - else { + } else { fname = "stdin"; if (fstat(fileno(stdin), &sb)) { |