Add support for tail -f'ing multiple files. part 1/2.
authorMatthew Dillon <dillon@dragonflybsd.org>
Mon, 27 Dec 2004 20:55:07 +0000 (20:55 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Mon, 27 Dec 2004 20:55:07 +0000 (20:55 +0000)
Submitted-by: "Andre Nathan" <andre@digirati.com.br>
Taken-from: FreeBSD

usr.bin/tail/extern.h
usr.bin/tail/forward.c
usr.bin/tail/tail.c

index 797d549..ab90648 100644 (file)
@@ -33,7 +33,7 @@
  *     @(#)extern.h    8.1 (Berkeley) 6/6/93
  *
  * $FreeBSD: src/usr.bin/tail/extern.h,v 1.4.6.2 2001/12/19 20:29:29 iedowse Exp $
- * $DragonFly: src/usr.bin/tail/extern.h,v 1.3 2003/11/03 19:31:33 eirikn Exp $
+ * $DragonFly: src/usr.bin/tail/extern.h,v 1.4 2004/12/27 20:55:07 dillon Exp $
  */
 
 #define        WR(p, size) do { \
@@ -51,8 +51,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 *);
 
@@ -64,5 +73,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 char *fname;
index 6e2bb5f..f2da041 100644 (file)
@@ -35,7 +35,7 @@
  *
  * @(#)forward.c       8.1 (Berkeley) 6/6/93
  * $FreeBSD: src/usr.bin/tail/forward.c,v 1.11.6.7 2003/01/07 05:26:22 tjr Exp $
- * $DragonFly: src/usr.bin/tail/forward.c,v 1.3 2003/10/04 20:36:51 hmp Exp $
+ * $DragonFly: src/usr.bin/tail/forward.c,v 1.4 2004/12/27 20:55:07 dillon Exp $
  */
 
 #include <sys/types.h>
@@ -61,6 +61,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.
  *
@@ -86,11 +90,7 @@ static void rlines(FILE *, off_t, struct stat *);
 void
 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
 {
-       int ch, n, kq = -1;
-       int action = USE_SLEEP;
-       struct kevent ev[2];
-       struct stat sb2;
-       struct timespec ts;
+       int ch;
 
        switch(style) {
        case FBYTES:
@@ -165,96 +165,15 @@ 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;
+       while ((ch = getc(fp)) != EOF) {
+               if (putchar(ch) == EOF)
+                       oerr();
        }
-
-       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_rdev != sbp->st_rdev ||
-                           sb2.st_nlink == 0) {
-                               fp = freopen(fname, "r", fp);
-                               if (fp == NULL) {
-                                       ierr();
-                                       return;
-                               } else {
-                                       *sbp = sb2;
-                                       action = ADD_EVENTS;
-                               }
-                       }
-               }
+       if (ferror(fp)) {
+               ierr();
+               return;
        }
+       fflush(stdout);
 }
 
 /*
@@ -307,3 +226,158 @@ rlines(FILE *fp, off_t off, struct stat *sbp)
                return;
        }
 }
+
+/*
+ * follow -- display the file, from an offset, forward.
+ */
+
+static void
+show(file_info_t *file)
+{
+       int ch, first;
+
+       first = 1;
+       while ((ch = getc(file->fp)) != EOF) {
+               if (first && no_files > 1) {
+                       printf("\n==> %s <==\n", file->file_name);
+                       first = 0;
+               }
+               if (putchar(ch) == EOF)
+                       oerr();
+       }
+       fflush(stdout);
+       if (ferror(file->fp)) {
+               file->fp = NULL;
+               ierr();
+       } else {
+               clearerr(file->fp);
+       }
+}
+
+static void
+set_events(file_info_t *files)
+{
+       int i, n;
+       file_info_t *file;
+       struct timespec ts;
+
+       ts.tv_sec = 0;
+       ts.tv_nsec = 0;
+
+       n = 0;
+       action = USE_KQUEUE;
+       for (i = 0, file = files; i < no_files; i++, file++) {
+               if (file->fp == NULL)
+                       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;
+       long spin;
+       file_info_t *file;
+       struct stat sb2;
+       struct timespec ts;
+
+       spin = 1;
+
+       /* 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)
+                               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 == -1)
+               err(1, "kqueue");
+       ev = malloc(n * sizeof(struct kevent));
+       if (ev == NULL)
+               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 == NULL)
+                               continue;
+                       if (Fflag && fileno(file->fp) != STDIN_FILENO) {
+                               if (stat(file->file_name, &sb2) == -1) {
+                                       /*
+                                        * 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 == -1)
+                               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, 0, SEEK_END) == -1) {
+                                       ierr();
+                                       continue;
+                               }
+                       }
+                       break;
+
+               case USE_SLEEP:
+                       usleep(250000);
+                       break;
+               }
+       }
+}
index 2a5dcb3..2ac0e57 100644 (file)
@@ -36,7 +36,7 @@
  * @(#) Copyright (c) 1991, 1993 The Regents of the University of California.  All rights reserved.
  * @(#)tail.c  8.1 (Berkeley) 6/6/93
  * $FreeBSD: src/usr.bin/tail/tail.c,v 1.6.2.2 2001/12/19 20:29:31 iedowse Exp $
- * $DragonFly: src/usr.bin/tail/tail.c,v 1.3 2003/10/04 20:36:52 hmp Exp $
+ * $DragonFly: src/usr.bin/tail/tail.c,v 1.4 2004/12/27 20:55:07 dillon Exp $
  */
 
 #include <sys/types.h>
 #include <err.h>
 #include "extern.h"
 
-int Fflag, fflag, rflag, rval;
+int Fflag, fflag, rflag, rval, no_files;
 char *fname;
 
+file_info_t *files;
+
 static void obsolete(char **);
 static void usage(void);
 
@@ -62,7 +64,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;
 
        /*
@@ -127,8 +130,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
@@ -157,8 +159,29 @@ main(int argc, char **argv)
                }
        }
 
-       if (*argv)
-               for (first = 1; fname = *argv++;) {
+       if (*argv && fflag) {
+               files = malloc(no_files * sizeof(struct file_info));
+               if (files == NULL)
+                       err(1, "Couldn't malloc space for files descriptors.");
+
+               for (file = files; (fname = *argv++) != NULL; file++) {
+                       file->file_name = strdup(fname);
+                       if (! file->file_name)
+                               errx(1, "Couldn't alloc space for file name.");
+                       file->fp = fopen(file->file_name, "r");
+                       if (file->fp == NULL ||
+                           fstat(fileno(file->fp), &file->st) == -1) {
+                               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++) != NULL;) {
                        if ((fp = fopen(fname, "r")) == NULL ||
                            fstat(fileno(fp), &sb)) {
                                ierr();
@@ -177,7 +200,7 @@ main(int argc, char **argv)
                                forward(fp, style, off, &sb);
                        (void)fclose(fp);
                }
-       else {
+       else {
                fname = "stdin";
 
                if (fstat(fileno(stdin), &sb)) {