Annotation of src/usr.bin/tail/forward.c, Revision 1.27
1.27 ! tedu 1: /* $OpenBSD: forward.c,v 1.26 2009/10/27 23:59:44 deraadt Exp $ */
1.2 niklas 2: /* $NetBSD: forward.c,v 1.7 1996/02/13 16:49:10 ghudson Exp $ */
1.1 deraadt 3:
4: /*-
5: * Copyright (c) 1991, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Edward Sze-Tyan Wang.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
1.15 millert 19: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #include <sys/types.h>
37: #include <sys/stat.h>
1.12 art 38: #include <sys/event.h>
1.1 deraadt 39:
1.4 millert 40: #include <err.h>
1.27 ! tedu 41: #include <errno.h>
1.1 deraadt 42: #include <stdio.h>
1.27 ! tedu 43: #include <stdlib.h>
1.1 deraadt 44: #include <string.h>
1.4 millert 45: #include <unistd.h>
46:
1.1 deraadt 47: #include "extern.h"
48:
1.27 ! tedu 49: static int rlines(struct tailfile *, off_t);
! 50: static int tfqueue(struct tailfile *tf);
! 51: static const struct timespec *tfreopen(struct tailfile *tf);
! 52:
! 53: static int kq = -1;
1.1 deraadt 54:
55: /*
56: * forward -- display the file, from an offset, forward.
57: *
58: * There are eight separate cases for this -- regular and non-regular
59: * files, by bytes or lines and from the beginning or end of the file.
60: *
61: * FBYTES byte offset from the beginning of the file
62: * REG seek
63: * NOREG read, counting bytes
64: *
65: * FLINES line offset from the beginning of the file
66: * REG read, counting lines
67: * NOREG read, counting lines
68: *
69: * RBYTES byte offset from the end of the file
70: * REG seek
71: * NOREG cyclically read characters into a wrap-around buffer
72: *
73: * RLINES
1.16 henning 74: * REG step back until the correct offset is reached.
1.1 deraadt 75: * NOREG cyclically read lines into a wrap-around array of buffers
76: */
77: void
1.27 ! tedu 78: forward(struct tailfile *tf, int nfiles, enum STYLE style, off_t off)
1.1 deraadt 79: {
1.12 art 80: int ch;
1.27 ! tedu 81: struct tailfile *ctf, *ltf;
1.25 landry 82: struct kevent ke;
1.27 ! tedu 83: const struct timespec *ts = NULL;
! 84: int i;
! 85: int nevents;
! 86:
! 87: if (nfiles < 1)
! 88: return;
! 89:
! 90: if ((kq = kqueue()) < 0)
! 91: warn("kqueue");
! 92:
! 93: for (i = 0; i < nfiles; i++) {
! 94: if (nfiles > 1)
! 95: printfname(tf[i].fname);
! 96:
! 97: switch(style) {
! 98: case FBYTES:
! 99: if (off == 0)
! 100: break;
! 101: if (S_ISREG(tf[i].sb.st_mode)) {
! 102: if (tf[i].sb.st_size < off)
! 103: off = tf[i].sb.st_size;
! 104: if (fseeko(tf[i].fp, off, SEEK_SET) == -1) {
! 105: ierr(tf[i].fname);
1.1 deraadt 106: return;
107: }
1.27 ! tedu 108: } else while (off--)
! 109: if ((ch = getc(tf[i].fp)) == EOF) {
! 110: if (ferror(tf[i].fp)) {
! 111: ierr(tf[i].fname);
! 112: return;
! 113: }
! 114: break;
! 115: }
! 116: break;
! 117: case FLINES:
! 118: if (off == 0)
1.1 deraadt 119: break;
1.27 ! tedu 120: for (;;) {
! 121: if ((ch = getc(tf[i].fp)) == EOF) {
! 122: if (ferror(tf[i].fp)) {
! 123: ierr(tf[i].fname);
! 124: return;
! 125: }
! 126: break;
! 127: }
! 128: if (ch == '\n' && !--off)
! 129: break;
1.1 deraadt 130: }
131: break;
1.27 ! tedu 132: case RBYTES:
! 133: if (S_ISREG(tf[i].sb.st_mode)) {
! 134: if (tf[i].sb.st_size >= off &&
! 135: fseeko(tf[i].fp, -off, SEEK_END) == -1) {
! 136: ierr(tf[i].fname);
! 137: return;
! 138: }
! 139: } else if (off == 0) {
! 140: while (getc(tf[i].fp) != EOF)
! 141: ;
! 142: if (ferror(tf[i].fp)) {
! 143: ierr(tf[i].fname);
1.1 deraadt 144: return;
145: }
1.27 ! tedu 146: } else {
! 147: if (bytes(&(tf[i]), off))
! 148: return;
1.1 deraadt 149: }
1.27 ! tedu 150: break;
! 151: case RLINES:
! 152: if (S_ISREG(tf[i].sb.st_mode)) {
! 153: if (!off) {
! 154: if (fseeko(tf[i].fp, (off_t)0,
! 155: SEEK_END) == -1) {
! 156: ierr(tf[i].fname);
! 157: return;
! 158: }
! 159: } else if (rlines(&(tf[i]), off) != 0)
! 160: lines(&(tf[i]), off);
! 161: } else if (off == 0) {
! 162: while (getc(tf[i].fp) != EOF)
! 163: ;
! 164: if (ferror(tf[i].fp)) {
! 165: ierr(tf[i].fname);
1.1 deraadt 166: return;
167: }
1.27 ! tedu 168: } else {
! 169: if (lines(&(tf[i]), off))
! 170: return;
1.1 deraadt 171: }
1.27 ! tedu 172: break;
! 173: default:
! 174: err(1, "Unsupported style");
1.9 ericj 175: }
1.27 ! tedu 176:
! 177: if (tfqueue(&(tf[i])) == -1)
! 178: warn("Unable to follow %s", tf[i].fname);
! 179:
1.1 deraadt 180: }
1.27 ! tedu 181: ltf = &(tf[i-1]);
1.1 deraadt 182:
1.27 ! tedu 183: (void)fflush(stdout);
! 184: if (!fflag || kq < 0)
! 185: return;
! 186:
! 187: while (1) {
! 188: if ((nevents = kevent(kq, NULL, 0, &ke, 1, ts)) <= 0) {
! 189: if (errno == EINTR) {
1.25 landry 190: close(kq);
1.27 ! tedu 191: return;
1.25 landry 192: }
1.12 art 193: }
194:
1.27 ! tedu 195: ctf = ke.udata;
! 196: if (nevents > 0) {
! 197: if (ke.filter == EVFILT_READ) {
! 198: if (ctf != ltf) {
! 199: printfname(ctf->fname);
! 200: ltf = ctf;
! 201: }
! 202: clearerr(ctf->fp);
! 203: while (!feof(ctf->fp) &&
! 204: (ch = getc(ctf->fp)) != EOF) {
! 205: if (putchar(ch) == EOF)
! 206: oerr();
! 207: }
! 208: if (ferror(ctf->fp)) {
! 209: ierr(ctf->fname);
! 210: fclose(ctf->fp);
! 211: warn("Lost file %s", ctf->fname);
! 212: continue;
! 213: }
! 214: (void)fflush(stdout);
! 215: clearerr(ctf->fp);
! 216: } else if (ke.filter == EVFILT_VNODE) {
! 217: if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) {
! 218: /*
! 219: * File was deleted or renamed.
! 220: *
! 221: * Continue to look at it until
! 222: * a new file reappears with
! 223: * the same name.
! 224: */
! 225: (void) tfreopen(ctf);
! 226: } else if (ke.fflags & NOTE_TRUNCATE) {
! 227: warnx("%s has been truncated, "
! 228: "resetting.", ctf->fname);
! 229: fpurge(ctf->fp);
! 230: rewind(ctf->fp);
! 231: }
1.6 millert 232: }
233: }
1.27 ! tedu 234: ts = tfreopen(NULL);
1.1 deraadt 235: }
236: }
237:
238: /*
239: * rlines -- display the last offset lines of the file.
240: */
1.7 millert 241: static int
1.27 ! tedu 242: rlines(struct tailfile *tf, off_t off)
1.1 deraadt 243: {
1.16 henning 244: off_t pos;
245: int ch;
1.1 deraadt 246:
1.27 ! tedu 247: pos = tf->sb.st_size;
1.16 henning 248: if (pos == 0)
1.7 millert 249: return (0);
1.1 deraadt 250:
1.16 henning 251: /*
252: * Position before char.
1.17 otto 253: * Last char is special, ignore it whether newline or not.
1.16 henning 254: */
255: pos -= 2;
256: ch = EOF;
257: for (; off > 0 && pos >= 0; pos--) {
258: /* A seek per char isn't a problem with a smart stdio */
1.27 ! tedu 259: if (fseeko(tf[0].fp, pos, SEEK_SET) == -1) {
! 260: ierr(tf->fname);
1.16 henning 261: return (1);
262: }
1.27 ! tedu 263: if ((ch = getc(tf[0].fp)) == '\n')
1.16 henning 264: off--;
265: else if (ch == EOF) {
1.27 ! tedu 266: if (ferror(tf[0].fp)) {
! 267: ierr(tf->fname);
1.16 henning 268: return (1);
269: }
1.1 deraadt 270: break;
271: }
1.16 henning 272: }
273: /* If we read until start of file, put back last read char */
1.27 ! tedu 274: if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, tf[0].fp) == EOF) {
! 275: ierr(tf->fname);
1.7 millert 276: return (1);
1.1 deraadt 277: }
1.16 henning 278:
1.27 ! tedu 279: while (!feof(tf[0].fp) && (ch = getc(tf[0].fp)) != EOF)
1.16 henning 280: if (putchar(ch) == EOF)
281: oerr();
1.27 ! tedu 282: if (ferror(tf[0].fp)) {
! 283: ierr(tf->fname);
1.7 millert 284: return (1);
1.1 deraadt 285: }
1.7 millert 286:
287: return (0);
1.27 ! tedu 288: }
! 289:
! 290: static int
! 291: tfqueue(struct tailfile *tf)
! 292: {
! 293: struct kevent ke[2];
! 294: int i = 1;
! 295:
! 296: if (kq < 0) {
! 297: errno = EBADF;
! 298: return -1;
! 299: }
! 300:
! 301: EV_SET(&(ke[0]), fileno(tf->fp), EVFILT_READ,
! 302: EV_ENABLE | EV_ADD | EV_CLEAR, 0, 0, tf);
! 303:
! 304: if (S_ISREG(tf->sb.st_mode)) {
! 305: i = 2;
! 306: EV_SET(&(ke[1]), fileno(tf->fp), EVFILT_VNODE,
! 307: EV_ENABLE | EV_ADD | EV_CLEAR,
! 308: NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
! 309: 0, tf);
! 310: }
! 311: if (kevent(kq, ke, i, NULL, 0, NULL) < 0) {
! 312: ierr(tf->fname);
! 313: return -1;
! 314: }
! 315: return 0;
! 316: }
! 317:
! 318: #define AFILESINCR 8
! 319: static const struct timespec *
! 320: tfreopen(struct tailfile *tf) {
! 321: static struct tailfile **reopen = NULL;
! 322: static int nfiles = 0, afiles = 0;
! 323: static const struct timespec ts = {1, 0};
! 324:
! 325: struct stat sb;
! 326: struct tailfile **treopen, *ttf;
! 327: int i;
! 328:
! 329: if (tf && ((stat(tf->fname, &sb) != 0) || sb.st_ino != tf->sb.st_ino)) {
! 330: if (afiles < ++nfiles) {
! 331: afiles += AFILESINCR;
! 332: treopen = reallocarray(reopen, afiles, sizeof(*reopen));
! 333: if (treopen)
! 334: reopen = treopen;
! 335: else
! 336: afiles -= AFILESINCR;
! 337: }
! 338: if (nfiles < afiles)
! 339: reopen[nfiles-1] = tf;
! 340: }
! 341:
! 342: for (i = 0; i < nfiles; i++) {
! 343: ttf = reopen[i];
! 344: if (stat(ttf->fname, &sb) == -1)
! 345: continue;
! 346: if (sb.st_ino != ttf->sb.st_ino) {
! 347: (void) memcpy(&(ttf->sb), &sb, sizeof(ttf->sb));
! 348: ttf->fp = freopen(ttf->fname, "r", ttf->fp);
! 349: if (ttf->fp == NULL)
! 350: ierr(ttf->fname);
! 351: else {
! 352: warnx("%s has been replaced, reopening.",
! 353: ttf->fname);
! 354: tfqueue(ttf);
! 355: }
! 356: }
! 357: reopen[i] = reopen[--nfiles];
! 358: }
! 359:
! 360: return nfiles ? &ts : NULL;
1.1 deraadt 361: }