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.3 2003/10/04 20:36:51 hmp 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 /* 65 * forward -- display the file, from an offset, forward. 66 * 67 * There are eight separate cases for this -- regular and non-regular 68 * files, by bytes or lines and from the beginning or end of the file. 69 * 70 * FBYTES byte offset from the beginning of the file 71 * REG seek 72 * NOREG read, counting bytes 73 * 74 * FLINES line offset from the beginning of the file 75 * REG read, counting lines 76 * NOREG read, counting lines 77 * 78 * RBYTES byte offset from the end of the file 79 * REG seek 80 * NOREG cyclically read characters into a wrap-around buffer 81 * 82 * RLINES 83 * REG mmap the file and step back until reach the correct offset. 84 * NOREG cyclically read lines into a wrap-around array of buffers 85 */ 86 void 87 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 88 { 89 int ch, n, kq = -1; 90 int action = USE_SLEEP; 91 struct kevent ev[2]; 92 struct stat sb2; 93 struct timespec ts; 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 if (fflag) { 169 kq = kqueue(); 170 if (kq < 0) 171 err(1, "kqueue"); 172 action = ADD_EVENTS; 173 } 174 175 for (;;) { 176 while ((ch = getc(fp)) != EOF) 177 if (putchar(ch) == EOF) 178 oerr(); 179 if (ferror(fp)) { 180 ierr(); 181 return; 182 } 183 (void)fflush(stdout); 184 if (! fflag) 185 break; 186 clearerr(fp); 187 188 switch (action) { 189 case ADD_EVENTS: 190 n = 0; 191 ts.tv_sec = 0; 192 ts.tv_nsec = 0; 193 194 if (Fflag && fileno(fp) != STDIN_FILENO) { 195 EV_SET(&ev[n], fileno(fp), EVFILT_VNODE, 196 EV_ADD | EV_ENABLE | EV_CLEAR, 197 NOTE_DELETE | NOTE_RENAME, 0, 0); 198 n++; 199 } 200 EV_SET(&ev[n], fileno(fp), EVFILT_READ, 201 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 202 n++; 203 204 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { 205 action = USE_SLEEP; 206 } else { 207 action = USE_KQUEUE; 208 } 209 break; 210 211 case USE_KQUEUE: 212 ts.tv_sec = 1; 213 ts.tv_nsec = 0; 214 /* 215 * In the -F case we set a timeout to ensure that 216 * we re-stat the file at least once every second. 217 */ 218 n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); 219 if (n < 0) 220 err(1, "kevent"); 221 if (n == 0) { 222 /* timeout */ 223 break; 224 } else if (ev->filter == EVFILT_READ && ev->data < 0) { 225 /* file shrank, reposition to end */ 226 if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 227 ierr(); 228 return; 229 } 230 } 231 break; 232 233 case USE_SLEEP: 234 (void) usleep(250000); 235 clearerr(fp); 236 break; 237 } 238 239 if (Fflag && fileno(fp) != STDIN_FILENO) { 240 while (stat(fname, &sb2) != 0) 241 /* file was rotated, wait until it reappears */ 242 (void)sleep(1); 243 if (sb2.st_ino != sbp->st_ino || 244 sb2.st_dev != sbp->st_dev || 245 sb2.st_rdev != sbp->st_rdev || 246 sb2.st_nlink == 0) { 247 fp = freopen(fname, "r", fp); 248 if (fp == NULL) { 249 ierr(); 250 return; 251 } else { 252 *sbp = sb2; 253 action = ADD_EVENTS; 254 } 255 } 256 } 257 } 258 } 259 260 /* 261 * rlines -- display the last offset lines of the file. 262 */ 263 static void 264 rlines(FILE *fp, off_t off, struct stat *sbp) 265 { 266 struct mapinfo map; 267 off_t curoff, size; 268 int i; 269 270 if (!(size = sbp->st_size)) 271 return; 272 map.start = NULL; 273 map.fd = fileno(fp); 274 map.mapoff = map.maxoff = size; 275 276 /* 277 * Last char is special, ignore whether newline or not. Note that 278 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 279 */ 280 curoff = size - 2; 281 while (curoff >= 0) { 282 if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 283 ierr(); 284 return; 285 } 286 for (i = curoff - map.mapoff; i >= 0; i--) 287 if (map.start[i] == '\n' && --off == 0) 288 break; 289 /* `i' is either the map offset of a '\n', or -1. */ 290 curoff = map.mapoff + i; 291 if (i >= 0) 292 break; 293 } 294 curoff++; 295 if (mapprint(&map, curoff, size - curoff) != 0) { 296 ierr(); 297 exit(1); 298 } 299 300 /* Set the file pointer to reflect the length displayed. */ 301 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 302 ierr(); 303 return; 304 } 305 if (map.start != NULL && munmap(map.start, map.maplen)) { 306 ierr(); 307 return; 308 } 309 } 310