[BACK]Return to forward.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tail

Diff for /src/usr.bin/tail/forward.c between version 1.26 and 1.27

version 1.26, 2009/10/27 23:59:44 version 1.27, 2015/11/19 17:50:04
Line 38 
Line 38 
 #include <sys/event.h>  #include <sys/event.h>
   
 #include <err.h>  #include <err.h>
   #include <errno.h>
 #include <stdio.h>  #include <stdio.h>
   #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
   
 #include "extern.h"  #include "extern.h"
   
 static int rlines(FILE *, off_t, struct stat *);  static int rlines(struct tailfile *, off_t);
   static int tfqueue(struct tailfile *tf);
   static const struct timespec *tfreopen(struct tailfile *tf);
   
   static int kq = -1;
   
 /*  /*
  * forward -- display the file, from an offset, forward.   * forward -- display the file, from an offset, forward.
  *   *
Line 69 
Line 75 
  *      NOREG   cyclically read lines into a wrap-around array of buffers   *      NOREG   cyclically read lines into a wrap-around array of buffers
  */   */
 void  void
 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)  forward(struct tailfile *tf, int nfiles, enum STYLE style, off_t off)
 {  {
         int ch;          int ch;
         struct stat nsb;          struct tailfile *ctf, *ltf;
         int kq, queue;  
         struct kevent ke;          struct kevent ke;
           const struct timespec *ts = NULL;
           int i;
           int nevents;
   
         switch(style) {          if (nfiles < 1)
         case FBYTES:                  return;
                 if (off == 0)  
                         break;          if ((kq = kqueue()) < 0)
                 if (S_ISREG(sbp->st_mode)) {                  warn("kqueue");
                         if (sbp->st_size < off)  
                                 off = sbp->st_size;          for (i = 0; i < nfiles; i++) {
                         if (fseeko(fp, off, SEEK_SET) == -1) {                  if (nfiles > 1)
                                 ierr();                          printfname(tf[i].fname);
                                 return;  
                         }                  switch(style) {
                 } else while (off--)                  case FBYTES:
                         if ((ch = getc(fp)) == EOF) {                          if (off == 0)
                                 if (ferror(fp)) {                                  break;
                                         ierr();                          if (S_ISREG(tf[i].sb.st_mode)) {
                                   if (tf[i].sb.st_size < off)
                                           off = tf[i].sb.st_size;
                                   if (fseeko(tf[i].fp, off, SEEK_SET) == -1) {
                                           ierr(tf[i].fname);
                                         return;                                          return;
                                 }                                  }
                           } else while (off--)
                                   if ((ch = getc(tf[i].fp)) == EOF) {
                                           if (ferror(tf[i].fp)) {
                                                   ierr(tf[i].fname);
                                                   return;
                                           }
                                           break;
                                   }
                           break;
                   case FLINES:
                           if (off == 0)
                                 break;                                  break;
                           for (;;) {
                                   if ((ch = getc(tf[i].fp)) == EOF) {
                                           if (ferror(tf[i].fp)) {
                                                   ierr(tf[i].fname);
                                                   return;
                                           }
                                           break;
                                   }
                                   if (ch == '\n' && !--off)
                                           break;
                         }                          }
                 break;  
         case FLINES:  
                 if (off == 0)  
                         break;                          break;
                 for (;;) {                  case RBYTES:
                         if ((ch = getc(fp)) == EOF) {                          if (S_ISREG(tf[i].sb.st_mode)) {
                                 if (ferror(fp)) {                                  if (tf[i].sb.st_size >= off &&
                                         ierr();                                      fseeko(tf[i].fp, -off, SEEK_END) == -1) {
                                           ierr(tf[i].fname);
                                         return;                                          return;
                                 }                                  }
                                 break;                          } else if (off == 0) {
                                   while (getc(tf[i].fp) != EOF)
                                           ;
                                   if (ferror(tf[i].fp)) {
                                           ierr(tf[i].fname);
                                           return;
                                   }
                           } else {
                                   if (bytes(&(tf[i]), off))
                                           return;
                         }                          }
                         if (ch == '\n' && !--off)                          break;
                                 break;                  case RLINES:
                 }                          if (S_ISREG(tf[i].sb.st_mode)) {
                 break;                                  if (!off) {
         case RBYTES:                                          if (fseeko(tf[i].fp, (off_t)0,
                 if (S_ISREG(sbp->st_mode)) {                                              SEEK_END) == -1) {
                         if (sbp->st_size >= off &&                                                  ierr(tf[i].fname);
                             fseeko(fp, -off, SEEK_END) == -1) {                                                  return;
                                 ierr();                                          }
                                 return;                                  } else if (rlines(&(tf[i]), off) != 0)
                         }                                          lines(&(tf[i]), off);
                 } else if (off == 0) {                          } else if (off == 0) {
                         while (getc(fp) != EOF)                                  while (getc(tf[i].fp) != EOF)
                                 ;                                          ;
                         if (ferror(fp)) {                                  if (ferror(tf[i].fp)) {
                                 ierr();                                          ierr(tf[i].fname);
                                 return;  
                         }  
                 } else {  
                         if (bytes(fp, off))  
                                 return;  
                 }  
                 break;  
         case RLINES:  
                 if (S_ISREG(sbp->st_mode)) {  
                         if (!off) {  
                                 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {  
                                         ierr();  
                                         return;                                          return;
                                 }                                  }
                         } else if (rlines(fp, off, sbp) != 0)                          } else {
                                 lines(fp, off);                                  if (lines(&(tf[i]), off))
                 } else if (off == 0) {                                          return;
                         while (getc(fp) != EOF)  
                                 ;  
                         if (ferror(fp)) {  
                                 ierr();  
                                 return;  
                         }                          }
                 } else {                          break;
                         if (lines(fp, off))                  default:
                                 return;                          err(1, "Unsupported style");
                 }                  }
                 break;  
                   if (tfqueue(&(tf[i])) == -1)
                           warn("Unable to follow %s", tf[i].fname);
   
         }          }
           ltf = &(tf[i-1]);
   
         kq = -1;          (void)fflush(stdout);
 kq_retry:          if (!fflag || kq < 0)
         if (fflag && ((kq = kqueue()) >= 0)) {                  return;
                 EV_SET(&ke, fileno(fp), EVFILT_READ,  
                     EV_ENABLE | EV_ADD | EV_CLEAR,          while (1) {
                     0,                  if ((nevents = kevent(kq, NULL, 0, &ke, 1, ts)) <= 0) {
                     0, NULL);                          if (errno == EINTR) {
                 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) {  
                                 close(kq);                                  close(kq);
                                 kq = -1;                                  return;
                         }                          }
                 }                  }
         }  
   
         for (;;) {                  ctf = ke.udata;
                 while (!feof(fp) && (ch = getc(fp)) != EOF)                  if (nevents > 0) {
                         if (putchar(ch) == EOF)                          if (ke.filter == EVFILT_READ) {
                                 oerr();                                  if (ctf != ltf) {
                 if (ferror(fp)) {                                          printfname(ctf->fname);
                         ierr();                                          ltf = ctf;
                         if (kq != -1)                                  }
                                 close(kq);                                  clearerr(ctf->fp);
                         return;                                  while (!feof(ctf->fp) &&
                 }                                      (ch = getc(ctf->fp)) != EOF) {
                 (void)fflush(stdout);                                          if (putchar(ch) == EOF)
                 if (!fflag)                                                  oerr();
                         break;                                  }
                 clearerr(fp);                                  if (ferror(ctf->fp)) {
                 queue = 1;                                          ierr(ctf->fname);
                 if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) {                                          fclose(ctf->fp);
                         queue = 0;                                          warn("Lost file %s", ctf->fname);
                         sleep(1);                                          continue;
                 } else if (ke.filter == EVFILT_READ) {                                  }
                         continue;                                  (void)fflush(stdout);
                 } else if ((ke.fflags & NOTE_TRUNCATE) == 0) {                                  clearerr(ctf->fp);
                         /*                          } else if (ke.filter == EVFILT_VNODE) {
                          * File was renamed or deleted.                                  if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) {
                          *                                          /*
                          * Continue to look at it until a new file reappears                                           * File was deleted or renamed.
                          * with the same name.                                           *
                          * Fall back to the old algorithm for that.                                           * Continue to look at it until
                          */                                           * a new file reappears with
                         close(kq);                                           * the same name.
                         kq = -1;                                           */
                 }                                          (void) tfreopen(ctf);
                                   } else if (ke.fflags & NOTE_TRUNCATE) {
                 if (is_stdin || stat(fname, &nsb) != 0)                                          warnx("%s has been truncated, "
                         continue;                                              "resetting.", ctf->fname);
                 /* Reopen file if the inode changes or file was truncated */                                          fpurge(ctf->fp);
                 if (nsb.st_ino != sbp->st_ino) {                                          rewind(ctf->fp);
                         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));                  ts = tfreopen(NULL);
         }          }
         if (kq >= 0)  
                 close(kq);  
 }  }
   
 /*  /*
  * rlines -- display the last offset lines of the file.   * rlines -- display the last offset lines of the file.
  */   */
 static int  static int
 rlines(FILE *fp, off_t off, struct stat *sbp)  rlines(struct tailfile *tf, off_t off)
 {  {
         off_t pos;          off_t pos;
         int ch;          int ch;
   
         pos = sbp->st_size;          pos = tf->sb.st_size;
         if (pos == 0)          if (pos == 0)
                 return (0);                  return (0);
   
Line 253 
Line 256 
         ch = EOF;          ch = EOF;
         for (; off > 0 && pos >= 0; pos--) {          for (; off > 0 && pos >= 0; pos--) {
                 /* A seek per char isn't a problem with a smart stdio */                  /* A seek per char isn't a problem with a smart stdio */
                 if (fseeko(fp, pos, SEEK_SET) == -1) {                  if (fseeko(tf[0].fp, pos, SEEK_SET) == -1) {
                         ierr();                          ierr(tf->fname);
                         return (1);                          return (1);
                 }                  }
                 if ((ch = getc(fp)) == '\n')                  if ((ch = getc(tf[0].fp)) == '\n')
                         off--;                          off--;
                 else if (ch == EOF) {                  else if (ch == EOF) {
                         if (ferror(fp)) {                          if (ferror(tf[0].fp)) {
                                 ierr();                                  ierr(tf->fname);
                                 return (1);                                  return (1);
                         }                          }
                         break;                          break;
                 }                  }
         }          }
         /* If we read until start of file, put back last read char */          /* If we read until start of file, put back last read char */
         if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, fp) == EOF) {          if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, tf[0].fp) == EOF) {
                 ierr();                  ierr(tf->fname);
                 return (1);                  return (1);
         }          }
   
         while (!feof(fp) && (ch = getc(fp)) != EOF)          while (!feof(tf[0].fp) && (ch = getc(tf[0].fp)) != EOF)
                 if (putchar(ch) == EOF)                  if (putchar(ch) == EOF)
                         oerr();                          oerr();
         if (ferror(fp)) {          if (ferror(tf[0].fp)) {
                 ierr();                  ierr(tf->fname);
                 return (1);                  return (1);
         }          }
   
         return (0);          return (0);
   }
   
   static int
   tfqueue(struct tailfile *tf)
   {
           struct kevent ke[2];
           int i = 1;
   
           if (kq < 0) {
                   errno = EBADF;
                   return -1;
           }
   
           EV_SET(&(ke[0]), fileno(tf->fp), EVFILT_READ,
               EV_ENABLE | EV_ADD | EV_CLEAR, 0, 0, tf);
   
           if (S_ISREG(tf->sb.st_mode)) {
                   i = 2;
                   EV_SET(&(ke[1]), fileno(tf->fp), EVFILT_VNODE,
                       EV_ENABLE | EV_ADD | EV_CLEAR,
                       NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
                       0, tf);
           }
           if (kevent(kq, ke, i, NULL, 0, NULL) < 0) {
                   ierr(tf->fname);
                   return -1;
           }
           return 0;
   }
   
   #define AFILESINCR 8
   static const struct timespec *
   tfreopen(struct tailfile *tf) {
           static struct tailfile          **reopen = NULL;
           static int                        nfiles = 0, afiles = 0;
           static const struct timespec      ts = {1, 0};
   
           struct stat                       sb;
           struct tailfile                 **treopen, *ttf;
           int                               i;
   
           if (tf && ((stat(tf->fname, &sb) != 0) || sb.st_ino != tf->sb.st_ino)) {
                   if (afiles < ++nfiles) {
                           afiles += AFILESINCR;
                           treopen = reallocarray(reopen, afiles, sizeof(*reopen));
                           if (treopen)
                                   reopen = treopen;
                           else
                                   afiles -= AFILESINCR;
                   }
                   if (nfiles < afiles)
                           reopen[nfiles-1] = tf;
           }
   
           for (i = 0; i < nfiles; i++) {
                   ttf = reopen[i];
                   if (stat(ttf->fname, &sb) == -1)
                           continue;
                   if (sb.st_ino != ttf->sb.st_ino) {
                           (void) memcpy(&(ttf->sb), &sb, sizeof(ttf->sb));
                           ttf->fp = freopen(ttf->fname, "r", ttf->fp);
                           if (ttf->fp == NULL)
                                   ierr(ttf->fname);
                           else {
                                   warnx("%s has been replaced, reopening.",
                                       ttf->fname);
                                   tfqueue(ttf);
                           }
                   }
                   reopen[i] = reopen[--nfiles];
           }
   
           return nfiles ? &ts : NULL;
 }  }

Legend:
Removed from v.1.26  
changed lines
  Added in v.1.27