xref: /titanic_52/usr/src/cmd/tail/forward.c (revision 72102e7461c97dc268d21d9dd8f02da45f174acd)
1209e49b2SChris Love /*
2209e49b2SChris Love  * Copyright (c) 1991, 1993
3209e49b2SChris Love  *	The Regents of the University of California.  All rights reserved.
4209e49b2SChris Love  *
5209e49b2SChris Love  * This code is derived from software contributed to Berkeley by
6209e49b2SChris Love  * Edward Sze-Tyan Wang.
7209e49b2SChris Love  *
8209e49b2SChris Love  * Redistribution and use in source and binary forms, with or without
9209e49b2SChris Love  * modification, are permitted provided that the following conditions
10209e49b2SChris Love  * are met:
11209e49b2SChris Love  * 1. Redistributions of source code must retain the above copyright
12209e49b2SChris Love  *    notice, this list of conditions and the following disclaimer.
13209e49b2SChris Love  * 2. Redistributions in binary form must reproduce the above copyright
14209e49b2SChris Love  *    notice, this list of conditions and the following disclaimer in the
15209e49b2SChris Love  *    documentation and/or other materials provided with the distribution.
16209e49b2SChris Love  * 4. Neither the name of the University nor the names of its contributors
17209e49b2SChris Love  *    may be used to endorse or promote products derived from this software
18209e49b2SChris Love  *    without specific prior written permission.
19209e49b2SChris Love  *
20209e49b2SChris Love  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21209e49b2SChris Love  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22209e49b2SChris Love  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23209e49b2SChris Love  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24209e49b2SChris Love  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25209e49b2SChris Love  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26209e49b2SChris Love  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27209e49b2SChris Love  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28209e49b2SChris Love  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29209e49b2SChris Love  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30209e49b2SChris Love  * SUCH DAMAGE.
31209e49b2SChris Love  */
32209e49b2SChris Love 
33209e49b2SChris Love /*
34*72102e74SBryan Cantrill  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
35209e49b2SChris Love  */
36209e49b2SChris Love 
37209e49b2SChris Love #include <sys/param.h>
38209e49b2SChris Love #include <sys/mount.h>
39209e49b2SChris Love #include <sys/types.h>
40209e49b2SChris Love #include <sys/stat.h>
41209e49b2SChris Love #include <sys/statfs.h>
42209e49b2SChris Love #include <sys/statvfs.h>
43209e49b2SChris Love #include <sys/time.h>
44209e49b2SChris Love #include <sys/mman.h>
45209e49b2SChris Love #include <sys/poll.h>
46209e49b2SChris Love #include <port.h>
47209e49b2SChris Love #include <err.h>
48209e49b2SChris Love #include <errno.h>
49209e49b2SChris Love #include <fcntl.h>
50209e49b2SChris Love #include <limits.h>
51209e49b2SChris Love #include <stdio.h>
52209e49b2SChris Love #include <stdlib.h>
53209e49b2SChris Love #include <string.h>
54*72102e74SBryan Cantrill #include <strings.h>
55209e49b2SChris Love #include <unistd.h>
56209e49b2SChris Love 
57209e49b2SChris Love #include "extern.h"
58209e49b2SChris Love 
59209e49b2SChris Love static void rlines(FILE *, const char *fn, off_t, struct stat *);
60209e49b2SChris Love static int show(file_info_t *);
61209e49b2SChris Love static void set_events(file_info_t *files);
62209e49b2SChris Love 
63209e49b2SChris Love /* defines for inner loop actions */
64209e49b2SChris Love #define	USE_SLEEP	0
65*72102e74SBryan Cantrill #define	USE_PORT	1
66209e49b2SChris Love #define	ADD_EVENTS	2
67209e49b2SChris Love 
68*72102e74SBryan Cantrill int port;
69*72102e74SBryan Cantrill int action = USE_PORT;
70209e49b2SChris Love 
71209e49b2SChris Love static const file_info_t *last;
72209e49b2SChris Love 
73209e49b2SChris Love /*
74209e49b2SChris Love  * forward -- display the file, from an offset, forward.
75209e49b2SChris Love  *
76209e49b2SChris Love  * There are eight separate cases for this -- regular and non-regular
77209e49b2SChris Love  * files, by bytes or lines and from the beginning or end of the file.
78209e49b2SChris Love  *
79209e49b2SChris Love  * FBYTES	byte offset from the beginning of the file
80209e49b2SChris Love  *	REG	seek
81209e49b2SChris Love  *	NOREG	read, counting bytes
82209e49b2SChris Love  *
83209e49b2SChris Love  * FLINES	line offset from the beginning of the file
84209e49b2SChris Love  *	REG	read, counting lines
85209e49b2SChris Love  *	NOREG	read, counting lines
86209e49b2SChris Love  *
87209e49b2SChris Love  * RBYTES	byte offset from the end of the file
88209e49b2SChris Love  *	REG	seek
89209e49b2SChris Love  *	NOREG	cyclically read characters into a wrap-around buffer
90209e49b2SChris Love  *
91209e49b2SChris Love  * RLINES
92209e49b2SChris Love  *	REG	mmap the file and step back until reach the correct offset.
93209e49b2SChris Love  *	NOREG	cyclically read lines into a wrap-around array of buffers
94209e49b2SChris Love  */
95209e49b2SChris Love void
96209e49b2SChris Love forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
97209e49b2SChris Love {
98209e49b2SChris Love 	int ch;
99209e49b2SChris Love 
100209e49b2SChris Love 	switch (style) {
101209e49b2SChris Love 	case FBYTES:
102209e49b2SChris Love 		if (off == 0)
103209e49b2SChris Love 			break;
104209e49b2SChris Love 		if (S_ISREG(sbp->st_mode)) {
105209e49b2SChris Love 			if (sbp->st_size < off)
106209e49b2SChris Love 				off = sbp->st_size;
107209e49b2SChris Love 			if (fseeko(fp, off, SEEK_SET) == -1) {
108209e49b2SChris Love 				ierr(fn);
109209e49b2SChris Love 				return;
110209e49b2SChris Love 			}
111209e49b2SChris Love 		} else while (off--)
112209e49b2SChris Love 			if ((ch = getc(fp)) == EOF) {
113209e49b2SChris Love 				if (ferror(fp)) {
114209e49b2SChris Love 					ierr(fn);
115209e49b2SChris Love 					return;
116209e49b2SChris Love 				}
117209e49b2SChris Love 				break;
118209e49b2SChris Love 			}
119209e49b2SChris Love 		break;
120209e49b2SChris Love 	case FLINES:
121209e49b2SChris Love 		if (off == 0)
122209e49b2SChris Love 			break;
123209e49b2SChris Love 		for (;;) {
124209e49b2SChris Love 			if ((ch = getc(fp)) == EOF) {
125209e49b2SChris Love 				if (ferror(fp)) {
126209e49b2SChris Love 					ierr(fn);
127209e49b2SChris Love 					return;
128209e49b2SChris Love 				}
129209e49b2SChris Love 				break;
130209e49b2SChris Love 			}
131209e49b2SChris Love 			if (ch == '\n' && !--off)
132209e49b2SChris Love 				break;
133209e49b2SChris Love 		}
134209e49b2SChris Love 		break;
135209e49b2SChris Love 	case RBYTES:
136209e49b2SChris Love 		if (S_ISREG(sbp->st_mode)) {
137209e49b2SChris Love 			if (sbp->st_size >= off &&
138209e49b2SChris Love 			    fseeko(fp, -off, SEEK_END) == -1) {
139209e49b2SChris Love 				ierr(fn);
140209e49b2SChris Love 				return;
141209e49b2SChris Love 			}
142209e49b2SChris Love 		} else if (off == 0) {
143209e49b2SChris Love 			while (getc(fp) != EOF)
144209e49b2SChris Love 				;
145209e49b2SChris Love 			if (ferror(fp)) {
146209e49b2SChris Love 				ierr(fn);
147209e49b2SChris Love 				return;
148209e49b2SChris Love 			}
149209e49b2SChris Love 		} else
150209e49b2SChris Love 			if (bytes(fp, fn, off))
151209e49b2SChris Love 				return;
152209e49b2SChris Love 		break;
153209e49b2SChris Love 	case RLINES:
154209e49b2SChris Love 		if (S_ISREG(sbp->st_mode))
155209e49b2SChris Love 			if (!off) {
156209e49b2SChris Love 				if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
157209e49b2SChris Love 					ierr(fn);
158209e49b2SChris Love 					return;
159209e49b2SChris Love 				}
160209e49b2SChris Love 			} else
161209e49b2SChris Love 				rlines(fp, fn, off, sbp);
162209e49b2SChris Love 		else if (off == 0) {
163209e49b2SChris Love 			while (getc(fp) != EOF)
164209e49b2SChris Love 				;
165209e49b2SChris Love 			if (ferror(fp)) {
166209e49b2SChris Love 				ierr(fn);
167209e49b2SChris Love 				return;
168209e49b2SChris Love 			}
169209e49b2SChris Love 		} else
170209e49b2SChris Love 			if (lines(fp, fn, off))
171209e49b2SChris Love 				return;
172209e49b2SChris Love 		break;
173209e49b2SChris Love 	default:
174209e49b2SChris Love 		break;
175209e49b2SChris Love 	}
176209e49b2SChris Love 
177209e49b2SChris Love 	while ((ch = getc(fp)) != EOF)
178209e49b2SChris Love 		if (putchar(ch) == EOF)
179209e49b2SChris Love 			oerr();
180209e49b2SChris Love 	if (ferror(fp)) {
181209e49b2SChris Love 		ierr(fn);
182209e49b2SChris Love 		return;
183209e49b2SChris Love 	}
184209e49b2SChris Love 	(void) fflush(stdout);
185209e49b2SChris Love }
186209e49b2SChris Love 
187209e49b2SChris Love /*
188209e49b2SChris Love  * rlines -- display the last offset lines of the file.
189209e49b2SChris Love  */
190209e49b2SChris Love static void
191209e49b2SChris Love rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
192209e49b2SChris Love {
193209e49b2SChris Love 	struct mapinfo map;
194209e49b2SChris Love 	off_t curoff, size;
195209e49b2SChris Love 	int i;
196209e49b2SChris Love 
197209e49b2SChris Love 	if ((size = sbp->st_size) == 0)
198209e49b2SChris Love 		return;
199209e49b2SChris Love 	map.start = NULL;
200209e49b2SChris Love 	map.fd = fileno(fp);
201209e49b2SChris Love 	map.mapoff = map.maxoff = size;
202209e49b2SChris Love 
203209e49b2SChris Love 	/*
204209e49b2SChris Love 	 * Last char is special, ignore whether newline or not. Note that
205209e49b2SChris Love 	 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
206209e49b2SChris Love 	 */
207209e49b2SChris Love 	curoff = size - 2;
208209e49b2SChris Love 	while (curoff >= 0) {
209209e49b2SChris Love 		if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
210209e49b2SChris Love 			ierr(fn);
211209e49b2SChris Love 			return;
212209e49b2SChris Love 		}
213209e49b2SChris Love 		for (i = curoff - map.mapoff; i >= 0; i--)
214209e49b2SChris Love 			if (map.start[i] == '\n' && --off == 0)
215209e49b2SChris Love 				break;
216209e49b2SChris Love 		/* `i' is either the map offset of a '\n', or -1. */
217209e49b2SChris Love 		curoff = map.mapoff + i;
218209e49b2SChris Love 		if (i >= 0)
219209e49b2SChris Love 			break;
220209e49b2SChris Love 	}
221209e49b2SChris Love 	curoff++;
222209e49b2SChris Love 	if (mapprint(&map, curoff, size - curoff) != 0) {
223209e49b2SChris Love 		ierr(fn);
224209e49b2SChris Love 		exit(1);
225209e49b2SChris Love 	}
226209e49b2SChris Love 
227209e49b2SChris Love 	/* Set the file pointer to reflect the length displayed. */
228209e49b2SChris Love 	if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
229209e49b2SChris Love 		ierr(fn);
230209e49b2SChris Love 		return;
231209e49b2SChris Love 	}
232209e49b2SChris Love 	if (map.start != NULL && munmap(map.start, map.maplen)) {
233209e49b2SChris Love 		ierr(fn);
234209e49b2SChris Love 		return;
235209e49b2SChris Love 	}
236209e49b2SChris Love }
237209e49b2SChris Love 
238209e49b2SChris Love static int
239209e49b2SChris Love show(file_info_t *file)
240209e49b2SChris Love {
241209e49b2SChris Love 	int ch;
242209e49b2SChris Love 
243209e49b2SChris Love 	while ((ch = getc(file->fp)) != EOF) {
244209e49b2SChris Love 		if (last != file && no_files > 1) {
245209e49b2SChris Love 			if (!qflag)
246209e49b2SChris Love 				(void) printf("\n==> %s <==\n",
247209e49b2SChris Love 				    file->file_name);
248209e49b2SChris Love 			last = file;
249209e49b2SChris Love 		}
250209e49b2SChris Love 		if (putchar(ch) == EOF)
251209e49b2SChris Love 			oerr();
252209e49b2SChris Love 	}
253209e49b2SChris Love 	(void) fflush(stdout);
254209e49b2SChris Love 	if (ferror(file->fp)) {
255209e49b2SChris Love 		(void) fclose(file->fp);
256209e49b2SChris Love 		file->fp = NULL;
257209e49b2SChris Love 		ierr(file->file_name);
258209e49b2SChris Love 		return (0);
259209e49b2SChris Love 	}
260209e49b2SChris Love 	clearerr(file->fp);
261209e49b2SChris Love 	return (1);
262209e49b2SChris Love }
263209e49b2SChris Love 
264209e49b2SChris Love static void
265*72102e74SBryan Cantrill associate(file_info_t *file, boolean_t assoc, port_event_t *ev)
266*72102e74SBryan Cantrill {
267*72102e74SBryan Cantrill 	char buf[64], *name;
268*72102e74SBryan Cantrill 	int i;
269*72102e74SBryan Cantrill 
270*72102e74SBryan Cantrill 	if (action != USE_PORT || file->fp == NULL)
271*72102e74SBryan Cantrill 		return;
272*72102e74SBryan Cantrill 
273*72102e74SBryan Cantrill 	if (!S_ISREG(file->st.st_mode)) {
274*72102e74SBryan Cantrill 		/*
275*72102e74SBryan Cantrill 		 * For FIFOs, we use PORT_SOURCE_FD as our port event source.
276*72102e74SBryan Cantrill 		 */
277*72102e74SBryan Cantrill 		if (assoc) {
278*72102e74SBryan Cantrill 			(void) port_associate(port, PORT_SOURCE_FD,
279*72102e74SBryan Cantrill 			    fileno(file->fp), POLLIN, file);
280*72102e74SBryan Cantrill 		} else {
281*72102e74SBryan Cantrill 			(void) port_dissociate(port, PORT_SOURCE_FD,
282*72102e74SBryan Cantrill 			    fileno(file->fp));
283*72102e74SBryan Cantrill 		}
284*72102e74SBryan Cantrill 
285*72102e74SBryan Cantrill 		return;
286*72102e74SBryan Cantrill 	}
287*72102e74SBryan Cantrill 
288*72102e74SBryan Cantrill 	bzero(&file->fobj, sizeof (file->fobj));
289*72102e74SBryan Cantrill 
290*72102e74SBryan Cantrill 	if (!Fflag) {
291*72102e74SBryan Cantrill 		/*
292*72102e74SBryan Cantrill 		 * PORT_SOURCE_FILE only allows us to specify a file name, not
293*72102e74SBryan Cantrill 		 * a file descriptor.  If we are following a specific file (as
294*72102e74SBryan Cantrill 		 * opposed to a file name) and we were to specify the name of
295*72102e74SBryan Cantrill 		 * the file to port_associate() and that file were moved
296*72102e74SBryan Cantrill 		 * aside, we would not be able to reassociate an event because
297*72102e74SBryan Cantrill 		 * we would not know a name that would resolve to the new file
298*72102e74SBryan Cantrill 		 * (indeed, there might not be such a name -- the file may
299*72102e74SBryan Cantrill 		 * have been unlinked).  But there _is_ a name that we know
300*72102e74SBryan Cantrill 		 * maps to the file and doesn't change: the name of the
301*72102e74SBryan Cantrill 		 * representation of the open file descriptor in /proc.  We
302*72102e74SBryan Cantrill 		 * therefore associate with this name (and the underlying
303*72102e74SBryan Cantrill 		 * file), not the name of the file as specified at the command
304*72102e74SBryan Cantrill 		 * line.  This also has the (desirable) side-effect of
305*72102e74SBryan Cantrill 		 * insulating against FILE_RENAME_FROM and FILE_RENAME_TO
306*72102e74SBryan Cantrill 		 * events that we need to ignore to assure that we don't lose
307*72102e74SBryan Cantrill 		 * FILE_TRUNC events.
308*72102e74SBryan Cantrill 		 */
309*72102e74SBryan Cantrill 		(void) snprintf(buf,
310*72102e74SBryan Cantrill 		    sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
311*72102e74SBryan Cantrill 		name = buf;
312*72102e74SBryan Cantrill 	} else {
313*72102e74SBryan Cantrill 		name = file->file_name;
314*72102e74SBryan Cantrill 	}
315*72102e74SBryan Cantrill 
316*72102e74SBryan Cantrill 	/*
317*72102e74SBryan Cantrill 	 * Note that portfs uses the address of the specified file_obj_t to
318*72102e74SBryan Cantrill 	 * tag an association; if one creates a different association with a
319*72102e74SBryan Cantrill 	 * (different) file_obj_t that happens to be at the same address,
320*72102e74SBryan Cantrill 	 * the first association will be implicitly removed.  To assure that
321*72102e74SBryan Cantrill 	 * each association has a disjoint file_obj_t, we allocate the memory
322*72102e74SBryan Cantrill 	 * for each in the file_info, not on the stack.
323*72102e74SBryan Cantrill 	 */
324*72102e74SBryan Cantrill 	file->fobj[0].fo_name = name;
325*72102e74SBryan Cantrill 	file->fobj[1].fo_name = name;
326*72102e74SBryan Cantrill 
327*72102e74SBryan Cantrill 	if (assoc) {
328*72102e74SBryan Cantrill 		/*
329*72102e74SBryan Cantrill 		 * To assure that we cannot possibly drop a FILE_TRUNC event,
330*72102e74SBryan Cantrill 		 * we have two different PORT_SOURCE_FILE associations with the
331*72102e74SBryan Cantrill 		 * port:  one to get only FILE_MODIFIED events and another to
332*72102e74SBryan Cantrill 		 * get only FILE_TRUNC events.  This assures that we always
333*72102e74SBryan Cantrill 		 * have an active association for FILE_TRUNC events when the
334*72102e74SBryan Cantrill 		 * seek offset is non-zero.  Note that the association order
335*72102e74SBryan Cantrill 		 * _must_ be FILE_TRUNC followed by FILE_MODIFIED:  if a single
336*72102e74SBryan Cantrill 		 * event induces both a FILE_TRUNC and a FILE_MODIFIED (as
337*72102e74SBryan Cantrill 		 * a VE_CREATE vnode event does), we must process the
338*72102e74SBryan Cantrill 		 * FILE_TRUNC before FILE_MODIFIED -- and the order in which
339*72102e74SBryan Cantrill 		 * these are processed will be the association order.  So
340*72102e74SBryan Cantrill 		 * if we see a FILE_TRUNC, we dissociate/reassociate the
341*72102e74SBryan Cantrill 		 * FILE_MODIFIED association.
342*72102e74SBryan Cantrill 		 */
343*72102e74SBryan Cantrill 		if (ev == NULL || (ev->portev_events & FILE_TRUNC) ||
344*72102e74SBryan Cantrill 		    !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) {
345*72102e74SBryan Cantrill 			(void) port_associate(port, PORT_SOURCE_FILE,
346*72102e74SBryan Cantrill 			    (uintptr_t)&file->fobj[0], FILE_TRUNC, file);
347*72102e74SBryan Cantrill 			(void) port_dissociate(port, PORT_SOURCE_FILE,
348*72102e74SBryan Cantrill 			    (uintptr_t)&file->fobj[1]);
349*72102e74SBryan Cantrill 			ev = NULL;
350*72102e74SBryan Cantrill 		}
351*72102e74SBryan Cantrill 
352*72102e74SBryan Cantrill 		if (ev == NULL || (ev->portev_events & FILE_MODIFIED) ||
353*72102e74SBryan Cantrill 		    !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) {
354*72102e74SBryan Cantrill 			(void) port_associate(port, PORT_SOURCE_FILE,
355*72102e74SBryan Cantrill 			    (uintptr_t)&file->fobj[1], FILE_MODIFIED, file);
356*72102e74SBryan Cantrill 		}
357*72102e74SBryan Cantrill 	} else {
358*72102e74SBryan Cantrill 		for (i = 0; i <= 1; i++) {
359*72102e74SBryan Cantrill 			(void) port_dissociate(port, PORT_SOURCE_FILE,
360*72102e74SBryan Cantrill 			    (uintptr_t)&file->fobj[i]);
361*72102e74SBryan Cantrill 		}
362*72102e74SBryan Cantrill 	}
363*72102e74SBryan Cantrill }
364*72102e74SBryan Cantrill 
365*72102e74SBryan Cantrill static void
366209e49b2SChris Love set_events(file_info_t *files)
367209e49b2SChris Love {
368209e49b2SChris Love 	int i;
369209e49b2SChris Love 	file_info_t *file;
370209e49b2SChris Love 
371209e49b2SChris Love 	for (i = 0, file = files; i < no_files; i++, file++) {
372209e49b2SChris Love 		if (! file->fp)
373209e49b2SChris Love 			continue;
374209e49b2SChris Love 
375209e49b2SChris Love 		(void) fstat(fileno(file->fp), &file->st);
376*72102e74SBryan Cantrill 
377*72102e74SBryan Cantrill 		associate(file, B_TRUE, NULL);
378209e49b2SChris Love 	}
379209e49b2SChris Love }
380209e49b2SChris Love 
381209e49b2SChris Love /*
382209e49b2SChris Love  * follow -- display the file, from an offset, forward.
383209e49b2SChris Love  *
384209e49b2SChris Love  */
385209e49b2SChris Love void
386209e49b2SChris Love follow(file_info_t *files, enum STYLE style, off_t off)
387209e49b2SChris Love {
388209e49b2SChris Love 	int active, ev_change, i, n = -1;
389209e49b2SChris Love 	struct stat sb2;
390209e49b2SChris Love 	file_info_t *file;
391*72102e74SBryan Cantrill 	struct timespec ts;
392*72102e74SBryan Cantrill 	port_event_t ev;
393209e49b2SChris Love 
394209e49b2SChris Love 	/* Position each of the files */
395209e49b2SChris Love 
396209e49b2SChris Love 	file = files;
397209e49b2SChris Love 	active = 0;
398209e49b2SChris Love 	n = 0;
399209e49b2SChris Love 	for (i = 0; i < no_files; i++, file++) {
400209e49b2SChris Love 		if (file->fp) {
401209e49b2SChris Love 			active = 1;
402209e49b2SChris Love 			n++;
403209e49b2SChris Love 			if (no_files > 1 && !qflag)
404209e49b2SChris Love 				(void) printf("\n==> %s <==\n",
405209e49b2SChris Love 				    file->file_name);
406209e49b2SChris Love 			forward(file->fp, file->file_name, style, off,
407209e49b2SChris Love 			    &file->st);
408209e49b2SChris Love 			if (Fflag && fileno(file->fp) != STDIN_FILENO)
409209e49b2SChris Love 				n++;
410209e49b2SChris Love 		}
411209e49b2SChris Love 	}
412209e49b2SChris Love 	if (!Fflag && !active)
413209e49b2SChris Love 		return;
414209e49b2SChris Love 
415209e49b2SChris Love 	last = --file;
416*72102e74SBryan Cantrill 
417*72102e74SBryan Cantrill 	if (action == USE_PORT &&
418*72102e74SBryan Cantrill 	    (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
419*72102e74SBryan Cantrill 	    (port = port_create()) == -1))
420*72102e74SBryan Cantrill 		action = USE_SLEEP;
421*72102e74SBryan Cantrill 
422209e49b2SChris Love 	set_events(files);
423209e49b2SChris Love 
424209e49b2SChris Love 	for (;;) {
425209e49b2SChris Love 		ev_change = 0;
426209e49b2SChris Love 		if (Fflag) {
427209e49b2SChris Love 			for (i = 0, file = files; i < no_files; i++, file++) {
428209e49b2SChris Love 				if (!file->fp) {
429209e49b2SChris Love 					file->fp = fopen(file->file_name, "r");
430209e49b2SChris Love 					if (file->fp != NULL &&
431209e49b2SChris Love 					    fstat(fileno(file->fp), &file->st)
432209e49b2SChris Love 					    == -1) {
433209e49b2SChris Love 						(void) fclose(file->fp);
434209e49b2SChris Love 						file->fp = NULL;
435209e49b2SChris Love 					}
436209e49b2SChris Love 					if (file->fp != NULL)
437209e49b2SChris Love 						ev_change++;
438209e49b2SChris Love 					continue;
439209e49b2SChris Love 				}
440209e49b2SChris Love 				if (fileno(file->fp) == STDIN_FILENO)
441209e49b2SChris Love 					continue;
442209e49b2SChris Love 				if (stat(file->file_name, &sb2) == -1) {
443209e49b2SChris Love 					if (errno != ENOENT)
444209e49b2SChris Love 						ierr(file->file_name);
445209e49b2SChris Love 					(void) show(file);
446209e49b2SChris Love 					(void) fclose(file->fp);
447209e49b2SChris Love 					file->fp = NULL;
448209e49b2SChris Love 					ev_change++;
449209e49b2SChris Love 					continue;
450209e49b2SChris Love 				}
451209e49b2SChris Love 
452209e49b2SChris Love 				if (sb2.st_ino != file->st.st_ino ||
453209e49b2SChris Love 				    sb2.st_dev != file->st.st_dev ||
454209e49b2SChris Love 				    sb2.st_nlink == 0) {
455209e49b2SChris Love 					(void) show(file);
456*72102e74SBryan Cantrill 					associate(file, B_FALSE, NULL);
457209e49b2SChris Love 					file->fp = freopen(file->file_name, "r",
458209e49b2SChris Love 					    file->fp);
459*72102e74SBryan Cantrill 					if (file->fp != NULL) {
460209e49b2SChris Love 						(void) memcpy(&file->st, &sb2,
461209e49b2SChris Love 						    sizeof (struct stat));
462*72102e74SBryan Cantrill 					} else if (errno != ENOENT)
463209e49b2SChris Love 						ierr(file->file_name);
464209e49b2SChris Love 					ev_change++;
465209e49b2SChris Love 				}
466209e49b2SChris Love 			}
467209e49b2SChris Love 		}
468209e49b2SChris Love 
469209e49b2SChris Love 		for (i = 0, file = files; i < no_files; i++, file++)
470209e49b2SChris Love 			if (file->fp && !show(file))
471209e49b2SChris Love 				ev_change++;
472209e49b2SChris Love 
473209e49b2SChris Love 		if (ev_change)
474209e49b2SChris Love 			set_events(files);
475209e49b2SChris Love 
476209e49b2SChris Love 		switch (action) {
477*72102e74SBryan Cantrill 		case USE_PORT:
478*72102e74SBryan Cantrill 			ts.tv_sec = 1;
479*72102e74SBryan Cantrill 			ts.tv_nsec = 0;
480*72102e74SBryan Cantrill 
481*72102e74SBryan Cantrill 			/*
482*72102e74SBryan Cantrill 			 * In the -F case we set a timeout to ensure that
483*72102e74SBryan Cantrill 			 * we re-stat the file at least once every second.
484*72102e74SBryan Cantrill 			 */
485*72102e74SBryan Cantrill 			n = port_get(port, &ev, Fflag ? &ts : NULL);
486*72102e74SBryan Cantrill 
487*72102e74SBryan Cantrill 			if (n == 0) {
488*72102e74SBryan Cantrill 				file = (file_info_t *)ev.portev_user;
489*72102e74SBryan Cantrill 				associate(file, B_TRUE, &ev);
490*72102e74SBryan Cantrill 
491*72102e74SBryan Cantrill 				if (ev.portev_events & FILE_TRUNC)
492*72102e74SBryan Cantrill 					(void) fseek(file->fp, 0, SEEK_SET);
493*72102e74SBryan Cantrill 			}
494*72102e74SBryan Cantrill 
495*72102e74SBryan Cantrill 			break;
496*72102e74SBryan Cantrill 
497209e49b2SChris Love 		case USE_SLEEP:
498209e49b2SChris Love 			(void) usleep(250000);
499209e49b2SChris Love 			break;
500209e49b2SChris Love 		}
501209e49b2SChris Love 	}
502209e49b2SChris Love }
503