Print this page
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>


  14  *    notice, this list of conditions and the following disclaimer in the
  15  *    documentation and/or other materials provided with the distribution.
  16  * 4. Neither the name of the University nor the names of its contributors
  17  *    may be used to endorse or promote products derived from this software
  18  *    without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30  * SUCH DAMAGE.
  31  */
  32 
  33 /*
  34  * Solaris porting notes:  the original FreeBSD version made use of the
  35  *                         BSD kqueue event notification framework; this
  36  *                         was changed to use usleep()
  37  */
  38 
  39 #include <sys/param.h>
  40 #include <sys/mount.h>
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <sys/statfs.h>
  44 #include <sys/statvfs.h>
  45 #include <sys/time.h>
  46 #include <sys/mman.h>
  47 #include <sys/poll.h>
  48 #include <port.h>
  49 #include <err.h>
  50 #include <errno.h>
  51 #include <fcntl.h>
  52 #include <limits.h>
  53 #include <stdio.h>
  54 #include <stdlib.h>
  55 #include <string.h>

  56 #include <unistd.h>
  57 
  58 #include "extern.h"
  59 
  60 static void rlines(FILE *, const char *fn, off_t, struct stat *);
  61 static int show(file_info_t *);
  62 static void set_events(file_info_t *files);
  63 
  64 /* defines for inner loop actions */
  65 #define USE_SLEEP       0

  66 #define ADD_EVENTS      2
  67 
  68 int action = USE_SLEEP;

  69 
  70 static const file_info_t *last;
  71 
  72 /*
  73  * forward -- display the file, from an offset, forward.
  74  *
  75  * There are eight separate cases for this -- regular and non-regular
  76  * files, by bytes or lines and from the beginning or end of the file.
  77  *
  78  * FBYTES       byte offset from the beginning of the file
  79  *      REG     seek
  80  *      NOREG   read, counting bytes
  81  *
  82  * FLINES       line offset from the beginning of the file
  83  *      REG     read, counting lines
  84  *      NOREG   read, counting lines
  85  *
  86  * RBYTES       byte offset from the end of the file
  87  *      REG     seek
  88  *      NOREG   cyclically read characters into a wrap-around buffer


 244                         if (!qflag)
 245                                 (void) printf("\n==> %s <==\n",
 246                                     file->file_name);
 247                         last = file;
 248                 }
 249                 if (putchar(ch) == EOF)
 250                         oerr();
 251         }
 252         (void) fflush(stdout);
 253         if (ferror(file->fp)) {
 254                 (void) fclose(file->fp);
 255                 file->fp = NULL;
 256                 ierr(file->file_name);
 257                 return (0);
 258         }
 259         clearerr(file->fp);
 260         return (1);
 261 }
 262 
 263 static void



























































 264 set_events(file_info_t *files)
 265 {
 266         int i;
 267         file_info_t *file;
 268 
 269         for (i = 0, file = files; i < no_files; i++, file++) {
 270                 if (! file->fp)
 271                         continue;
 272 
 273                 (void) fstat(fileno(file->fp), &file->st);


 274         }
 275 }
 276 
 277 /*
 278  * follow -- display the file, from an offset, forward.
 279  *
 280  */
 281 void
 282 follow(file_info_t *files, enum STYLE style, off_t off)
 283 {
 284         int active, ev_change, i, n = -1;
 285         struct stat sb2;
 286         file_info_t *file;


 287 
 288         /* Position each of the files */
 289 
 290         file = files;
 291         active = 0;
 292         n = 0;
 293         for (i = 0; i < no_files; i++, file++) {
 294                 if (file->fp) {
 295                         active = 1;
 296                         n++;
 297                         if (no_files > 1 && !qflag)
 298                                 (void) printf("\n==> %s <==\n",
 299                                     file->file_name);
 300                         forward(file->fp, file->file_name, style, off,
 301                             &file->st);
 302                         if (Fflag && fileno(file->fp) != STDIN_FILENO)
 303                                 n++;
 304                 }
 305         }
 306         if (!Fflag && !active)
 307                 return;
 308 
 309         last = --file;






 310         set_events(files);
 311 
 312         for (;;) {
 313                 ev_change = 0;
 314                 if (Fflag) {
 315                         for (i = 0, file = files; i < no_files; i++, file++) {
 316                                 if (!file->fp) {
 317                                         file->fp = fopen(file->file_name, "r");
 318                                         if (file->fp != NULL &&
 319                                             fstat(fileno(file->fp), &file->st)
 320                                             == -1) {
 321                                                 (void) fclose(file->fp);
 322                                                 file->fp = NULL;
 323                                         }
 324                                         if (file->fp != NULL)
 325                                                 ev_change++;
 326                                         continue;
 327                                 }
 328                                 if (fileno(file->fp) == STDIN_FILENO)
 329                                         continue;
 330                                 if (stat(file->file_name, &sb2) == -1) {
 331                                         if (errno != ENOENT)
 332                                                 ierr(file->file_name);
 333                                         (void) show(file);
 334                                         (void) fclose(file->fp);
 335                                         file->fp = NULL;
 336                                         ev_change++;
 337                                         continue;
 338                                 }
 339 
 340                                 if (sb2.st_ino != file->st.st_ino ||
 341                                     sb2.st_dev != file->st.st_dev ||
 342                                     sb2.st_nlink == 0) {
 343                                         (void) show(file);

 344                                         file->fp = freopen(file->file_name, "r",
 345                                             file->fp);
 346                                         if (file->fp != NULL)
 347                                                 (void) memcpy(&file->st, &sb2,
 348                                                     sizeof (struct stat));
 349                                         else if (errno != ENOENT)
 350                                                 ierr(file->file_name);
 351                                         ev_change++;
 352                                 }
 353                         }
 354                 }
 355 
 356                 for (i = 0, file = files; i < no_files; i++, file++)
 357                         if (file->fp && !show(file))
 358                                 ev_change++;
 359 
 360                 if (ev_change)
 361                         set_events(files);
 362 
 363                 switch (action) {




















 364                 case USE_SLEEP:
 365                         (void) usleep(250000);
 366                         break;
 367                 }
 368         }
 369 }


  14  *    notice, this list of conditions and the following disclaimer in the
  15  *    documentation and/or other materials provided with the distribution.
  16  * 4. Neither the name of the University nor the names of its contributors
  17  *    may be used to endorse or promote products derived from this software
  18  *    without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30  * SUCH DAMAGE.
  31  */
  32 
  33 /*
  34  * Copyright (c) 2013, Joyent, Inc. All rights reserved.


  35  */
  36 
  37 #include <sys/param.h>
  38 #include <sys/mount.h>
  39 #include <sys/types.h>
  40 #include <sys/stat.h>
  41 #include <sys/statfs.h>
  42 #include <sys/statvfs.h>
  43 #include <sys/time.h>
  44 #include <sys/mman.h>
  45 #include <sys/poll.h>
  46 #include <port.h>
  47 #include <err.h>
  48 #include <errno.h>
  49 #include <fcntl.h>
  50 #include <limits.h>
  51 #include <stdio.h>
  52 #include <stdlib.h>
  53 #include <string.h>
  54 #include <strings.h>
  55 #include <unistd.h>
  56 
  57 #include "extern.h"
  58 
  59 static void rlines(FILE *, const char *fn, off_t, struct stat *);
  60 static int show(file_info_t *);
  61 static void set_events(file_info_t *files);
  62 
  63 /* defines for inner loop actions */
  64 #define USE_SLEEP       0
  65 #define USE_PORT        1
  66 #define ADD_EVENTS      2
  67 
  68 int port;
  69 int action = USE_PORT;
  70 
  71 static const file_info_t *last;
  72 
  73 /*
  74  * forward -- display the file, from an offset, forward.
  75  *
  76  * There are eight separate cases for this -- regular and non-regular
  77  * files, by bytes or lines and from the beginning or end of the file.
  78  *
  79  * FBYTES       byte offset from the beginning of the file
  80  *      REG     seek
  81  *      NOREG   read, counting bytes
  82  *
  83  * FLINES       line offset from the beginning of the file
  84  *      REG     read, counting lines
  85  *      NOREG   read, counting lines
  86  *
  87  * RBYTES       byte offset from the end of the file
  88  *      REG     seek
  89  *      NOREG   cyclically read characters into a wrap-around buffer


 245                         if (!qflag)
 246                                 (void) printf("\n==> %s <==\n",
 247                                     file->file_name);
 248                         last = file;
 249                 }
 250                 if (putchar(ch) == EOF)
 251                         oerr();
 252         }
 253         (void) fflush(stdout);
 254         if (ferror(file->fp)) {
 255                 (void) fclose(file->fp);
 256                 file->fp = NULL;
 257                 ierr(file->file_name);
 258                 return (0);
 259         }
 260         clearerr(file->fp);
 261         return (1);
 262 }
 263 
 264 static void
 265 associate(file_info_t *file, boolean_t assoc)
 266 {
 267         char buf[64];
 268 
 269         if (action != USE_PORT || file->fp == NULL)
 270                 return;
 271 
 272         if (!S_ISREG(file->st.st_mode)) {
 273                 /*
 274                  * For FIFOs, we use PORT_SOURCE_FD as our port event source.
 275                  */
 276                 if (assoc) {
 277                         (void) port_associate(port, PORT_SOURCE_FD,
 278                             fileno(file->fp), POLLIN, file);
 279                 } else {
 280                         (void) port_dissociate(port, PORT_SOURCE_FD,
 281                             fileno(file->fp));
 282                 }
 283 
 284                 return;
 285         }
 286 
 287         bzero(&file->fobj, sizeof (file->fobj));
 288 
 289         /*
 290          * We pull a bit of a stunt here.  PORT_SOURCE_FILE only allows us to
 291          * specify a file name -- not a file descriptor.  If we were to specify
 292          * the name of the file to port_associate() and that file were moved
 293          * aside, we would not be able to reassociate an event because we would
 294          * not know a name that would resolve to the new file (indeed, there
 295          * might not be such a name -- the file may have been unlinked).  But
 296          * there _is_ a name that we know maps to the file and doesn't change:
 297          * the name of the representation of the open file descriptor in /proc.
 298          * We therefore associate with this name (and the underlying file),
 299          * not the name of the file as specified at the command line.
 300          */
 301         (void) snprintf(buf,
 302             sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
 303 
 304         /*
 305          * Note that portfs uses the address of the specified file_obj_t to
 306          * tag an association; if one creates a different association with a
 307          * (different) file_ob_t that happens to be at the same address,
 308          * the first association will be implicitly removed.  To assure that
 309          * each file has a disjoint file_obj_t, we allocate the memory for it
 310          * in the file_info, not on the stack.
 311          */
 312         file->fobj.fo_name = buf;
 313 
 314         if (assoc) {
 315                 (void) port_associate(port, PORT_SOURCE_FILE,
 316                     (uintptr_t)&file->fobj, FILE_MODIFIED | FILE_TRUNC, file);
 317         } else {
 318                 (void) port_dissociate(port, PORT_SOURCE_FILE,
 319                     (uintptr_t)&file->fobj);
 320         }
 321 }
 322 
 323 static void
 324 set_events(file_info_t *files)
 325 {
 326         int i;
 327         file_info_t *file;
 328 
 329         for (i = 0, file = files; i < no_files; i++, file++) {
 330                 if (! file->fp)
 331                         continue;
 332 
 333                 (void) fstat(fileno(file->fp), &file->st);
 334 
 335                 associate(file, B_TRUE);
 336         }
 337 }
 338 
 339 /*
 340  * follow -- display the file, from an offset, forward.
 341  *
 342  */
 343 void
 344 follow(file_info_t *files, enum STYLE style, off_t off)
 345 {
 346         int active, ev_change, i, n = -1;
 347         struct stat sb2;
 348         file_info_t *file;
 349         struct timespec ts;
 350         port_event_t ev;
 351 
 352         /* Position each of the files */
 353 
 354         file = files;
 355         active = 0;
 356         n = 0;
 357         for (i = 0; i < no_files; i++, file++) {
 358                 if (file->fp) {
 359                         active = 1;
 360                         n++;
 361                         if (no_files > 1 && !qflag)
 362                                 (void) printf("\n==> %s <==\n",
 363                                     file->file_name);
 364                         forward(file->fp, file->file_name, style, off,
 365                             &file->st);
 366                         if (Fflag && fileno(file->fp) != STDIN_FILENO)
 367                                 n++;
 368                 }
 369         }
 370         if (!Fflag && !active)
 371                 return;
 372 
 373         last = --file;
 374 
 375         if (action == USE_PORT &&
 376             (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
 377             (port = port_create()) == -1))
 378                 action = USE_SLEEP;
 379 
 380         set_events(files);
 381 
 382         for (;;) {
 383                 ev_change = 0;
 384                 if (Fflag) {
 385                         for (i = 0, file = files; i < no_files; i++, file++) {
 386                                 if (!file->fp) {
 387                                         file->fp = fopen(file->file_name, "r");
 388                                         if (file->fp != NULL &&
 389                                             fstat(fileno(file->fp), &file->st)
 390                                             == -1) {
 391                                                 (void) fclose(file->fp);
 392                                                 file->fp = NULL;
 393                                         }
 394                                         if (file->fp != NULL)
 395                                                 ev_change++;
 396                                         continue;
 397                                 }
 398                                 if (fileno(file->fp) == STDIN_FILENO)
 399                                         continue;
 400                                 if (stat(file->file_name, &sb2) == -1) {
 401                                         if (errno != ENOENT)
 402                                                 ierr(file->file_name);
 403                                         (void) show(file);
 404                                         (void) fclose(file->fp);
 405                                         file->fp = NULL;
 406                                         ev_change++;
 407                                         continue;
 408                                 }
 409 
 410                                 if (sb2.st_ino != file->st.st_ino ||
 411                                     sb2.st_dev != file->st.st_dev ||
 412                                     sb2.st_nlink == 0) {
 413                                         (void) show(file);
 414                                         associate(file, B_FALSE);
 415                                         file->fp = freopen(file->file_name, "r",
 416                                             file->fp);
 417                                         if (file->fp != NULL) {
 418                                                 (void) memcpy(&file->st, &sb2,
 419                                                     sizeof (struct stat));
 420                                         } else if (errno != ENOENT)
 421                                                 ierr(file->file_name);
 422                                         ev_change++;
 423                                 }
 424                         }
 425                 }
 426 
 427                 for (i = 0, file = files; i < no_files; i++, file++)
 428                         if (file->fp && !show(file))
 429                                 ev_change++;
 430 
 431                 if (ev_change)
 432                         set_events(files);
 433 
 434                 switch (action) {
 435                 case USE_PORT:
 436                         ts.tv_sec = 1;
 437                         ts.tv_nsec = 0;
 438 
 439                         /*
 440                          * In the -F case we set a timeout to ensure that
 441                          * we re-stat the file at least once every second.
 442                          */
 443                         n = port_get(port, &ev, Fflag ? &ts : NULL);
 444 
 445                         if (n == 0) {
 446                                 file = (file_info_t *)ev.portev_user;
 447                                 associate(file, B_TRUE);
 448 
 449                                 if (ev.portev_events & FILE_TRUNC)
 450                                         (void) fseek(file->fp, 0, SEEK_SET);
 451                         }
 452 
 453                         break;
 454 
 455                 case USE_SLEEP:
 456                         (void) usleep(250000);
 457                         break;
 458                 }
 459         }
 460 }