Annotation of src/usr.bin/tail/forward.c, Revision 1.32
1.32 ! martijn 1: /* $OpenBSD: forward.c,v 1.31 2016/07/05 05:06:27 jsg 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);
1.29 tedu 50: static inline void tfprint(FILE *fp);
1.27 tedu 51: static int tfqueue(struct tailfile *tf);
52: static const struct timespec *tfreopen(struct tailfile *tf);
53:
54: static int kq = -1;
1.1 deraadt 55:
56: /*
57: * forward -- display the file, from an offset, forward.
58: *
59: * There are eight separate cases for this -- regular and non-regular
60: * files, by bytes or lines and from the beginning or end of the file.
61: *
62: * FBYTES byte offset from the beginning of the file
63: * REG seek
64: * NOREG read, counting bytes
65: *
66: * FLINES line offset from the beginning of the file
67: * REG read, counting lines
68: * NOREG read, counting lines
69: *
70: * RBYTES byte offset from the end of the file
71: * REG seek
72: * NOREG cyclically read characters into a wrap-around buffer
73: *
74: * RLINES
1.16 henning 75: * REG step back until the correct offset is reached.
1.1 deraadt 76: * NOREG cyclically read lines into a wrap-around array of buffers
77: */
78: void
1.28 tedu 79: forward(struct tailfile *tf, int nfiles, enum STYLE style, off_t origoff)
1.1 deraadt 80: {
1.12 art 81: int ch;
1.27 tedu 82: struct tailfile *ctf, *ltf;
1.25 landry 83: struct kevent ke;
1.27 tedu 84: const struct timespec *ts = NULL;
85: int i;
86: int nevents;
87:
88: if (nfiles < 1)
89: return;
90:
1.29 tedu 91: if (fflag && (kq = kqueue()) < 0)
1.27 tedu 92: warn("kqueue");
93:
94: for (i = 0; i < nfiles; i++) {
1.28 tedu 95: off_t off = origoff;
1.27 tedu 96: if (nfiles > 1)
97: printfname(tf[i].fname);
98:
99: switch(style) {
100: case FBYTES:
101: if (off == 0)
102: break;
103: if (S_ISREG(tf[i].sb.st_mode)) {
104: if (tf[i].sb.st_size < off)
105: off = tf[i].sb.st_size;
106: if (fseeko(tf[i].fp, off, SEEK_SET) == -1) {
107: ierr(tf[i].fname);
1.1 deraadt 108: return;
109: }
1.27 tedu 110: } else while (off--)
111: if ((ch = getc(tf[i].fp)) == EOF) {
112: if (ferror(tf[i].fp)) {
113: ierr(tf[i].fname);
114: return;
115: }
116: break;
117: }
118: break;
119: case FLINES:
120: if (off == 0)
1.1 deraadt 121: break;
1.27 tedu 122: for (;;) {
123: if ((ch = getc(tf[i].fp)) == EOF) {
124: if (ferror(tf[i].fp)) {
125: ierr(tf[i].fname);
126: return;
127: }
128: break;
129: }
1.29 tedu 130: if (ch == '\n' && !--off)
1.27 tedu 131: break;
1.1 deraadt 132: }
133: break;
1.27 tedu 134: case RBYTES:
135: if (S_ISREG(tf[i].sb.st_mode)) {
136: if (tf[i].sb.st_size >= off &&
137: fseeko(tf[i].fp, -off, SEEK_END) == -1) {
138: ierr(tf[i].fname);
139: return;
140: }
141: } else if (off == 0) {
142: while (getc(tf[i].fp) != EOF)
143: ;
144: if (ferror(tf[i].fp)) {
145: ierr(tf[i].fname);
1.1 deraadt 146: return;
147: }
1.27 tedu 148: } else {
149: if (bytes(&(tf[i]), off))
150: return;
1.1 deraadt 151: }
1.27 tedu 152: break;
153: case RLINES:
154: if (S_ISREG(tf[i].sb.st_mode)) {
155: if (!off) {
156: if (fseeko(tf[i].fp, (off_t)0,
157: SEEK_END) == -1) {
158: ierr(tf[i].fname);
159: return;
160: }
161: } else if (rlines(&(tf[i]), off) != 0)
162: lines(&(tf[i]), off);
163: } else if (off == 0) {
164: while (getc(tf[i].fp) != EOF)
165: ;
166: if (ferror(tf[i].fp)) {
167: ierr(tf[i].fname);
1.1 deraadt 168: return;
169: }
1.27 tedu 170: } else {
171: if (lines(&(tf[i]), off))
172: return;
1.1 deraadt 173: }
1.27 tedu 174: break;
175: default:
176: err(1, "Unsupported style");
1.9 ericj 177: }
1.27 tedu 178:
1.29 tedu 179: tfprint(tf[i].fp);
180: if (fflag && tfqueue(&(tf[i])) == -1)
1.27 tedu 181: warn("Unable to follow %s", tf[i].fname);
182:
1.1 deraadt 183: }
1.27 tedu 184: ltf = &(tf[i-1]);
1.1 deraadt 185:
1.27 tedu 186: (void)fflush(stdout);
187: if (!fflag || kq < 0)
188: return;
189:
190: while (1) {
191: if ((nevents = kevent(kq, NULL, 0, &ke, 1, ts)) <= 0) {
192: if (errno == EINTR) {
1.25 landry 193: close(kq);
1.27 tedu 194: return;
1.25 landry 195: }
1.12 art 196: }
197:
1.27 tedu 198: ctf = ke.udata;
199: if (nevents > 0) {
200: if (ke.filter == EVFILT_READ) {
201: if (ctf != ltf) {
202: printfname(ctf->fname);
203: ltf = ctf;
204: }
205: clearerr(ctf->fp);
1.29 tedu 206: tfprint(ctf->fp);
1.27 tedu 207: if (ferror(ctf->fp)) {
208: ierr(ctf->fname);
209: fclose(ctf->fp);
210: warn("Lost file %s", ctf->fname);
211: continue;
212: }
213: (void)fflush(stdout);
214: clearerr(ctf->fp);
215: } else if (ke.filter == EVFILT_VNODE) {
216: if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) {
217: /*
218: * File was deleted or renamed.
219: *
220: * Continue to look at it until
221: * a new file reappears with
222: * the same name.
223: */
224: (void) tfreopen(ctf);
225: } else if (ke.fflags & NOTE_TRUNCATE) {
226: warnx("%s has been truncated, "
227: "resetting.", ctf->fname);
228: fpurge(ctf->fp);
229: rewind(ctf->fp);
230: }
1.6 millert 231: }
232: }
1.27 tedu 233: ts = tfreopen(NULL);
1.1 deraadt 234: }
235: }
236:
237: /*
238: * rlines -- display the last offset lines of the file.
239: */
1.7 millert 240: static int
1.27 tedu 241: rlines(struct tailfile *tf, off_t off)
1.1 deraadt 242: {
1.16 henning 243: off_t pos;
244: int ch;
1.1 deraadt 245:
1.27 tedu 246: pos = tf->sb.st_size;
1.16 henning 247: if (pos == 0)
1.7 millert 248: return (0);
1.1 deraadt 249:
1.16 henning 250: /*
251: * Position before char.
1.17 otto 252: * Last char is special, ignore it whether newline or not.
1.16 henning 253: */
254: pos -= 2;
255: ch = EOF;
256: for (; off > 0 && pos >= 0; pos--) {
257: /* A seek per char isn't a problem with a smart stdio */
1.27 tedu 258: if (fseeko(tf[0].fp, pos, SEEK_SET) == -1) {
259: ierr(tf->fname);
1.16 henning 260: return (1);
261: }
1.27 tedu 262: if ((ch = getc(tf[0].fp)) == '\n')
1.16 henning 263: off--;
264: else if (ch == EOF) {
1.27 tedu 265: if (ferror(tf[0].fp)) {
266: ierr(tf->fname);
1.16 henning 267: return (1);
268: }
1.1 deraadt 269: break;
270: }
1.16 henning 271: }
272: /* If we read until start of file, put back last read char */
1.27 tedu 273: if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, tf[0].fp) == EOF) {
274: ierr(tf->fname);
1.7 millert 275: return (1);
1.1 deraadt 276: }
1.16 henning 277:
1.27 tedu 278: while (!feof(tf[0].fp) && (ch = getc(tf[0].fp)) != EOF)
1.16 henning 279: if (putchar(ch) == EOF)
280: oerr();
1.27 tedu 281: if (ferror(tf[0].fp)) {
282: ierr(tf->fname);
1.7 millert 283: return (1);
1.1 deraadt 284: }
1.7 millert 285:
286: return (0);
1.29 tedu 287: }
288:
289: static inline void
290: tfprint(FILE *fp)
291: {
292: int ch;
293:
294: while (!feof(fp) && (ch = getc(fp)) != EOF)
295: if (putchar(ch) == EOF)
296: oerr();
1.27 tedu 297: }
298:
299: static int
300: tfqueue(struct tailfile *tf)
301: {
302: struct kevent ke[2];
303: int i = 1;
304:
305: if (kq < 0) {
306: errno = EBADF;
307: return -1;
308: }
309:
310: EV_SET(&(ke[0]), fileno(tf->fp), EVFILT_READ,
311: EV_ENABLE | EV_ADD | EV_CLEAR, 0, 0, tf);
312:
313: if (S_ISREG(tf->sb.st_mode)) {
314: i = 2;
315: EV_SET(&(ke[1]), fileno(tf->fp), EVFILT_VNODE,
316: EV_ENABLE | EV_ADD | EV_CLEAR,
317: NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
318: 0, tf);
319: }
320: if (kevent(kq, ke, i, NULL, 0, NULL) < 0) {
321: ierr(tf->fname);
322: return -1;
323: }
324: return 0;
325: }
326:
327: #define AFILESINCR 8
328: static const struct timespec *
329: tfreopen(struct tailfile *tf) {
330: static struct tailfile **reopen = NULL;
331: static int nfiles = 0, afiles = 0;
332: static const struct timespec ts = {1, 0};
333:
334: struct stat sb;
335: struct tailfile **treopen, *ttf;
336: int i;
337:
1.32 ! martijn 338: if (tf && !(tf->fp == stdin) &&
! 339: ((stat(tf->fname, &sb) != 0) || sb.st_ino != tf->sb.st_ino)) {
1.27 tedu 340: if (afiles < ++nfiles) {
341: afiles += AFILESINCR;
342: treopen = reallocarray(reopen, afiles, sizeof(*reopen));
343: if (treopen)
344: reopen = treopen;
345: else
346: afiles -= AFILESINCR;
347: }
1.30 martijn 348: if (nfiles <= afiles) {
349: for (i = 0; i < nfiles - 1; i++)
350: if (strcmp(reopen[i]->fname, tf->fname) == 0)
351: break;
352: if (i < nfiles - 1)
353: nfiles--;
354: else
355: reopen[nfiles-1] = tf;
356: } else {
1.31 jsg 357: warnx("Lost track of %s", tf->fname);
1.30 martijn 358: nfiles--;
359: }
1.27 tedu 360: }
361:
362: for (i = 0; i < nfiles; i++) {
363: ttf = reopen[i];
364: if (stat(ttf->fname, &sb) == -1)
365: continue;
366: if (sb.st_ino != ttf->sb.st_ino) {
367: (void) memcpy(&(ttf->sb), &sb, sizeof(ttf->sb));
368: ttf->fp = freopen(ttf->fname, "r", ttf->fp);
369: if (ttf->fp == NULL)
370: ierr(ttf->fname);
371: else {
372: warnx("%s has been replaced, reopening.",
373: ttf->fname);
374: tfqueue(ttf);
375: }
376: }
377: reopen[i] = reopen[--nfiles];
378: }
379:
380: return nfiles ? &ts : NULL;
1.1 deraadt 381: }