forward.c revision 137225
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 * 3. All advertising materials mentioning features or use of this software 171590Srgrimes * must display the following acknowledgement: 181590Srgrimes * This product includes software developed by the University of 191590Srgrimes * California, Berkeley and its contributors. 201590Srgrimes * 4. Neither the name of the University nor the names of its contributors 211590Srgrimes * may be used to endorse or promote products derived from this software 221590Srgrimes * without specific prior written permission. 231590Srgrimes * 241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341590Srgrimes * SUCH DAMAGE. 351590Srgrimes */ 361590Srgrimes 3787712Smarkm#include <sys/cdefs.h> 3887712Smarkm 3987712Smarkm__FBSDID("$FreeBSD: head/usr.bin/tail/forward.c 137225 2004-11-04 19:18:19Z paul $"); 4087712Smarkm 411590Srgrimes#ifndef lint 4287712Smarkmstatic const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 4369528Sasmodai#endif 441590Srgrimes 451590Srgrimes#include <sys/types.h> 461590Srgrimes#include <sys/stat.h> 471590Srgrimes#include <sys/time.h> 481590Srgrimes#include <sys/mman.h> 4959373Sjlemon#include <sys/event.h> 501590Srgrimes 5187712Smarkm#include <err.h> 5287712Smarkm#include <errno.h> 5387712Smarkm#include <fcntl.h> 541590Srgrimes#include <limits.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 5887712Smarkm#include <unistd.h> 5987712Smarkm 601590Srgrimes#include "extern.h" 611590Srgrimes 6292922Simpstatic void rlines(FILE *, off_t, struct stat *); 631590Srgrimes 6461964Sjlemon/* defines for inner loop actions */ 6561964Sjlemon#define USE_SLEEP 0 6661964Sjlemon#define USE_KQUEUE 1 6761964Sjlemon#define ADD_EVENTS 2 6861964Sjlemon 69137225Spaulstruct kevent *ev; 70137225Spaulint action = USE_SLEEP; 71137225Spaulint kq; 72137225Spaul 731590Srgrimes/* 741590Srgrimes * forward -- display the file, from an offset, forward. 751590Srgrimes * 761590Srgrimes * There are eight separate cases for this -- regular and non-regular 771590Srgrimes * files, by bytes or lines and from the beginning or end of the file. 781590Srgrimes * 791590Srgrimes * FBYTES byte offset from the beginning of the file 801590Srgrimes * REG seek 811590Srgrimes * NOREG read, counting bytes 821590Srgrimes * 831590Srgrimes * FLINES line offset from the beginning of the file 841590Srgrimes * REG read, counting lines 851590Srgrimes * NOREG read, counting lines 861590Srgrimes * 871590Srgrimes * RBYTES byte offset from the end of the file 881590Srgrimes * REG seek 891590Srgrimes * NOREG cyclically read characters into a wrap-around buffer 901590Srgrimes * 911590Srgrimes * RLINES 921590Srgrimes * REG mmap the file and step back until reach the correct offset. 931590Srgrimes * NOREG cyclically read lines into a wrap-around array of buffers 941590Srgrimes */ 951590Srgrimesvoid 96137157Spaulforward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 971590Srgrimes{ 9886888Siedowse int ch, n, kq = -1; 9961964Sjlemon int action = USE_SLEEP; 10059291Sjlemon struct kevent ev[2]; 10161964Sjlemon struct stat sb2; 10286888Siedowse struct timespec ts; 1031590Srgrimes 1041590Srgrimes switch(style) { 1051590Srgrimes case FBYTES: 1061590Srgrimes if (off == 0) 1071590Srgrimes break; 1081590Srgrimes if (S_ISREG(sbp->st_mode)) { 1091590Srgrimes if (sbp->st_size < off) 1101590Srgrimes off = sbp->st_size; 11182762Sache if (fseeko(fp, off, SEEK_SET) == -1) { 1121590Srgrimes ierr(); 1131590Srgrimes return; 1141590Srgrimes } 1151590Srgrimes } else while (off--) 1161590Srgrimes if ((ch = getc(fp)) == EOF) { 1171590Srgrimes if (ferror(fp)) { 1181590Srgrimes ierr(); 1191590Srgrimes return; 1201590Srgrimes } 1211590Srgrimes break; 1221590Srgrimes } 1231590Srgrimes break; 1241590Srgrimes case FLINES: 1251590Srgrimes if (off == 0) 1261590Srgrimes break; 1271590Srgrimes for (;;) { 1281590Srgrimes if ((ch = getc(fp)) == EOF) { 1291590Srgrimes if (ferror(fp)) { 1301590Srgrimes ierr(); 1311590Srgrimes return; 1321590Srgrimes } 1331590Srgrimes break; 1341590Srgrimes } 1351590Srgrimes if (ch == '\n' && !--off) 1361590Srgrimes break; 1371590Srgrimes } 1381590Srgrimes break; 1391590Srgrimes case RBYTES: 1401590Srgrimes if (S_ISREG(sbp->st_mode)) { 1411590Srgrimes if (sbp->st_size >= off && 14282762Sache fseeko(fp, -off, SEEK_END) == -1) { 1431590Srgrimes ierr(); 1441590Srgrimes return; 1451590Srgrimes } 1461590Srgrimes } else if (off == 0) { 1471590Srgrimes while (getc(fp) != EOF); 1481590Srgrimes if (ferror(fp)) { 1491590Srgrimes ierr(); 1501590Srgrimes return; 1511590Srgrimes } 1521590Srgrimes } else 15317341Sadam if (bytes(fp, off)) 15417341Sadam return; 1551590Srgrimes break; 1561590Srgrimes case RLINES: 1571590Srgrimes if (S_ISREG(sbp->st_mode)) 1581590Srgrimes if (!off) { 15982762Sache if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 1601590Srgrimes ierr(); 1611590Srgrimes return; 1621590Srgrimes } 1631590Srgrimes } else 1641590Srgrimes rlines(fp, off, sbp); 1651590Srgrimes else if (off == 0) { 1661590Srgrimes while (getc(fp) != EOF); 1671590Srgrimes if (ferror(fp)) { 1681590Srgrimes ierr(); 1691590Srgrimes return; 1701590Srgrimes } 1711590Srgrimes } else 17217341Sadam if (lines(fp, off)) 17317341Sadam return; 1741590Srgrimes break; 17587712Smarkm default: 17694178Smurray break; 1771590Srgrimes } 1781590Srgrimes 179137225Spaul while ((ch = getc(fp)) != EOF) 180137225Spaul if (putchar(ch) == EOF) 181137225Spaul oerr(); 182137225Spaul if (ferror(fp)) { 183137225Spaul ierr(); 184137225Spaul return; 18559291Sjlemon } 186137225Spaul (void)fflush(stdout); 1871590Srgrimes} 1881590Srgrimes 1891590Srgrimes/* 1901590Srgrimes * rlines -- display the last offset lines of the file. 1911590Srgrimes */ 1921590Srgrimesstatic void 1931590Srgrimesrlines(fp, off, sbp) 1941590Srgrimes FILE *fp; 19582762Sache off_t off; 1961590Srgrimes struct stat *sbp; 1971590Srgrimes{ 19874876Sdwmalone struct mapinfo map; 19974876Sdwmalone off_t curoff, size; 20074876Sdwmalone int i; 2011590Srgrimes 2021590Srgrimes if (!(size = sbp->st_size)) 2031590Srgrimes return; 20474876Sdwmalone map.start = NULL; 20574876Sdwmalone map.fd = fileno(fp); 20674876Sdwmalone map.mapoff = map.maxoff = size; 2071590Srgrimes 20874863Sache /* 20974876Sdwmalone * Last char is special, ignore whether newline or not. Note that 21074876Sdwmalone * size == 0 is dealt with above, and size == 1 sets curoff to -1. 21174863Sache */ 21274876Sdwmalone curoff = size - 2; 21374876Sdwmalone while (curoff >= 0) { 21474876Sdwmalone if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 21574876Sdwmalone ierr(); 21674876Sdwmalone return; 21774876Sdwmalone } 21874876Sdwmalone for (i = curoff - map.mapoff; i >= 0; i--) 21974876Sdwmalone if (map.start[i] == '\n' && --off == 0) 22074876Sdwmalone break; 22174876Sdwmalone /* `i' is either the map offset of a '\n', or -1. */ 22274876Sdwmalone curoff = map.mapoff + i; 22374876Sdwmalone if (i >= 0) 22474876Sdwmalone break; 2251590Srgrimes } 22674876Sdwmalone curoff++; 22774876Sdwmalone if (mapprint(&map, curoff, size - curoff) != 0) { 22874827Sache ierr(); 22974827Sache exit(1); 23074827Sache } 23174827Sache 2321590Srgrimes /* Set the file pointer to reflect the length displayed. */ 23374933Sache if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 2341590Srgrimes ierr(); 2351590Srgrimes return; 2361590Srgrimes } 23774876Sdwmalone if (map.start != NULL && munmap(map.start, map.maplen)) { 23817833Sadam ierr(); 2391590Srgrimes return; 2401590Srgrimes } 2411590Srgrimes} 242137225Spaul 243137225Spaul/* 244137225Spaul * follow -- display the file, from an offset, forward. 245137225Spaul * 246137225Spaul */ 247137225Spaul 248137225Spaulvoid 249137225Spaulshow(file_info_t *file) 250137225Spaul{ 251137225Spaul int ch, first; 252137225Spaul 253137225Spaul first = 1; 254137225Spaul while ((ch = getc(file->fp)) != EOF) { 255137225Spaul if (first && no_files > 1) { 256137225Spaul (void)printf("\n==> %s <==\n", file->file_name); 257137225Spaul first = 0; 258137225Spaul } 259137225Spaul if (putchar(ch) == EOF) 260137225Spaul oerr(); 261137225Spaul } 262137225Spaul (void)fflush(stdout); 263137225Spaul if (ferror(file->fp)) { 264137225Spaul file->fp = NULL; 265137225Spaul ierr(); 266137225Spaul } else 267137225Spaul clearerr(file->fp); 268137225Spaul} 269137225Spaul 270137225Spaulvoid 271137225Spaulset_events(file_info_t *files) 272137225Spaul{ 273137225Spaul int i, n = 0; 274137225Spaul file_info_t *file; 275137225Spaul struct timespec ts; 276137225Spaul 277137225Spaul ts.tv_sec = 0; 278137225Spaul ts.tv_nsec = 0; 279137225Spaul 280137225Spaul action = USE_KQUEUE; 281137225Spaul for (i = 0, file = files; i < no_files; i++, file++) { 282137225Spaul if (! file->fp) 283137225Spaul continue; 284137225Spaul if (Fflag && fileno(file->fp) != STDIN_FILENO) { 285137225Spaul EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 286137225Spaul EV_ADD | EV_ENABLE | EV_CLEAR, 287137225Spaul NOTE_DELETE | NOTE_RENAME, 0, 0); 288137225Spaul n++; 289137225Spaul } 290137225Spaul EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 291137225Spaul EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 292137225Spaul n++; 293137225Spaul } 294137225Spaul 295137225Spaul if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 296137225Spaul action = USE_SLEEP; 297137225Spaul } 298137225Spaul} 299137225Spaul 300137225Spaulvoid 301137225Spaulfollow(file_info_t *files, enum STYLE style, off_t off) 302137225Spaul{ 303137225Spaul int active, i, n = -1; 304137225Spaul struct stat sb2; 305137225Spaul struct stat *sbp; 306137225Spaul file_info_t *file; 307137225Spaul long spin=1; 308137225Spaul struct timespec ts; 309137225Spaul 310137225Spaul /* Position each of the files */ 311137225Spaul 312137225Spaul file = files; 313137225Spaul active = 0; 314137225Spaul n = 0; 315137225Spaul for (i = 0; i < no_files; i++, file++) { 316137225Spaul if (file->fp) { 317137225Spaul active = 1; 318137225Spaul n++; 319137225Spaul if (no_files > 1) 320137225Spaul (void)printf("\n==> %s <==\n", file->file_name); 321137225Spaul forward(file->fp, style, off, &file->st); 322137225Spaul if (Fflag && fileno(file->fp) != STDIN_FILENO) 323137225Spaul n++; 324137225Spaul } 325137225Spaul } 326137225Spaul if (! active) 327137225Spaul return; 328137225Spaul 329137225Spaul kq = kqueue(); 330137225Spaul if (kq < 0) 331137225Spaul err(1, "kqueue"); 332137225Spaul ev = malloc(n * sizeof(struct kevent)); 333137225Spaul if (! ev) 334137225Spaul err(1, "Couldn't allocate memory for kevents."); 335137225Spaul set_events(files); 336137225Spaul 337137225Spaul for (;;) { 338137225Spaul for (i = 0, file = files; i < no_files; i++, file++) { 339137225Spaul if (! file->fp) 340137225Spaul continue; 341137225Spaul if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) { 342137225Spaul if (stat(file->file_name, &sb2) != 0) { 343137225Spaul /* file was rotated, skip it until it reappears */ 344137225Spaul continue; 345137225Spaul } 346137225Spaul if (sb2.st_ino != file->st.st_ino || 347137225Spaul sb2.st_dev != file->st.st_dev || 348137225Spaul sb2.st_nlink == 0) { 349137225Spaul file->fp = freopen(file->file_name, "r", file->fp); 350137225Spaul if (file->fp == NULL) { 351137225Spaul ierr(); 352137225Spaul continue; 353137225Spaul } else { 354137225Spaul memcpy(&file->st, &sb2, sizeof(struct stat)); 355137225Spaul set_events(files); 356137225Spaul } 357137225Spaul } 358137225Spaul } 359137225Spaul show(file); 360137225Spaul } 361137225Spaul 362137225Spaul switch (action) { 363137225Spaul case USE_KQUEUE: 364137225Spaul ts.tv_sec = 1; 365137225Spaul ts.tv_nsec = 0; 366137225Spaul /* 367137225Spaul * In the -F case we set a timeout to ensure that 368137225Spaul * we re-stat the file at least once every second. 369137225Spaul */ 370137225Spaul n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 371137225Spaul if (n < 0) 372137225Spaul err(1, "kevent"); 373137225Spaul if (n == 0) { 374137225Spaul /* timeout */ 375137225Spaul break; 376137225Spaul } else if (ev->filter == EVFILT_READ && ev->data < 0) { 377137225Spaul /* file shrank, reposition to end */ 378137225Spaul if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { 379137225Spaul ierr(); 380137225Spaul continue; 381137225Spaul } 382137225Spaul } 383137225Spaul break; 384137225Spaul 385137225Spaul case USE_SLEEP: 386137225Spaul (void) usleep(250000); 387137225Spaul break; 388137225Spaul } 389137225Spaul } 390137225Spaul} 391