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 if (!qflag) 253 (void)printf("\n==> %s <==\n", file->file_name); 254 last = file; 255 } 256 if (putchar(ch) == EOF) 257 oerr(); 258 } 259 (void)fflush(stdout); 260 if (ferror(file->fp)) { 261 file->fp = NULL; 262 ierr(); 263 } else 264 clearerr(file->fp); 265 } 266 267 static void 268 set_events(file_info_t *files) 269 { 270 int i, n = 0; 271 file_info_t *file; 272 struct timespec ts; 273 struct statfs sf; 274 275 ts.tv_sec = 0; 276 ts.tv_nsec = 0; 277 278 action = USE_KQUEUE; 279 for (i = 0, file = files; i < no_files; i++, file++) { 280 if (! file->fp) 281 continue; 282 283 if (fstatfs(fileno(file->fp), &sf) == 0 && 284 (sf.f_flags & MNT_LOCAL) == 0) { 285 action = USE_SLEEP; 286 return; 287 } 288 289 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 290 EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 291 EV_ADD | EV_ENABLE | EV_CLEAR, 292 NOTE_DELETE | NOTE_RENAME, 0, 0); 293 n++; 294 } 295 EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 296 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 297 n++; 298 } 299 300 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 301 action = USE_SLEEP; 302 } 303 } 304 305 /* 306 * follow -- display the file, from an offset, forward. 307 * 308 */ 309 void 310 follow(file_info_t *files, enum STYLE style, off_t off) 311 { 312 int active, i, n = -1; 313 struct stat sb2; 314 file_info_t *file; 315 struct timespec ts; 316 317 /* Position each of the files */ 318 319 file = files; 320 active = 0; 321 n = 0; 322 for (i = 0; i < no_files; i++, file++) { 323 if (file->fp) { 324 active = 1; 325 n++; 326 if (no_files > 1 && !qflag) 327 (void)printf("\n==> %s <==\n", file->file_name); 328 forward(file->fp, style, off, &file->st); 329 if (Fflag && fileno(file->fp) != STDIN_FILENO) 330 n++; 331 } 332 } 333 if (! active) 334 return; 335 336 last = --file; 337 338 kq = kqueue(); 339 if (kq < 0) 340 err(1, "kqueue"); 341 ev = malloc(n * sizeof(struct kevent)); 342 if (! ev) 343 err(1, "Couldn't allocate memory for kevents."); 344 set_events(files); 345 346 for (;;) { 347 for (i = 0, file = files; i < no_files; i++, file++) { 348 if (! file->fp) 349 continue; 350 if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) { 351 if (stat(file->file_name, &sb2) == 0 && 352 (sb2.st_ino != file->st.st_ino || 353 sb2.st_dev != file->st.st_dev || 354 sb2.st_nlink == 0)) { 355 show(file); 356 file->fp = freopen(file->file_name, "r", file->fp); 357 if (file->fp == NULL) { 358 ierr(); 359 continue; 360 } else { 361 memcpy(&file->st, &sb2, sizeof(struct stat)); 362 set_events(files); 363 } 364 } 365 } 366 show(file); 367 } 368 369 switch (action) { 370 case USE_KQUEUE: 371 ts.tv_sec = 1; 372 ts.tv_nsec = 0; 373 /* 374 * In the -F case we set a timeout to ensure that 375 * we re-stat the file at least once every second. 376 */ 377 n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 378 if (n < 0) 379 err(1, "kevent"); 380 if (n == 0) { 381 /* timeout */ 382 break; 383 } else if (ev->filter == EVFILT_READ && ev->data < 0) { 384 /* file shrank, reposition to end */ 385 if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { 386 ierr(); 387 continue; 388 } 389 } 390 break; 391 392 case USE_SLEEP: 393 (void) usleep(250000); 394 break; 395 } 396 } 397 } 398