forward.c revision 216370
1235537Sgber/*- 2235537Sgber * Copyright (c) 1991, 1993 3235537Sgber * The Regents of the University of California. All rights reserved. 4235537Sgber * 5235537Sgber * This code is derived from software contributed to Berkeley by 6235537Sgber * Edward Sze-Tyan Wang. 7235537Sgber * 8235537Sgber * Redistribution and use in source and binary forms, with or without 9235537Sgber * modification, are permitted provided that the following conditions 10235537Sgber * are met: 11235537Sgber * 1. Redistributions of source code must retain the above copyright 12235537Sgber * notice, this list of conditions and the following disclaimer. 13235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 14235537Sgber * notice, this list of conditions and the following disclaimer in the 15235537Sgber * documentation and/or other materials provided with the distribution. 16235537Sgber * 4. Neither the name of the University nor the names of its contributors 17235537Sgber * may be used to endorse or promote products derived from this software 18235537Sgber * without specific prior written permission. 19235537Sgber * 20235537Sgber * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30235537Sgber * SUCH DAMAGE. 31235537Sgber */ 32235537Sgber 33235537Sgber#include <sys/cdefs.h> 34235537Sgber 35235537Sgber__FBSDID("$FreeBSD: head/usr.bin/tail/forward.c 216370 2010-12-11 08:32:16Z joel $"); 36235537Sgber 37235537Sgber#ifndef lint 38235537Sgberstatic const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 39235537Sgber#endif 40235537Sgber 41235537Sgber#include <sys/param.h> 42235537Sgber#include <sys/mount.h> 43235537Sgber#include <sys/types.h> 44235537Sgber#include <sys/stat.h> 45235537Sgber#include <sys/time.h> 46235537Sgber#include <sys/mman.h> 47235537Sgber#include <sys/event.h> 48235537Sgber 49235537Sgber#include <err.h> 50235537Sgber#include <errno.h> 51235537Sgber#include <fcntl.h> 52235537Sgber#include <limits.h> 53235537Sgber#include <stdio.h> 54235537Sgber#include <stdlib.h> 55235537Sgber#include <string.h> 56235537Sgber#include <unistd.h> 57235537Sgber 58235537Sgber#include "extern.h" 59235537Sgber 60235537Sgberstatic void rlines(FILE *, const char *fn, off_t, struct stat *); 61235537Sgberstatic int show(file_info_t *); 62235537Sgberstatic void set_events(file_info_t *files); 63235537Sgber 64235537Sgber/* defines for inner loop actions */ 65235537Sgber#define USE_SLEEP 0 66235537Sgber#define USE_KQUEUE 1 67235537Sgber#define ADD_EVENTS 2 68235537Sgber 69235537Sgberstruct kevent *ev; 70235537Sgberint action = USE_SLEEP; 71235537Sgberint kq; 72235537Sgber 73235537Sgberstatic const file_info_t *last; 74235537Sgber 75235537Sgber/* 76235537Sgber * forward -- display the file, from an offset, forward. 77235537Sgber * 78235537Sgber * There are eight separate cases for this -- regular and non-regular 79235537Sgber * files, by bytes or lines and from the beginning or end of the file. 80235537Sgber * 81235537Sgber * FBYTES byte offset from the beginning of the file 82235537Sgber * REG seek 83235537Sgber * NOREG read, counting bytes 84235537Sgber * 85235537Sgber * FLINES line offset from the beginning of the file 86235537Sgber * REG read, counting lines 87235537Sgber * NOREG read, counting lines 88235537Sgber * 89235537Sgber * RBYTES byte offset from the end of the file 90235537Sgber * REG seek 91235537Sgber * NOREG cyclically read characters into a wrap-around buffer 92235537Sgber * 93235537Sgber * RLINES 94235537Sgber * REG mmap the file and step back until reach the correct offset. 95235537Sgber * NOREG cyclically read lines into a wrap-around array of buffers 96235537Sgber */ 97235537Sgbervoid 98235537Sgberforward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 99235537Sgber{ 100235537Sgber int ch; 101235537Sgber 102235537Sgber switch(style) { 103235537Sgber case FBYTES: 104235537Sgber if (off == 0) 105235537Sgber break; 106235537Sgber if (S_ISREG(sbp->st_mode)) { 107235537Sgber if (sbp->st_size < off) 108235537Sgber off = sbp->st_size; 109235537Sgber if (fseeko(fp, off, SEEK_SET) == -1) { 110235537Sgber ierr(fn); 111235537Sgber return; 112235537Sgber } 113235537Sgber } else while (off--) 114235537Sgber if ((ch = getc(fp)) == EOF) { 115235537Sgber if (ferror(fp)) { 116235537Sgber ierr(fn); 117235537Sgber return; 118235537Sgber } 119235537Sgber break; 120235537Sgber } 121235537Sgber break; 122235537Sgber case FLINES: 123235537Sgber if (off == 0) 124235537Sgber break; 125235537Sgber for (;;) { 126235537Sgber if ((ch = getc(fp)) == EOF) { 127235537Sgber if (ferror(fp)) { 128235537Sgber ierr(fn); 129235537Sgber return; 130235537Sgber } 131235537Sgber break; 132235537Sgber } 133235537Sgber if (ch == '\n' && !--off) 134235537Sgber break; 135235537Sgber } 136235537Sgber break; 137235537Sgber case RBYTES: 138235537Sgber if (S_ISREG(sbp->st_mode)) { 139235537Sgber if (sbp->st_size >= off && 140235537Sgber fseeko(fp, -off, SEEK_END) == -1) { 141235537Sgber ierr(fn); 142235537Sgber return; 143235537Sgber } 144235537Sgber } else if (off == 0) { 145235537Sgber while (getc(fp) != EOF); 146235537Sgber if (ferror(fp)) { 147235537Sgber ierr(fn); 148235537Sgber return; 149235537Sgber } 150235537Sgber } else 151235537Sgber if (bytes(fp, fn, off)) 152235537Sgber return; 153235537Sgber break; 154235537Sgber case RLINES: 155235537Sgber if (S_ISREG(sbp->st_mode)) 156235537Sgber if (!off) { 157235537Sgber if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 158235537Sgber ierr(fn); 159235537Sgber return; 160235537Sgber } 161235537Sgber } else 162235537Sgber rlines(fp, fn, off, sbp); 163235537Sgber else if (off == 0) { 164235537Sgber while (getc(fp) != EOF); 165235537Sgber if (ferror(fp)) { 166235537Sgber ierr(fn); 167235537Sgber return; 168235537Sgber } 169235537Sgber } else 170235537Sgber if (lines(fp, fn, off)) 171235537Sgber return; 172235537Sgber break; 173235537Sgber default: 174235537Sgber break; 175235537Sgber } 176235537Sgber 177235537Sgber while ((ch = getc(fp)) != EOF) 178235537Sgber if (putchar(ch) == EOF) 179235537Sgber oerr(); 180235537Sgber if (ferror(fp)) { 181235537Sgber ierr(fn); 182235537Sgber return; 183235537Sgber } 184235537Sgber (void)fflush(stdout); 185235537Sgber} 186235537Sgber 187235537Sgber/* 188235537Sgber * rlines -- display the last offset lines of the file. 189235537Sgber */ 190235537Sgberstatic void 191235537Sgberrlines(FILE *fp, const char *fn, off_t off, struct stat *sbp) 192235537Sgber{ 193235537Sgber struct mapinfo map; 194235537Sgber off_t curoff, size; 195235537Sgber int i; 196235537Sgber 197235537Sgber if (!(size = sbp->st_size)) 198235537Sgber return; 199235537Sgber map.start = NULL; 200235537Sgber map.fd = fileno(fp); 201235537Sgber map.mapoff = map.maxoff = size; 202235537Sgber 203235537Sgber /* 204235537Sgber * Last char is special, ignore whether newline or not. Note that 205235537Sgber * size == 0 is dealt with above, and size == 1 sets curoff to -1. 206235537Sgber */ 207235537Sgber curoff = size - 2; 208235537Sgber while (curoff >= 0) { 209235537Sgber if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 210235537Sgber ierr(fn); 211235537Sgber return; 212235537Sgber } 213235537Sgber for (i = curoff - map.mapoff; i >= 0; i--) 214235537Sgber if (map.start[i] == '\n' && --off == 0) 215235537Sgber break; 216235537Sgber /* `i' is either the map offset of a '\n', or -1. */ 217235537Sgber curoff = map.mapoff + i; 218235537Sgber if (i >= 0) 219235537Sgber break; 220235537Sgber } 221235537Sgber curoff++; 222235537Sgber if (mapprint(&map, curoff, size - curoff) != 0) { 223235537Sgber ierr(fn); 224235537Sgber exit(1); 225235537Sgber } 226235537Sgber 227235537Sgber /* Set the file pointer to reflect the length displayed. */ 228235537Sgber if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 229235537Sgber ierr(fn); 230235537Sgber return; 231235537Sgber } 232235537Sgber if (map.start != NULL && munmap(map.start, map.maplen)) { 233235537Sgber ierr(fn); 234235537Sgber return; 235235537Sgber } 236235537Sgber} 237235537Sgber 238235537Sgberstatic int 239235537Sgbershow(file_info_t *file) 240235537Sgber{ 241235537Sgber int ch; 242235537Sgber 243235537Sgber while ((ch = getc(file->fp)) != EOF) { 244235537Sgber if (last != file && no_files > 1) { 245235537Sgber if (!qflag) 246235537Sgber (void)printf("\n==> %s <==\n", file->file_name); 247235537Sgber last = file; 248235537Sgber } 249235537Sgber if (putchar(ch) == EOF) 250235537Sgber oerr(); 251235537Sgber } 252235537Sgber (void)fflush(stdout); 253235537Sgber if (ferror(file->fp)) { 254235537Sgber fclose(file->fp); 255235537Sgber file->fp = NULL; 256235537Sgber ierr(file->file_name); 257235537Sgber return 0; 258235537Sgber } 259235537Sgber clearerr(file->fp); 260235537Sgber return 1; 261235537Sgber} 262235537Sgber 263235537Sgberstatic void 264235537Sgberset_events(file_info_t *files) 265235537Sgber{ 266235537Sgber int i, n = 0; 267235537Sgber file_info_t *file; 268235537Sgber struct timespec ts; 269235537Sgber struct statfs sf; 270235537Sgber 271235537Sgber ts.tv_sec = 0; 272235537Sgber ts.tv_nsec = 0; 273235537Sgber 274235537Sgber action = USE_KQUEUE; 275235537Sgber for (i = 0, file = files; i < no_files; i++, file++) { 276235537Sgber if (! file->fp) 277235537Sgber continue; 278235537Sgber 279235537Sgber if (fstatfs(fileno(file->fp), &sf) == 0 && 280235537Sgber (sf.f_flags & MNT_LOCAL) == 0) { 281235537Sgber action = USE_SLEEP; 282235537Sgber return; 283235537Sgber } 284235537Sgber 285235537Sgber if (Fflag && fileno(file->fp) != STDIN_FILENO) { 286235537Sgber EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 287235537Sgber EV_ADD | EV_ENABLE | EV_CLEAR, 288235537Sgber NOTE_DELETE | NOTE_RENAME, 0, 0); 289235537Sgber n++; 290235537Sgber } 291235537Sgber EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 292235537Sgber EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 293235537Sgber n++; 294235537Sgber } 295235537Sgber 296235537Sgber if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 297235537Sgber action = USE_SLEEP; 298235537Sgber } 299235537Sgber} 300235537Sgber 301235537Sgber/* 302235537Sgber * follow -- display the file, from an offset, forward. 303235537Sgber * 304235537Sgber */ 305235537Sgbervoid 306235537Sgberfollow(file_info_t *files, enum STYLE style, off_t off) 307235537Sgber{ 308235537Sgber int active, ev_change, i, n = -1; 309235537Sgber struct stat sb2; 310235537Sgber file_info_t *file; 311235537Sgber struct timespec ts; 312235537Sgber 313235537Sgber /* Position each of the files */ 314235537Sgber 315235537Sgber file = files; 316235537Sgber active = 0; 317235537Sgber n = 0; 318235537Sgber for (i = 0; i < no_files; i++, file++) { 319235537Sgber if (file->fp) { 320235537Sgber active = 1; 321235537Sgber n++; 322235537Sgber if (no_files > 1 && !qflag) 323235537Sgber (void)printf("\n==> %s <==\n", file->file_name); 324235537Sgber forward(file->fp, file->file_name, style, off, &file->st); 325235537Sgber if (Fflag && fileno(file->fp) != STDIN_FILENO) 326235537Sgber n++; 327235537Sgber } 328235537Sgber } 329235537Sgber if (!Fflag && !active) 330235537Sgber return; 331235537Sgber 332235537Sgber last = --file; 333235537Sgber 334235537Sgber kq = kqueue(); 335235537Sgber if (kq < 0) 336235537Sgber err(1, "kqueue"); 337235537Sgber ev = malloc(n * sizeof(struct kevent)); 338235537Sgber if (! ev) 339235537Sgber err(1, "Couldn't allocate memory for kevents."); 340235537Sgber set_events(files); 341235537Sgber 342235537Sgber for (;;) { 343235537Sgber ev_change = 0; 344235537Sgber if (Fflag) { 345235537Sgber for (i = 0, file = files; i < no_files; i++, file++) { 346235537Sgber if (!file->fp) { 347235537Sgber file->fp = fopen(file->file_name, "r"); 348235537Sgber if (file->fp != NULL && 349235537Sgber fstat(fileno(file->fp), &file->st) 350235537Sgber == -1) { 351235537Sgber fclose(file->fp); 352235537Sgber file->fp = NULL; 353235537Sgber } 354235537Sgber if (file->fp != NULL) 355235537Sgber ev_change++; 356235537Sgber continue; 357235537Sgber } 358235537Sgber if (fileno(file->fp) == STDIN_FILENO) 359235537Sgber continue; 360235537Sgber if (stat(file->file_name, &sb2) == -1) { 361235537Sgber if (errno != ENOENT) 362235537Sgber ierr(file->file_name); 363235537Sgber show(file); 364235537Sgber fclose(file->fp); 365235537Sgber file->fp = NULL; 366235537Sgber ev_change++; 367235537Sgber continue; 368235537Sgber } 369235537Sgber 370235537Sgber if (sb2.st_ino != file->st.st_ino || 371235537Sgber sb2.st_dev != file->st.st_dev || 372235537Sgber sb2.st_nlink == 0) { 373235537Sgber show(file); 374235537Sgber file->fp = freopen(file->file_name, "r", 375235537Sgber file->fp); 376235537Sgber if (file->fp != NULL) 377235537Sgber memcpy(&file->st, &sb2, 378235537Sgber sizeof(struct stat)); 379235537Sgber else if (errno != ENOENT) 380235537Sgber ierr(file->file_name); 381235537Sgber ev_change++; 382235537Sgber } 383235537Sgber } 384235537Sgber } 385235537Sgber 386235537Sgber for (i = 0, file = files; i < no_files; i++, file++) 387235537Sgber if (file->fp && !show(file)) 388235537Sgber ev_change++; 389235537Sgber 390235537Sgber if (ev_change) 391235537Sgber set_events(files); 392235537Sgber 393235537Sgber switch (action) { 394235537Sgber case USE_KQUEUE: 395235537Sgber ts.tv_sec = 1; 396235537Sgber ts.tv_nsec = 0; 397235537Sgber /* 398235537Sgber * In the -F case we set a timeout to ensure that 399235537Sgber * we re-stat the file at least once every second. 400235537Sgber */ 401235537Sgber n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 402235537Sgber if (n < 0) 403235537Sgber err(1, "kevent"); 404235537Sgber if (n == 0) { 405235537Sgber /* timeout */ 406235537Sgber break; 407235537Sgber } else if (ev->filter == EVFILT_READ && ev->data < 0) { 408235537Sgber /* file shrank, reposition to end */ 409235537Sgber if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { 410235537Sgber ierr(file->file_name); 411235537Sgber continue; 412235537Sgber } 413235537Sgber } 414235537Sgber break; 415235537Sgber 416235537Sgber case USE_SLEEP: 417235537Sgber (void) usleep(250000); 418235537Sgber break; 419235537Sgber } 420235537Sgber } 421235537Sgber} 422235537Sgber