[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.24 and 1.25

version 1.24, 2008/10/17 11:38:20 version 1.25, 2008/11/13 18:33:03
Line 48 
Line 48 
 #include <stdio.h>  #include <stdio.h>
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
 #include <stdlib.h>  
   
 #include "extern.h"  #include "extern.h"
   
Line 80 
Line 79 
 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)  forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
 {  {
         int ch;          int ch;
           struct stat nsb;
           int kq, queue;
           struct kevent ke;
   
         switch(style) {          switch(style) {
         case FBYTES:          case FBYTES:
Line 158 
Line 160 
                 break;                  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 = -1;
         if ((kq = kqueue()) < 0)  kq_retry:
                 err(2, "kqueue() failed");          if (fflag && ((kq = kqueue()) >= 0)) {
   
         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_SET(&ke, fileno(fp), EVFILT_READ,
                     EV_ENABLE | EV_ADD | EV_CLEAR,                      EV_ENABLE | EV_ADD | EV_CLEAR,
                     NULL, 0, NULL);                      0,
                 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)                      0, NULL);
                         goto cleanup;                  if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
                 else if (S_ISREG(files[i].fst.st_mode)) {                          close(kq);
                         /* one event to detect if inode changed */                          kq = -1;
                   } else if (S_ISREG(sbp->st_mode)) {
                         EV_SET(&ke, fileno(fp), EVFILT_VNODE,                          EV_SET(&ke, fileno(fp), EVFILT_VNODE,
                             EV_ENABLE | EV_ADD | EV_CLEAR,                              EV_ENABLE | EV_ADD | EV_CLEAR,
                             NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,                              NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
                             0, NULL);                              0, NULL);
                         if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)                          if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
                                 goto cleanup;                                  close(kq);
                                   kq = -1;
                           }
                 }                  }
         }          }
   
         /* no files to read */  
         if (nbfiles == 0)  
                 goto cleanup;  
   
         if (fp == NULL)  
                 fp = files[nbfiles - 1].fp;  
   
         for (;;) {          for (;;) {
                 while (!feof(fp) && (ch = getc(fp)) != EOF)                  while (!feof(fp) && (ch = getc(fp)) != EOF)
                         if (putchar(ch) == EOF)                          if (putchar(ch) == EOF)
                                 oerr();                                  oerr();
                 if (ferror(fp))                  if (ferror(fp)) {
                         goto cleanup;                          ierr();
                           if (kq != -1)
                                   close(kq);
                           return;
                   }
                 (void)fflush(stdout);                  (void)fflush(stdout);
                   if (!fflag)
                           break;
                 clearerr(fp);                  clearerr(fp);
                 /* give it a chance to fail.. */                  queue = 1;
                 if (kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) {                  if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) {
                           queue = 0;
                         sleep(1);                          sleep(1);
                   } else if (ke.filter == EVFILT_READ) {
                         continue;                          continue;
                 } else {                  } else if ((ke.fflags & NOTE_TRUNCATE) == 0) {
                         /* an event occured on file #i */                          /*
                         for (i = 0 ; i < nbfiles ; i++)                           * File was renamed or deleted.
                                 if (fileno(files[i].fp) == ke.ident)                           *
                                         break;                           * 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 (is_stdin || stat(fname, &nsb) != 0)
                         if (ke.filter == EVFILT_READ) {                          continue;
                                 if (fp != files[i].fp) {                  /* Reopen file if the inode changes or file was truncated */
                                         (void)printf("\n==> %s <==\n",files[i].fname);                  if (nsb.st_ino != sbp->st_ino) {
                                         fp = files[i].fp;                          warnx("%s has been replaced, reopening.", fname);
                                         clearerr(fp);                          if ((fp = freopen(fname, "r", fp)) == NULL) {
                                 }                                  ierr();
                         /* EVFILT_VNODE event and File was renamed or deleted */                                  if (kq >= 0)
                         } else if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) {                                          close(kq);
                                 /* file didn't reappear */                                  return;
                                 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)          if (kq >= 0)
                 close(kq);                  close(kq);
         free(files);  
         return;  
 }  }
   
 /*  /*

Legend:
Removed from v.1.24  
changed lines
  Added in v.1.25