=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tail/forward.c,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- src/usr.bin/tail/forward.c 2007/10/22 01:14:26 1.23 +++ src/usr.bin/tail/forward.c 2008/10/17 11:38:20 1.24 @@ -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 @@ -48,6 +48,7 @@ #include #include #include +#include #include "extern.h" @@ -79,9 +80,6 @@ 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 @@ 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; } /*