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