1 /*
   2  * Copyright (c) 1991, 1993
   3  *      The Regents of the University of California.  All rights reserved.
   4  *
   5  * This code is derived from software contributed to Berkeley by
   6  * Edward Sze-Tyan Wang.
   7  *
   8  * Redistribution and use in source and binary forms, with or without
   9  * modification, are permitted provided that the following conditions
  10  * are met:
  11  * 1. Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  * 2. Redistributions in binary form must reproduce the above copyright
  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
  90  *
  91  * RLINES
  92  *      REG     mmap the file and step back until reach the correct offset.
  93  *      NOREG   cyclically read lines into a wrap-around array of buffers
  94  */
  95 void
  96 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
  97 {
  98         int ch;
  99 
 100         switch (style) {
 101         case FBYTES:
 102                 if (off == 0)
 103                         break;
 104                 if (S_ISREG(sbp->st_mode)) {
 105                         if (sbp->st_size < off)
 106                                 off = sbp->st_size;
 107                         if (fseeko(fp, off, SEEK_SET) == -1) {
 108                                 ierr(fn);
 109                                 return;
 110                         }
 111                 } else while (off--)
 112                         if ((ch = getc(fp)) == EOF) {
 113                                 if (ferror(fp)) {
 114                                         ierr(fn);
 115                                         return;
 116                                 }
 117                                 break;
 118                         }
 119                 break;
 120         case FLINES:
 121                 if (off == 0)
 122                         break;
 123                 for (;;) {
 124                         if ((ch = getc(fp)) == EOF) {
 125                                 if (ferror(fp)) {
 126                                         ierr(fn);
 127                                         return;
 128                                 }
 129                                 break;
 130                         }
 131                         if (ch == '\n' && !--off)
 132                                 break;
 133                 }
 134                 break;
 135         case RBYTES:
 136                 if (S_ISREG(sbp->st_mode)) {
 137                         if (sbp->st_size >= off &&
 138                             fseeko(fp, -off, SEEK_END) == -1) {
 139                                 ierr(fn);
 140                                 return;
 141                         }
 142                 } else if (off == 0) {
 143                         while (getc(fp) != EOF)
 144                                 ;
 145                         if (ferror(fp)) {
 146                                 ierr(fn);
 147                                 return;
 148                         }
 149                 } else
 150                         if (bytes(fp, fn, off))
 151                                 return;
 152                 break;
 153         case RLINES:
 154                 if (S_ISREG(sbp->st_mode))
 155                         if (!off) {
 156                                 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
 157                                         ierr(fn);
 158                                         return;
 159                                 }
 160                         } else
 161                                 rlines(fp, fn, off, sbp);
 162                 else if (off == 0) {
 163                         while (getc(fp) != EOF)
 164                                 ;
 165                         if (ferror(fp)) {
 166                                 ierr(fn);
 167                                 return;
 168                         }
 169                 } else
 170                         if (lines(fp, fn, off))
 171                                 return;
 172                 break;
 173         default:
 174                 break;
 175         }
 176 
 177         while ((ch = getc(fp)) != EOF)
 178                 if (putchar(ch) == EOF)
 179                         oerr();
 180         if (ferror(fp)) {
 181                 ierr(fn);
 182                 return;
 183         }
 184         (void) fflush(stdout);
 185 }
 186 
 187 /*
 188  * rlines -- display the last offset lines of the file.
 189  */
 190 static void
 191 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
 192 {
 193         struct mapinfo map;
 194         off_t curoff, size;
 195         int i;
 196 
 197         if ((size = sbp->st_size) == 0)
 198                 return;
 199         map.start = NULL;
 200         map.fd = fileno(fp);
 201         map.mapoff = map.maxoff = size;
 202 
 203         /*
 204          * Last char is special, ignore whether newline or not. Note that
 205          * size == 0 is dealt with above, and size == 1 sets curoff to -1.
 206          */
 207         curoff = size - 2;
 208         while (curoff >= 0) {
 209                 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
 210                         ierr(fn);
 211                         return;
 212                 }
 213                 for (i = curoff - map.mapoff; i >= 0; i--)
 214                         if (map.start[i] == '\n' && --off == 0)
 215                                 break;
 216                 /* `i' is either the map offset of a '\n', or -1. */
 217                 curoff = map.mapoff + i;
 218                 if (i >= 0)
 219                         break;
 220         }
 221         curoff++;
 222         if (mapprint(&map, curoff, size - curoff) != 0) {
 223                 ierr(fn);
 224                 exit(1);
 225         }
 226 
 227         /* Set the file pointer to reflect the length displayed. */
 228         if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
 229                 ierr(fn);
 230                 return;
 231         }
 232         if (map.start != NULL && munmap(map.start, map.maplen)) {
 233                 ierr(fn);
 234                 return;
 235         }
 236 }
 237 
 238 static int
 239 show(file_info_t *file)
 240 {
 241         int ch;
 242 
 243         while ((ch = getc(file->fp)) != EOF) {
 244                 if (last != file && no_files > 1) {
 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 }