Home | History | Annotate | Line # | Download | only in tail
forward.c revision 1.28.28.1
      1 /*	$NetBSD: forward.c,v 1.28.28.1 2009/05/13 19:20:07 jym Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1991, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Edward Sze-Tyan Wang.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #ifndef lint
     37 #if 0
     38 static char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
     39 #endif
     40 __RCSID("$NetBSD: forward.c,v 1.28.28.1 2009/05/13 19:20:07 jym Exp $");
     41 #endif /* not lint */
     42 
     43 #include <sys/types.h>
     44 #include <sys/stat.h>
     45 #include <sys/time.h>
     46 #include <sys/mman.h>
     47 #include <sys/event.h>
     48 
     49 #include <limits.h>
     50 #include <fcntl.h>
     51 #include <errno.h>
     52 #include <unistd.h>
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <string.h>
     56 #include "extern.h"
     57 
     58 static int rlines(FILE *, off_t, struct stat *);
     59 
     60 /* defines for inner loop actions */
     61 #define	USE_SLEEP	0
     62 #define	USE_KQUEUE	1
     63 #define	ADD_EVENTS	2
     64 
     65 /*
     66  * forward -- display the file, from an offset, forward.
     67  *
     68  * There are eight separate cases for this -- regular and non-regular
     69  * files, by bytes or lines and from the beginning or end of the file.
     70  *
     71  * FBYTES	byte offset from the beginning of the file
     72  *	REG	seek
     73  *	NOREG	read, counting bytes
     74  *
     75  * FLINES	line offset from the beginning of the file
     76  *	REG	read, counting lines
     77  *	NOREG	read, counting lines
     78  *
     79  * RBYTES	byte offset from the end of the file
     80  *	REG	seek
     81  *	NOREG	cyclically read characters into a wrap-around buffer
     82  *
     83  * RLINES
     84  *	REG	mmap the file and step back until reach the correct offset.
     85  *	NOREG	cyclically read lines into a wrap-around array of buffers
     86  */
     87 void
     88 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
     89 {
     90 	int ch, n;
     91 	int kq=-1, action=USE_SLEEP;
     92 	struct stat statbuf;
     93 	dev_t lastdev;
     94 	ino_t lastino;
     95 	struct kevent ev[2];
     96 
     97 	/* Keep track of file's previous incarnation. */
     98 	lastdev = sbp->st_dev;
     99 	lastino = sbp->st_ino;
    100 
    101 	switch(style) {
    102 	case FBYTES:
    103 		if (off == 0)
    104 			break;
    105 		if (S_ISREG(sbp->st_mode)) {
    106 			if (sbp->st_size < off)
    107 				off = sbp->st_size;
    108 			if (fseeko(fp, off, SEEK_SET) == -1) {
    109 				ierr();
    110 				return;
    111 			}
    112 		} else while (off--)
    113 			if ((ch = getc(fp)) == EOF) {
    114 				if (ferror(fp)) {
    115 					ierr();
    116 					return;
    117 				}
    118 				break;
    119 			}
    120 		break;
    121 	case FLINES:
    122 		if (off == 0)
    123 			break;
    124 		for (;;) {
    125 			if ((ch = getc(fp)) == EOF) {
    126 				if (ferror(fp)) {
    127 					ierr();
    128 					return;
    129 				}
    130 				break;
    131 			}
    132 			if (ch == '\n' && !--off)
    133 				break;
    134 		}
    135 		break;
    136 	case RBYTES:
    137 		if (S_ISREG(sbp->st_mode)) {
    138 			if (sbp->st_size >= off &&
    139 			    fseeko(fp, -off, SEEK_END) == -1) {
    140 				ierr();
    141 				return;
    142 			}
    143 		} else if (off == 0) {
    144 			while (getc(fp) != EOF);
    145 			if (ferror(fp)) {
    146 				ierr();
    147 				return;
    148 			}
    149 		} else {
    150 			if (displaybytes(fp, off))
    151 				return;
    152 		}
    153 		break;
    154 	case RLINES:
    155 		if (S_ISREG(sbp->st_mode)) {
    156 			if (!off) {
    157 				if (fseek(fp, 0L, SEEK_END) == -1) {
    158 					ierr();
    159 					return;
    160 				}
    161 			} else {
    162 				if (rlines(fp, off, sbp))
    163 					return;
    164 			}
    165 		} else if (off == 0) {
    166 			while (getc(fp) != EOF);
    167 			if (ferror(fp)) {
    168 				ierr();
    169 				return;
    170 			}
    171 		} else {
    172 			if (displaylines(fp, off))
    173 				return;
    174 		}
    175 		break;
    176 	default:
    177 		break;
    178 	}
    179 
    180 	if (fflag) {
    181 		kq = kqueue();
    182 		if (kq < 0)
    183 			err(1, "kqueue");
    184 		action = ADD_EVENTS;
    185 	}
    186 
    187 	for (;;) {
    188 		while ((ch = getc(fp)) != EOF)  {
    189 			if (putchar(ch) == EOF)
    190 				oerr();
    191 		}
    192 		if (ferror(fp)) {
    193 			ierr();
    194 			return;
    195 		}
    196 		(void)fflush(stdout);
    197 		if (!fflag)
    198 			break;
    199 
    200 		clearerr(fp);
    201 
    202 		switch (action) {
    203 		case ADD_EVENTS:
    204 			n = 0;
    205 
    206 			memset(ev, 0, sizeof(ev));
    207 			if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
    208 				EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
    209 					EV_ADD | EV_ENABLE | EV_CLEAR,
    210 					NOTE_DELETE | NOTE_RENAME, 0, 0);
    211 				n++;
    212 			}
    213 			EV_SET(&ev[n], fileno(fp), EVFILT_READ,
    214 				EV_ADD | EV_ENABLE, 0, 0, 0);
    215 			n++;
    216 
    217 			if (kevent(kq, ev, n, NULL, 0, NULL) < 0) {
    218 				close(kq);
    219 				kq = -1;
    220 				action = USE_SLEEP;
    221 			} else {
    222 				action = USE_KQUEUE;
    223 			}
    224 			break;
    225 
    226 		case USE_KQUEUE:
    227 			if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
    228 				err(1, "kevent");
    229 
    230 			if (ev[0].filter == EVFILT_VNODE) {
    231 				/* file was rotated, wait until it reappears */
    232 				action = USE_SLEEP;
    233 			} else if (ev[0].data < 0) {
    234 				/* file shrank, reposition to end */
    235 				if (fseek(fp, 0L, SEEK_END) == -1) {
    236 					ierr();
    237 					return;
    238 				}
    239 			}
    240 			break;
    241 
    242 		case USE_SLEEP:
    243 			/*
    244 			 * We pause for one second after displaying any data
    245 			 * that has accumulated since we read the file.
    246 			 */
    247                 	(void) sleep(1);
    248 
    249 			if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
    250 			    stat(fname, &statbuf) != -1) {
    251 				if (statbuf.st_ino != sbp->st_ino ||
    252 				    statbuf.st_dev != sbp->st_dev ||
    253 				    statbuf.st_rdev != sbp->st_rdev ||
    254 				    statbuf.st_nlink == 0) {
    255 					fp = freopen(fname, "r", fp);
    256 					if (fp == NULL) {
    257 						ierr();
    258 						goto out;
    259 					}
    260 					*sbp = statbuf;
    261 					if (kq != -1)
    262 						action = ADD_EVENTS;
    263 				} else if (kq != -1)
    264 					action = USE_KQUEUE;
    265 			}
    266 			break;
    267 		}
    268 	}
    269 out:
    270 	if (fflag && kq != -1)
    271 		close(kq);
    272 }
    273 
    274 /*
    275  * rlines -- display the last offset lines of the file.
    276  *
    277  * Non-zero return means than a (non-fatal) error occurred.
    278  */
    279 static int
    280 rlines(FILE *fp, off_t off, struct stat *sbp)
    281 {
    282 	off_t file_size;
    283 	off_t file_remaining;
    284 	char *p = NULL;
    285 	char *start = NULL;
    286 	off_t mmap_size;
    287 	off_t mmap_offset;
    288 	off_t mmap_remaining = 0;
    289 
    290 #define MMAP_MAXSIZE  (10 * 1024 * 1024)
    291 
    292 	if (!(file_size = sbp->st_size))
    293 		return (0);
    294 	file_remaining = file_size;
    295 
    296 	if (file_remaining > MMAP_MAXSIZE) {
    297 		mmap_size = MMAP_MAXSIZE;
    298 		mmap_offset = file_remaining - MMAP_MAXSIZE;
    299 	} else {
    300 		mmap_size = file_remaining;
    301 		mmap_offset = 0;
    302 	}
    303 
    304 	while (off) {
    305 		start = mmap(NULL, (size_t)mmap_size, PROT_READ,
    306 			     MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
    307 		if (start == MAP_FAILED) {
    308 			err(0, "%s: %s", fname, strerror(EFBIG));
    309 			return (1);
    310 		}
    311 
    312 		mmap_remaining = mmap_size;
    313 		/* Last char is special, ignore whether newline or not. */
    314 		for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
    315 			if (*--p == '\n' && !--off) {
    316 				++p;
    317 				break;
    318 			}
    319 
    320 		file_remaining -= mmap_size - mmap_remaining;
    321 
    322 		if (off == 0)
    323 			break;
    324 
    325 		if (file_remaining == 0)
    326 			break;
    327 
    328 		if (munmap(start, mmap_size)) {
    329 			err(0, "%s: %s", fname, strerror(errno));
    330 			return (1);
    331 		}
    332 
    333 		if (mmap_offset >= MMAP_MAXSIZE) {
    334 			mmap_offset -= MMAP_MAXSIZE;
    335 		} else {
    336 			mmap_offset = 0;
    337 			mmap_size = file_remaining;
    338 		}
    339 	}
    340 
    341 	/*
    342 	 * Output the (perhaps partial) data in this mmap'd block.
    343 	 */
    344 	WR(p, mmap_size - mmap_remaining);
    345 	file_remaining += mmap_size - mmap_remaining;
    346 	if (munmap(start, mmap_size)) {
    347 		err(0, "%s: %s", fname, strerror(errno));
    348 		return (1);
    349 	}
    350 
    351 	/*
    352 	 * Set the file pointer to reflect the length displayed.
    353 	 * This will cause the caller to redisplay the data if/when
    354 	 * needed.
    355 	 */
    356 	if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
    357 		ierr();
    358 		return (1);
    359 	}
    360 	return (0);
    361 }
    362