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