1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Edward Sze-Tyan Wang. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 39 __FBSDID("$FreeBSD$"); 40 41 #ifndef lint 42 static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 43 #endif 44 45 #include <sys/param.h> 46 #include <sys/mount.h> 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 <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <limits.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 62 #include "extern.h" 63 64 static void rlines(FILE *, off_t, struct stat *); 65 static void show(file_info_t *); 66 static void set_events(file_info_t *files); 67 68 /* defines for inner loop actions */ 69 #define USE_SLEEP 0 70 #define USE_KQUEUE 1 71 #define ADD_EVENTS 2 72 73 struct kevent *ev; 74 int action = USE_SLEEP; 75 int kq; 76 77 static const file_info_t *last; 78 79 /* 80 * forward -- display the file, from an offset, forward. 81 * 82 * There are eight separate cases for this -- regular and non-regular 83 * files, by bytes or lines and from the beginning or end of the file. 84 * 85 * FBYTES byte offset from the beginning of the file 86 * REG seek 87 * NOREG read, counting bytes 88 * 89 * FLINES line offset from the beginning of the file 90 * REG read, counting lines 91 * NOREG read, counting lines 92 * 93 * RBYTES byte offset from the end of the file 94 * REG seek 95 * NOREG cyclically read characters into a wrap-around buffer 96 * 97 * RLINES 98 * REG mmap the file and step back until reach the correct offset. 99 * NOREG cyclically read lines into a wrap-around array of buffers 100 */ 101 void 102 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 103 { 104 int ch; 105 106 switch(style) { 107 case FBYTES: 108 if (off == 0) 109 break; 110 if (S_ISREG(sbp->st_mode)) { 111 if (sbp->st_size < off) 112 off = sbp->st_size; 113 if (fseeko(fp, off, SEEK_SET) == -1) { 114 ierr(); 115 return; 116 } 117 } else while (off--) 118 if ((ch = getc(fp)) == EOF) { 119 if (ferror(fp)) { 120 ierr(); 121 return; 122 } 123 break; 124 } 125 break; 126 case FLINES: 127 if (off == 0) 128 break; 129 for (;;) { 130 if ((ch = getc(fp)) == EOF) { 131 if (ferror(fp)) { 132 ierr(); 133 return; 134 } 135 break; 136 } 137 if (ch == '\n' && !--off) 138 break; 139 } 140 break; 141 case RBYTES: 142 if (S_ISREG(sbp->st_mode)) { 143 if (sbp->st_size >= off && 144 fseeko(fp, -off, SEEK_END) == -1) { 145 ierr(); 146 return; 147 } 148 } else if (off == 0) { 149 while (getc(fp) != EOF); 150 if (ferror(fp)) { 151 ierr(); 152 return; 153 } 154 } else 155 if (bytes(fp, off)) 156 return; 157 break; 158 case RLINES: 159 if (S_ISREG(sbp->st_mode)) 160 if (!off) { 161 if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 162 ierr(); 163 return; 164 } 165 } else 166 rlines(fp, off, sbp); 167 else if (off == 0) { 168 while (getc(fp) != EOF); 169 if (ferror(fp)) { 170 ierr(); 171 return; 172 } 173 } else 174 if (lines(fp, off)) 175 return; 176 break; 177 default: 178 break; 179 } 180 181 while ((ch = getc(fp)) != EOF) 182 if (putchar(ch) == EOF) 183 oerr(); 184 if (ferror(fp)) { 185 ierr(); 186 return; 187 } 188 (void)fflush(stdout); 189 } 190 191 /* 192 * rlines -- display the last offset lines of the file. 193 */ 194 static void 195 rlines(fp, off, sbp) 196 FILE *fp; 197 off_t off; 198 struct stat *sbp; 199 { 200 struct mapinfo map; 201 off_t curoff, size; 202 int i; 203 204 if (!(size = sbp->st_size)) 205 return; 206 map.start = NULL; 207 map.fd = fileno(fp); 208 map.mapoff = map.maxoff = size; 209 210 /* 211 * Last char is special, ignore whether newline or not. Note that 212 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 213 */ 214 curoff = size - 2; 215 while (curoff >= 0) { 216 if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 217 ierr(); 218 return; 219 } 220 for (i = curoff - map.mapoff; i >= 0; i--) 221 if (map.start[i] == '\n' && --off == 0) 222 break; 223 /* `i' is either the map offset of a '\n', or -1. */ 224 curoff = map.mapoff + i; 225 if (i >= 0) 226 break; 227 } 228 curoff++; 229 if (mapprint(&map, curoff, size - curoff) != 0) { 230 ierr(); 231 exit(1); 232 } 233 234 /* Set the file pointer to reflect the length displayed. */ 235 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 236 ierr(); 237 return; 238 } 239 if (map.start != NULL && munmap(map.start, map.maplen)) { 240 ierr(); 241 return; 242 } 243 } 244 245 static void 246 show(file_info_t *file) 247 { 248 int ch; 249 250 while ((ch = getc(file->fp)) != EOF) { 251 if (last != file && no_files > 1) { 252 (void)printf("\n==> %s <==\n", file->file_name); 253 last = file; 254 } 255 if (putchar(ch) == EOF) 256 oerr(); 257 } 258 (void)fflush(stdout); 259 if (ferror(file->fp)) { 260 file->fp = NULL; 261 ierr(); 262 } else 263 clearerr(file->fp); 264 } 265 266 static void 267 set_events(file_info_t *files) 268 { 269 int i, n = 0; 270 file_info_t *file; 271 struct timespec ts; 272 struct statfs sf; 273 274 ts.tv_sec = 0; 275 ts.tv_nsec = 0; 276 277 action = USE_KQUEUE; 278 for (i = 0, file = files; i < no_files; i++, file++) { 279 if (! file->fp) 280 continue; 281 282 if (fstatfs(fileno(file->fp), &sf) == 0 && 283 (sf.f_flags & MNT_LOCAL) == 0) { 284 action = USE_SLEEP; 285 return; 286 } 287 288 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 289 EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 290 EV_ADD | EV_ENABLE | EV_CLEAR, 291 NOTE_DELETE | NOTE_RENAME, 0, 0); 292 n++; 293 } 294 EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 295 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 296 n++; 297 } 298 299 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 300 action = USE_SLEEP; 301 } 302 } 303 304 /* 305 * follow -- display the file, from an offset, forward. 306 * 307 */ 308 void 309 follow(file_info_t *files, enum STYLE style, off_t off) 310 { 311 int active, i, n = -1; 312 struct stat sb2; 313 file_info_t *file; 314 struct timespec ts; 315 316 /* Position each of the files */ 317 318 file = files; 319 active = 0; 320 n = 0; 321 for (i = 0; i < no_files; i++, file++) { 322 if (file->fp) { 323 active = 1; 324 n++; 325 if (no_files > 1) 326 (void)printf("\n==> %s <==\n", file->file_name); 327 forward(file->fp, style, off, &file->st); 328 if (Fflag && fileno(file->fp) != STDIN_FILENO) 329 n++; 330 } 331 } 332 if (! active) 333 return; 334 335 last = --file; 336 337 kq = kqueue(); 338 if (kq < 0) 339 err(1, "kqueue"); 340 ev = malloc(n * sizeof(struct kevent)); 341 if (! ev) 342 err(1, "Couldn't allocate memory for kevents."); 343 set_events(files); 344 345 for (;;) { 346 for (i = 0, file = files; i < no_files; i++, file++) { 347 if (! file->fp) 348 continue; 349 if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) { 350 if (stat(file->file_name, &sb2) != 0) { 351 /* file was rotated, skip it until it reappears */ 352 continue; 353 } 354 if (sb2.st_ino != file->st.st_ino || 355 sb2.st_dev != file->st.st_dev || 356 sb2.st_nlink == 0) { 357 file->fp = freopen(file->file_name, "r", file->fp); 358 if (file->fp == NULL) { 359 ierr(); 360 continue; 361 } else { 362 memcpy(&file->st, &sb2, sizeof(struct stat)); 363 set_events(files); 364 } 365 } 366 } 367 show(file); 368 } 369 370 switch (action) { 371 case USE_KQUEUE: 372 ts.tv_sec = 1; 373 ts.tv_nsec = 0; 374 /* 375 * In the -F case we set a timeout to ensure that 376 * we re-stat the file at least once every second. 377 */ 378 n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 379 if (n < 0) 380 err(1, "kevent"); 381 if (n == 0) { 382 /* timeout */ 383 break; 384 } else if (ev->filter == EVFILT_READ && ev->data < 0) { 385 /* file shrank, reposition to end */ 386 if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { 387 ierr(); 388 continue; 389 } 390 } 391 break; 392 393 case USE_SLEEP: 394 (void) usleep(250000); 395 break; 396 } 397 } 398 } 399