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 * @(#)forward.c 8.1 (Berkeley) 6/6/93 37 * $FreeBSD: src/usr.bin/tail/forward.c,v 1.11.6.7 2003/01/07 05:26:22 tjr Exp $ 38 * $DragonFly: src/usr.bin/tail/forward.c,v 1.6 2005/03/01 21:37:33 cpressey Exp $ 39 */ 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/time.h> 44 #include <sys/mman.h> 45 #include <sys/event.h> 46 47 #include <limits.h> 48 #include <fcntl.h> 49 #include <errno.h> 50 #include <unistd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <err.h> 55 #include "extern.h" 56 57 static void rlines(FILE *, off_t, struct stat *); 58 59 /* defines for inner loop actions */ 60 #define USE_SLEEP 0 61 #define USE_KQUEUE 1 62 #define ADD_EVENTS 2 63 64 struct kevent *ev; 65 int action = USE_SLEEP; 66 int kq; 67 68 /* 69 * forward -- display the file, from an offset, forward. 70 * 71 * There are eight separate cases for this -- regular and non-regular 72 * files, by bytes or lines and from the beginning or end of the file. 73 * 74 * FBYTES byte offset from the beginning of the file 75 * REG seek 76 * NOREG read, counting bytes 77 * 78 * FLINES line offset from the beginning of the file 79 * REG read, counting lines 80 * NOREG read, counting lines 81 * 82 * RBYTES byte offset from the end of the file 83 * REG seek 84 * NOREG cyclically read characters into a wrap-around buffer 85 * 86 * RLINES 87 * REG mmap the file and step back until reach the correct offset. 88 * NOREG cyclically read lines into a wrap-around array of buffers 89 */ 90 void 91 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 92 { 93 int ch; 94 95 switch(style) { 96 case FBYTES: 97 if (off == 0) 98 break; 99 if (S_ISREG(sbp->st_mode)) { 100 if (sbp->st_size < off) 101 off = sbp->st_size; 102 if (fseeko(fp, off, SEEK_SET) == -1) { 103 ierr(); 104 return; 105 } 106 } else while (off--) 107 if ((ch = getc(fp)) == EOF) { 108 if (ferror(fp)) { 109 ierr(); 110 return; 111 } 112 break; 113 } 114 break; 115 case FLINES: 116 if (off == 0) 117 break; 118 for (;;) { 119 if ((ch = getc(fp)) == EOF) { 120 if (ferror(fp)) { 121 ierr(); 122 return; 123 } 124 break; 125 } 126 if (ch == '\n' && !--off) 127 break; 128 } 129 break; 130 case RBYTES: 131 if (S_ISREG(sbp->st_mode)) { 132 if (sbp->st_size >= off && 133 fseeko(fp, -off, SEEK_END) == -1) { 134 ierr(); 135 return; 136 } 137 } else if (off == 0) { 138 while (getc(fp) != EOF); 139 if (ferror(fp)) { 140 ierr(); 141 return; 142 } 143 } else 144 if (display_bytes(fp, off)) 145 return; 146 break; 147 case RLINES: 148 if (S_ISREG(sbp->st_mode)) 149 if (!off) { 150 if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 151 ierr(); 152 return; 153 } 154 } else 155 rlines(fp, off, sbp); 156 else if (off == 0) { 157 while (getc(fp) != EOF); 158 if (ferror(fp)) { 159 ierr(); 160 return; 161 } 162 } else 163 if (display_lines(fp, off)) 164 return; 165 break; 166 case REVERSE: 167 errx(1, "internal error: forward style cannot be REVERSE"); 168 /* NOTREACHED */ 169 } 170 171 while ((ch = getc(fp)) != EOF) { 172 if (putchar(ch) == EOF) 173 oerr(); 174 } 175 if (ferror(fp)) { 176 ierr(); 177 return; 178 } 179 fflush(stdout); 180 } 181 182 /* 183 * rlines -- display the last offset lines of the file. 184 */ 185 static void 186 rlines(FILE *fp, off_t off, struct stat *sbp) 187 { 188 struct mapinfo map; 189 off_t curoff, size; 190 int i; 191 192 if (!(size = sbp->st_size)) 193 return; 194 map.start = NULL; 195 map.fd = fileno(fp); 196 map.mapoff = map.maxoff = size; 197 198 /* 199 * Last char is special, ignore whether newline or not. Note that 200 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 201 */ 202 curoff = size - 2; 203 while (curoff >= 0) { 204 if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 205 ierr(); 206 return; 207 } 208 for (i = curoff - map.mapoff; i >= 0; i--) 209 if (map.start[i] == '\n' && --off == 0) 210 break; 211 /* `i' is either the map offset of a '\n', or -1. */ 212 curoff = map.mapoff + i; 213 if (i >= 0) 214 break; 215 } 216 curoff++; 217 if (mapprint(&map, curoff, size - curoff) != 0) { 218 ierr(); 219 exit(1); 220 } 221 222 /* Set the file pointer to reflect the length displayed. */ 223 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 224 ierr(); 225 return; 226 } 227 if (map.start != NULL && munmap(map.start, map.maplen)) { 228 ierr(); 229 return; 230 } 231 } 232 233 /* 234 * follow -- display the file, from an offset, forward. 235 */ 236 237 static void 238 show(file_info_t *file, int at_index) 239 { 240 int ch, first; 241 242 first = 1; 243 while ((ch = getc(file->fp)) != EOF) { 244 if (first && no_files > 1) { 245 showfilename(at_index, file->file_name); 246 first = 0; 247 } 248 if (putchar(ch) == EOF) 249 oerr(); 250 } 251 fflush(stdout); 252 if (ferror(file->fp)) { 253 file->fp = NULL; 254 ierr(); 255 } else { 256 clearerr(file->fp); 257 } 258 } 259 260 void 261 showfilename(int at_index, const char *filename) 262 { 263 static int last_index = -1; 264 static int continuing = 0; 265 266 if (last_index == at_index) 267 return; 268 if (continuing) 269 printf("\n"); 270 printf("==> %s <==\n", filename); 271 continuing = 1; 272 last_index = at_index; 273 } 274 275 static void 276 set_events(file_info_t *files) 277 { 278 int i, n; 279 file_info_t *file; 280 struct timespec ts; 281 282 ts.tv_sec = 0; 283 ts.tv_nsec = 0; 284 285 n = 0; 286 action = USE_KQUEUE; 287 for (i = 0, file = files; i < no_files; i++, file++) { 288 if (file->fp == NULL) 289 continue; 290 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 291 EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 292 EV_ADD | EV_ENABLE | EV_CLEAR, 293 NOTE_DELETE | NOTE_RENAME, 0, 0); 294 n++; 295 } 296 EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 297 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 298 n++; 299 } 300 301 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) 302 action = USE_SLEEP; 303 } 304 305 void 306 follow(file_info_t *files, enum STYLE style, off_t off) 307 { 308 int active, i, n; 309 long spin; 310 file_info_t *file; 311 struct stat sb2; 312 struct timespec ts; 313 314 spin = 1; 315 316 /* Position each of the files */ 317 file = files; 318 active = 0; 319 n = 0; 320 for (i = 0; i < no_files; i++, file++) { 321 if (file->fp) { 322 active = 1; 323 n++; 324 if (no_files > 1) 325 showfilename(i, file->file_name); 326 forward(file->fp, style, off, &file->st); 327 if (Fflag && fileno(file->fp) != STDIN_FILENO) 328 n++; 329 } 330 } 331 332 if (!active) 333 return; 334 335 kq = kqueue(); 336 if (kq == -1) 337 err(1, "kqueue"); 338 ev = malloc(n * sizeof(struct kevent)); 339 if (ev == NULL) 340 err(1, "Couldn't allocate memory for kevents."); 341 set_events(files); 342 343 for (;;) { 344 for (i = 0, file = files; i < no_files; i++, file++) { 345 if (file->fp == NULL) 346 continue; 347 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 348 if (stat(file->file_name, &sb2) == -1) { 349 /* 350 * file was rotated, skip it until it 351 * reappears. 352 */ 353 continue; 354 } 355 if (sb2.st_ino != file->st.st_ino || 356 sb2.st_dev != file->st.st_dev || 357 sb2.st_nlink == 0) { 358 file->fp = freopen(file->file_name, "r", 359 file->fp); 360 if (file->fp == NULL) { 361 ierr(); 362 continue; 363 } else { 364 memcpy(&file->st, &sb2, 365 sizeof(struct stat)); 366 set_events(files); 367 } 368 } 369 } 370 show(file, i); 371 } 372 373 switch (action) { 374 case USE_KQUEUE: 375 ts.tv_sec = 1; 376 ts.tv_nsec = 0; 377 /* 378 * In the -F case, we set a timeout to ensure that 379 * we re-stat the file at least once every second. 380 */ 381 n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 382 if (n == -1) 383 err(1, "kevent"); 384 if (n == 0) { 385 /* timeout */ 386 break; 387 } else if (ev->filter == EVFILT_READ && ev->data < 0) { 388 /* file shrank, reposition to end */ 389 if (lseek(ev->ident, 0, SEEK_END) == -1) { 390 ierr(); 391 continue; 392 } 393 } 394 break; 395 396 case USE_SLEEP: 397 usleep(250000); 398 break; 399 } 400 } 401 } 402