11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1991, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * Edward Sze-Tyan Wang. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 4. Neither the name of the University nor the names of its contributors 171590Srgrimes * may be used to endorse or promote products derived from this software 181590Srgrimes * without specific prior written permission. 191590Srgrimes * 201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301590Srgrimes * SUCH DAMAGE. 311590Srgrimes */ 321590Srgrimes 3387712Smarkm#include <sys/cdefs.h> 3487712Smarkm 3587712Smarkm__FBSDID("$FreeBSD: releng/10.3/usr.bin/tail/forward.c 251565 2013-06-09 08:06:26Z jh $"); 3687712Smarkm 371590Srgrimes#ifndef lint 3887712Smarkmstatic const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 3969528Sasmodai#endif 401590Srgrimes 41149485Sps#include <sys/param.h> 42149485Sps#include <sys/mount.h> 431590Srgrimes#include <sys/types.h> 441590Srgrimes#include <sys/stat.h> 451590Srgrimes#include <sys/time.h> 461590Srgrimes#include <sys/mman.h> 4759373Sjlemon#include <sys/event.h> 481590Srgrimes 4987712Smarkm#include <err.h> 5087712Smarkm#include <errno.h> 5187712Smarkm#include <fcntl.h> 521590Srgrimes#include <limits.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 5687712Smarkm#include <unistd.h> 5787712Smarkm 581590Srgrimes#include "extern.h" 591590Srgrimes 60193488Sbrianstatic void rlines(FILE *, const char *fn, off_t, struct stat *); 61193488Sbrianstatic int show(file_info_t *); 62139993Sdwmalonestatic void set_events(file_info_t *files); 631590Srgrimes 6461964Sjlemon/* defines for inner loop actions */ 6561964Sjlemon#define USE_SLEEP 0 6661964Sjlemon#define USE_KQUEUE 1 6761964Sjlemon#define ADD_EVENTS 2 6861964Sjlemon 69227184Sedstatic struct kevent *ev; 70227184Sedstatic int action = USE_SLEEP; 71227184Sedstatic int kq; 72137225Spaul 73141279Sdelphijstatic const file_info_t *last; 74141279Sdelphij 751590Srgrimes/* 761590Srgrimes * forward -- display the file, from an offset, forward. 771590Srgrimes * 781590Srgrimes * There are eight separate cases for this -- regular and non-regular 791590Srgrimes * files, by bytes or lines and from the beginning or end of the file. 801590Srgrimes * 811590Srgrimes * FBYTES byte offset from the beginning of the file 821590Srgrimes * REG seek 831590Srgrimes * NOREG read, counting bytes 841590Srgrimes * 851590Srgrimes * FLINES line offset from the beginning of the file 861590Srgrimes * REG read, counting lines 871590Srgrimes * NOREG read, counting lines 881590Srgrimes * 891590Srgrimes * RBYTES byte offset from the end of the file 901590Srgrimes * REG seek 911590Srgrimes * NOREG cyclically read characters into a wrap-around buffer 921590Srgrimes * 931590Srgrimes * RLINES 941590Srgrimes * REG mmap the file and step back until reach the correct offset. 951590Srgrimes * NOREG cyclically read lines into a wrap-around array of buffers 961590Srgrimes */ 971590Srgrimesvoid 98193488Sbrianforward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 991590Srgrimes{ 100139993Sdwmalone int ch; 1011590Srgrimes 1021590Srgrimes switch(style) { 1031590Srgrimes case FBYTES: 1041590Srgrimes if (off == 0) 1051590Srgrimes break; 1061590Srgrimes if (S_ISREG(sbp->st_mode)) { 1071590Srgrimes if (sbp->st_size < off) 1081590Srgrimes off = sbp->st_size; 10982762Sache if (fseeko(fp, off, SEEK_SET) == -1) { 110193488Sbrian ierr(fn); 1111590Srgrimes return; 1121590Srgrimes } 1131590Srgrimes } else while (off--) 1141590Srgrimes if ((ch = getc(fp)) == EOF) { 1151590Srgrimes if (ferror(fp)) { 116193488Sbrian ierr(fn); 1171590Srgrimes return; 1181590Srgrimes } 1191590Srgrimes break; 1201590Srgrimes } 1211590Srgrimes break; 1221590Srgrimes case FLINES: 1231590Srgrimes if (off == 0) 1241590Srgrimes break; 1251590Srgrimes for (;;) { 1261590Srgrimes if ((ch = getc(fp)) == EOF) { 1271590Srgrimes if (ferror(fp)) { 128193488Sbrian ierr(fn); 1291590Srgrimes return; 1301590Srgrimes } 1311590Srgrimes break; 1321590Srgrimes } 1331590Srgrimes if (ch == '\n' && !--off) 1341590Srgrimes break; 1351590Srgrimes } 1361590Srgrimes break; 1371590Srgrimes case RBYTES: 1381590Srgrimes if (S_ISREG(sbp->st_mode)) { 1391590Srgrimes if (sbp->st_size >= off && 14082762Sache fseeko(fp, -off, SEEK_END) == -1) { 141193488Sbrian ierr(fn); 1421590Srgrimes return; 1431590Srgrimes } 1441590Srgrimes } else if (off == 0) { 1451590Srgrimes while (getc(fp) != EOF); 1461590Srgrimes if (ferror(fp)) { 147193488Sbrian ierr(fn); 1481590Srgrimes return; 1491590Srgrimes } 1501590Srgrimes } else 151193488Sbrian if (bytes(fp, fn, off)) 15217341Sadam return; 1531590Srgrimes break; 1541590Srgrimes case RLINES: 1551590Srgrimes if (S_ISREG(sbp->st_mode)) 1561590Srgrimes if (!off) { 15782762Sache if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 158193488Sbrian ierr(fn); 1591590Srgrimes return; 1601590Srgrimes } 1611590Srgrimes } else 162193488Sbrian rlines(fp, fn, off, sbp); 1631590Srgrimes else if (off == 0) { 1641590Srgrimes while (getc(fp) != EOF); 1651590Srgrimes if (ferror(fp)) { 166193488Sbrian ierr(fn); 1671590Srgrimes return; 1681590Srgrimes } 1691590Srgrimes } else 170193488Sbrian if (lines(fp, fn, off)) 17117341Sadam return; 1721590Srgrimes break; 17387712Smarkm default: 17494178Smurray break; 1751590Srgrimes } 1761590Srgrimes 177137225Spaul while ((ch = getc(fp)) != EOF) 178137225Spaul if (putchar(ch) == EOF) 179137225Spaul oerr(); 180137225Spaul if (ferror(fp)) { 181193488Sbrian ierr(fn); 182137225Spaul return; 18359291Sjlemon } 184137225Spaul (void)fflush(stdout); 1851590Srgrimes} 1861590Srgrimes 1871590Srgrimes/* 1881590Srgrimes * rlines -- display the last offset lines of the file. 1891590Srgrimes */ 1901590Srgrimesstatic void 191193488Sbrianrlines(FILE *fp, const char *fn, off_t off, struct stat *sbp) 1921590Srgrimes{ 19374876Sdwmalone struct mapinfo map; 19474876Sdwmalone off_t curoff, size; 19574876Sdwmalone int i; 1961590Srgrimes 1971590Srgrimes if (!(size = sbp->st_size)) 1981590Srgrimes return; 19974876Sdwmalone map.start = NULL; 20074876Sdwmalone map.fd = fileno(fp); 20174876Sdwmalone map.mapoff = map.maxoff = size; 2021590Srgrimes 20374863Sache /* 20474876Sdwmalone * Last char is special, ignore whether newline or not. Note that 20574876Sdwmalone * size == 0 is dealt with above, and size == 1 sets curoff to -1. 20674863Sache */ 20774876Sdwmalone curoff = size - 2; 20874876Sdwmalone while (curoff >= 0) { 20974876Sdwmalone if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 210193488Sbrian ierr(fn); 21174876Sdwmalone return; 21274876Sdwmalone } 21374876Sdwmalone for (i = curoff - map.mapoff; i >= 0; i--) 21474876Sdwmalone if (map.start[i] == '\n' && --off == 0) 21574876Sdwmalone break; 21674876Sdwmalone /* `i' is either the map offset of a '\n', or -1. */ 21774876Sdwmalone curoff = map.mapoff + i; 21874876Sdwmalone if (i >= 0) 21974876Sdwmalone break; 2201590Srgrimes } 22174876Sdwmalone curoff++; 22274876Sdwmalone if (mapprint(&map, curoff, size - curoff) != 0) { 223193488Sbrian ierr(fn); 22474827Sache exit(1); 22574827Sache } 22674827Sache 2271590Srgrimes /* Set the file pointer to reflect the length displayed. */ 22874933Sache if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 229193488Sbrian ierr(fn); 2301590Srgrimes return; 2311590Srgrimes } 23274876Sdwmalone if (map.start != NULL && munmap(map.start, map.maplen)) { 233193488Sbrian ierr(fn); 2341590Srgrimes return; 2351590Srgrimes } 2361590Srgrimes} 237137225Spaul 238193488Sbrianstatic int 239137225Spaulshow(file_info_t *file) 240137225Spaul{ 241193488Sbrian int ch; 242137225Spaul 243193488Sbrian while ((ch = getc(file->fp)) != EOF) { 244193488Sbrian if (last != file && no_files > 1) { 245193488Sbrian if (!qflag) 246251565Sjh printfn(file->file_name, 1); 247193488Sbrian last = file; 248193488Sbrian } 249193488Sbrian if (putchar(ch) == EOF) 250193488Sbrian oerr(); 251137225Spaul } 252193488Sbrian (void)fflush(stdout); 253193488Sbrian if (ferror(file->fp)) { 254193488Sbrian fclose(file->fp); 255193488Sbrian file->fp = NULL; 256193488Sbrian ierr(file->file_name); 257193488Sbrian return 0; 258193488Sbrian } 259193488Sbrian clearerr(file->fp); 260193488Sbrian return 1; 261137225Spaul} 262137225Spaul 263139993Sdwmalonestatic void 264137225Spaulset_events(file_info_t *files) 265137225Spaul{ 266137225Spaul int i, n = 0; 267137225Spaul file_info_t *file; 268137225Spaul struct timespec ts; 269149485Sps struct statfs sf; 270137225Spaul 271137225Spaul ts.tv_sec = 0; 272137225Spaul ts.tv_nsec = 0; 273137225Spaul 274137225Spaul action = USE_KQUEUE; 275137225Spaul for (i = 0, file = files; i < no_files; i++, file++) { 276137225Spaul if (! file->fp) 277137225Spaul continue; 278149485Sps 279149485Sps if (fstatfs(fileno(file->fp), &sf) == 0 && 280149485Sps (sf.f_flags & MNT_LOCAL) == 0) { 281149485Sps action = USE_SLEEP; 282149485Sps return; 283149485Sps } 284149485Sps 285137225Spaul if (Fflag && fileno(file->fp) != STDIN_FILENO) { 286137225Spaul EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 287137225Spaul EV_ADD | EV_ENABLE | EV_CLEAR, 288137225Spaul NOTE_DELETE | NOTE_RENAME, 0, 0); 289137225Spaul n++; 290137225Spaul } 291137225Spaul EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 292137225Spaul EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 293137225Spaul n++; 294137225Spaul } 295137225Spaul 296137225Spaul if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 297137225Spaul action = USE_SLEEP; 298137225Spaul } 299137225Spaul} 300137225Spaul 301141279Sdelphij/* 302141279Sdelphij * follow -- display the file, from an offset, forward. 303141279Sdelphij * 304141279Sdelphij */ 305137225Spaulvoid 306137225Spaulfollow(file_info_t *files, enum STYLE style, off_t off) 307137225Spaul{ 308193488Sbrian int active, ev_change, i, n = -1; 309137225Spaul struct stat sb2; 310137225Spaul file_info_t *file; 311137225Spaul struct timespec ts; 312137225Spaul 313137225Spaul /* Position each of the files */ 314137225Spaul 315137225Spaul file = files; 316137225Spaul active = 0; 317137225Spaul n = 0; 318137225Spaul for (i = 0; i < no_files; i++, file++) { 319137225Spaul if (file->fp) { 320137225Spaul active = 1; 321137225Spaul n++; 322160045Sflz if (no_files > 1 && !qflag) 323251565Sjh printfn(file->file_name, 1); 324193488Sbrian forward(file->fp, file->file_name, style, off, &file->st); 325137225Spaul if (Fflag && fileno(file->fp) != STDIN_FILENO) 326193488Sbrian n++; 327137225Spaul } 328137225Spaul } 329193488Sbrian if (!Fflag && !active) 330137225Spaul return; 331137225Spaul 332141279Sdelphij last = --file; 333141279Sdelphij 334137225Spaul kq = kqueue(); 335137225Spaul if (kq < 0) 336137225Spaul err(1, "kqueue"); 337137225Spaul ev = malloc(n * sizeof(struct kevent)); 338137225Spaul if (! ev) 339137225Spaul err(1, "Couldn't allocate memory for kevents."); 340137225Spaul set_events(files); 341137225Spaul 342137225Spaul for (;;) { 343193488Sbrian ev_change = 0; 344193488Sbrian if (Fflag) { 345193488Sbrian for (i = 0, file = files; i < no_files; i++, file++) { 346193488Sbrian if (!file->fp) { 347193488Sbrian file->fp = fopen(file->file_name, "r"); 348193488Sbrian if (file->fp != NULL && 349193488Sbrian fstat(fileno(file->fp), &file->st) 350193488Sbrian == -1) { 351193488Sbrian fclose(file->fp); 352193488Sbrian file->fp = NULL; 353137225Spaul } 354193488Sbrian if (file->fp != NULL) 355193488Sbrian ev_change++; 356193488Sbrian continue; 357137225Spaul } 358193488Sbrian if (fileno(file->fp) == STDIN_FILENO) 359193488Sbrian continue; 360193488Sbrian if (stat(file->file_name, &sb2) == -1) { 361193488Sbrian if (errno != ENOENT) 362193488Sbrian ierr(file->file_name); 363193488Sbrian show(file); 364224865Sjilles if (file->fp != NULL) { 365224865Sjilles fclose(file->fp); 366224865Sjilles file->fp = NULL; 367224865Sjilles } 368193488Sbrian ev_change++; 369193488Sbrian continue; 370193488Sbrian } 371193488Sbrian 372193488Sbrian if (sb2.st_ino != file->st.st_ino || 373193488Sbrian sb2.st_dev != file->st.st_dev || 374193488Sbrian sb2.st_nlink == 0) { 375193488Sbrian show(file); 376193488Sbrian file->fp = freopen(file->file_name, "r", 377193488Sbrian file->fp); 378193488Sbrian if (file->fp != NULL) 379193488Sbrian memcpy(&file->st, &sb2, 380193488Sbrian sizeof(struct stat)); 381193488Sbrian else if (errno != ENOENT) 382193488Sbrian ierr(file->file_name); 383193488Sbrian ev_change++; 384193488Sbrian } 385137225Spaul } 386137225Spaul } 387137225Spaul 388193488Sbrian for (i = 0, file = files; i < no_files; i++, file++) 389193488Sbrian if (file->fp && !show(file)) 390193488Sbrian ev_change++; 391193488Sbrian 392193488Sbrian if (ev_change) 393193488Sbrian set_events(files); 394193488Sbrian 395137225Spaul switch (action) { 396137225Spaul case USE_KQUEUE: 397137225Spaul ts.tv_sec = 1; 398137225Spaul ts.tv_nsec = 0; 399137225Spaul /* 400137225Spaul * In the -F case we set a timeout to ensure that 401137225Spaul * we re-stat the file at least once every second. 402137225Spaul */ 403137225Spaul n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 404137225Spaul if (n < 0) 405137225Spaul err(1, "kevent"); 406137225Spaul if (n == 0) { 407137225Spaul /* timeout */ 408137225Spaul break; 409137225Spaul } else if (ev->filter == EVFILT_READ && ev->data < 0) { 410193488Sbrian /* file shrank, reposition to end */ 411137225Spaul if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { 412193488Sbrian ierr(file->file_name); 413137225Spaul continue; 414137225Spaul } 415137225Spaul } 416137225Spaul break; 417137225Spaul 418137225Spaul case USE_SLEEP: 419137225Spaul (void) usleep(250000); 420137225Spaul break; 421137225Spaul } 422137225Spaul } 423137225Spaul} 424