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