=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tail/forward.c,v retrieving revision 1.24 retrieving revision 1.25 diff -u -r1.24 -r1.25 --- src/usr.bin/tail/forward.c 2008/10/17 11:38:20 1.24 +++ src/usr.bin/tail/forward.c 2008/11/13 18:33:03 1.25 @@ -1,4 +1,4 @@ -/* $OpenBSD: forward.c,v 1.24 2008/10/17 11:38:20 landry Exp $ */ +/* $OpenBSD: forward.c,v 1.25 2008/11/13 18:33:03 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.24 2008/10/17 11:38:20 landry Exp $"; +static char rcsid[] = "$OpenBSD: forward.c,v 1.25 2008/11/13 18:33:03 landry Exp $"; #endif /* not lint */ #include @@ -48,7 +48,6 @@ #include #include #include -#include #include "extern.h" @@ -80,6 +79,9 @@ 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: @@ -158,188 +160,83 @@ 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; - 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); +kq_retry: + if (fflag && ((kq = kqueue()) >= 0)) { 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)) { - /* one event to detect if inode changed */ + 0, + 0, NULL); + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { + close(kq); + kq = -1; + } else if (S_ISREG(sbp->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; + if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { + close(kq); + kq = -1; + } } } - /* 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)) - goto cleanup; - + if (ferror(fp)) { + ierr(); + if (kq != -1) + close(kq); + return; + } (void)fflush(stdout); + if (!fflag) + break; clearerr(fp); - /* give it a chance to fail.. */ - if (kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { + queue = 1; + if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { + queue = 0; sleep(1); + } else if (ke.filter == EVFILT_READ) { continue; - } else { - /* an event occured on file #i */ - for (i = 0 ; i < nbfiles ; i++) - if (fileno(files[i].fp) == ke.ident) - break; + } 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; + } - /* 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; + 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; } + (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; } /*