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