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.5 2004/12/27 21:06:39 dillon 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 (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 (lines(fp, off)) 164 return; 165 break; 166 } 167 168 while ((ch = getc(fp)) != EOF) { 169 if (putchar(ch) == EOF) 170 oerr(); 171 } 172 if (ferror(fp)) { 173 ierr(); 174 return; 175 } 176 fflush(stdout); 177 } 178 179 /* 180 * rlines -- display the last offset lines of the file. 181 */ 182 static void 183 rlines(FILE *fp, off_t off, struct stat *sbp) 184 { 185 struct mapinfo map; 186 off_t curoff, size; 187 int i; 188 189 if (!(size = sbp->st_size)) 190 return; 191 map.start = NULL; 192 map.fd = fileno(fp); 193 map.mapoff = map.maxoff = size; 194 195 /* 196 * Last char is special, ignore whether newline or not. Note that 197 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 198 */ 199 curoff = size - 2; 200 while (curoff >= 0) { 201 if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 202 ierr(); 203 return; 204 } 205 for (i = curoff - map.mapoff; i >= 0; i--) 206 if (map.start[i] == '\n' && --off == 0) 207 break; 208 /* `i' is either the map offset of a '\n', or -1. */ 209 curoff = map.mapoff + i; 210 if (i >= 0) 211 break; 212 } 213 curoff++; 214 if (mapprint(&map, curoff, size - curoff) != 0) { 215 ierr(); 216 exit(1); 217 } 218 219 /* Set the file pointer to reflect the length displayed. */ 220 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 221 ierr(); 222 return; 223 } 224 if (map.start != NULL && munmap(map.start, map.maplen)) { 225 ierr(); 226 return; 227 } 228 } 229 230 /* 231 * follow -- display the file, from an offset, forward. 232 */ 233 234 static void 235 show(file_info_t *file, int index) 236 { 237 int ch, first; 238 239 first = 1; 240 while ((ch = getc(file->fp)) != EOF) { 241 if (first && no_files > 1) { 242 showfilename(index, file->file_name); 243 first = 0; 244 } 245 if (putchar(ch) == EOF) 246 oerr(); 247 } 248 fflush(stdout); 249 if (ferror(file->fp)) { 250 file->fp = NULL; 251 ierr(); 252 } else { 253 clearerr(file->fp); 254 } 255 } 256 257 void 258 showfilename(int index, const char *filename) 259 { 260 static int last_index = -1; 261 static int continuing = 0; 262 263 if (last_index == index) 264 return; 265 if (continuing) 266 printf("\n"); 267 printf("==> %s <==\n", filename); 268 continuing = 1; 269 last_index = index; 270 } 271 272 static void 273 set_events(file_info_t *files) 274 { 275 int i, n; 276 file_info_t *file; 277 struct timespec ts; 278 279 ts.tv_sec = 0; 280 ts.tv_nsec = 0; 281 282 n = 0; 283 action = USE_KQUEUE; 284 for (i = 0, file = files; i < no_files; i++, file++) { 285 if (file->fp == NULL) 286 continue; 287 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 288 EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, 289 EV_ADD | EV_ENABLE | EV_CLEAR, 290 NOTE_DELETE | NOTE_RENAME, 0, 0); 291 n++; 292 } 293 EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, 294 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 295 n++; 296 } 297 298 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) 299 action = USE_SLEEP; 300 } 301 302 void 303 follow(file_info_t *files, enum STYLE style, off_t off) 304 { 305 int active, i, n; 306 long spin; 307 file_info_t *file; 308 struct stat sb2; 309 struct timespec ts; 310 311 spin = 1; 312 313 /* Position each of the files */ 314 file = files; 315 active = 0; 316 n = 0; 317 for (i = 0; i < no_files; i++, file++) { 318 if (file->fp) { 319 active = 1; 320 n++; 321 if (no_files > 1) 322 showfilename(i, file->file_name); 323 forward(file->fp, style, off, &file->st); 324 if (Fflag && fileno(file->fp) != STDIN_FILENO) 325 n++; 326 } 327 } 328 329 if (!active) 330 return; 331 332 kq = kqueue(); 333 if (kq == -1) 334 err(1, "kqueue"); 335 ev = malloc(n * sizeof(struct kevent)); 336 if (ev == NULL) 337 err(1, "Couldn't allocate memory for kevents."); 338 set_events(files); 339 340 for (;;) { 341 for (i = 0, file = files; i < no_files; i++, file++) { 342 if (file->fp == NULL) 343 continue; 344 if (Fflag && fileno(file->fp) != STDIN_FILENO) { 345 if (stat(file->file_name, &sb2) == -1) { 346 /* 347 * file was rotated, skip it until it 348 * reappears. 349 */ 350 continue; 351 } 352 if (sb2.st_ino != file->st.st_ino || 353 sb2.st_dev != file->st.st_dev || 354 sb2.st_nlink == 0) { 355 file->fp = freopen(file->file_name, "r", 356 file->fp); 357 if (file->fp == NULL) { 358 ierr(); 359 continue; 360 } else { 361 memcpy(&file->st, &sb2, 362 sizeof(struct stat)); 363 set_events(files); 364 } 365 } 366 } 367 show(file, i); 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 == -1) 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, 0, SEEK_END) == -1) { 387 ierr(); 388 continue; 389 } 390 } 391 break; 392 393 case USE_SLEEP: 394 usleep(250000); 395 break; 396 } 397 } 398 } 399