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 }