xref: /illumos-gate/usr/src/cmd/tail/forward.c (revision 209e49b2)
1*209e49b2SChris Love /*
2*209e49b2SChris Love  * Copyright (c) 1991, 1993
3*209e49b2SChris Love  *	The Regents of the University of California.  All rights reserved.
4*209e49b2SChris Love  *
5*209e49b2SChris Love  * This code is derived from software contributed to Berkeley by
6*209e49b2SChris Love  * Edward Sze-Tyan Wang.
7*209e49b2SChris Love  *
8*209e49b2SChris Love  * Redistribution and use in source and binary forms, with or without
9*209e49b2SChris Love  * modification, are permitted provided that the following conditions
10*209e49b2SChris Love  * are met:
11*209e49b2SChris Love  * 1. Redistributions of source code must retain the above copyright
12*209e49b2SChris Love  *    notice, this list of conditions and the following disclaimer.
13*209e49b2SChris Love  * 2. Redistributions in binary form must reproduce the above copyright
14*209e49b2SChris Love  *    notice, this list of conditions and the following disclaimer in the
15*209e49b2SChris Love  *    documentation and/or other materials provided with the distribution.
16*209e49b2SChris Love  * 4. Neither the name of the University nor the names of its contributors
17*209e49b2SChris Love  *    may be used to endorse or promote products derived from this software
18*209e49b2SChris Love  *    without specific prior written permission.
19*209e49b2SChris Love  *
20*209e49b2SChris Love  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21*209e49b2SChris Love  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*209e49b2SChris Love  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*209e49b2SChris Love  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24*209e49b2SChris Love  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*209e49b2SChris Love  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*209e49b2SChris Love  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*209e49b2SChris Love  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*209e49b2SChris Love  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*209e49b2SChris Love  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*209e49b2SChris Love  * SUCH DAMAGE.
31*209e49b2SChris Love  */
32*209e49b2SChris Love 
33*209e49b2SChris Love /*
34*209e49b2SChris Love  * Solaris porting notes:  the original FreeBSD version made use of the
35*209e49b2SChris Love  *                         BSD kqueue event notification framework; this
36*209e49b2SChris Love  *                         was changed to use the Solaris event completion
37*209e49b2SChris Love  *                         framework: port_create(), port_associate(),
38*209e49b2SChris Love  *                         and port_get().
39*209e49b2SChris Love  */
40*209e49b2SChris Love 
41*209e49b2SChris Love #include <sys/param.h>
42*209e49b2SChris Love #include <sys/mount.h>
43*209e49b2SChris Love #include <sys/types.h>
44*209e49b2SChris Love #include <sys/stat.h>
45*209e49b2SChris Love #include <sys/statfs.h>
46*209e49b2SChris Love #include <sys/statvfs.h>
47*209e49b2SChris Love #include <sys/time.h>
48*209e49b2SChris Love #include <sys/mman.h>
49*209e49b2SChris Love #include <sys/poll.h>
50*209e49b2SChris Love #include <port.h>
51*209e49b2SChris Love #include <err.h>
52*209e49b2SChris Love #include <errno.h>
53*209e49b2SChris Love #include <fcntl.h>
54*209e49b2SChris Love #include <limits.h>
55*209e49b2SChris Love #include <stdio.h>
56*209e49b2SChris Love #include <stdlib.h>
57*209e49b2SChris Love #include <string.h>
58*209e49b2SChris Love #include <unistd.h>
59*209e49b2SChris Love 
60*209e49b2SChris Love #include "extern.h"
61*209e49b2SChris Love 
62*209e49b2SChris Love static void rlines(FILE *, const char *fn, off_t, struct stat *);
63*209e49b2SChris Love static int show(file_info_t *);
64*209e49b2SChris Love static void set_events(file_info_t *files);
65*209e49b2SChris Love 
66*209e49b2SChris Love /* defines for inner loop actions */
67*209e49b2SChris Love #define	USE_SLEEP	0
68*209e49b2SChris Love #define	USE_PORT	1
69*209e49b2SChris Love #define	ADD_EVENTS	2
70*209e49b2SChris Love 
71*209e49b2SChris Love int port;
72*209e49b2SChris Love int action = USE_SLEEP;
73*209e49b2SChris Love port_event_t ev;
74*209e49b2SChris Love 
75*209e49b2SChris Love static const file_info_t *last;
76*209e49b2SChris Love 
77*209e49b2SChris Love /*
78*209e49b2SChris Love  * forward -- display the file, from an offset, forward.
79*209e49b2SChris Love  *
80*209e49b2SChris Love  * There are eight separate cases for this -- regular and non-regular
81*209e49b2SChris Love  * files, by bytes or lines and from the beginning or end of the file.
82*209e49b2SChris Love  *
83*209e49b2SChris Love  * FBYTES	byte offset from the beginning of the file
84*209e49b2SChris Love  *	REG	seek
85*209e49b2SChris Love  *	NOREG	read, counting bytes
86*209e49b2SChris Love  *
87*209e49b2SChris Love  * FLINES	line offset from the beginning of the file
88*209e49b2SChris Love  *	REG	read, counting lines
89*209e49b2SChris Love  *	NOREG	read, counting lines
90*209e49b2SChris Love  *
91*209e49b2SChris Love  * RBYTES	byte offset from the end of the file
92*209e49b2SChris Love  *	REG	seek
93*209e49b2SChris Love  *	NOREG	cyclically read characters into a wrap-around buffer
94*209e49b2SChris Love  *
95*209e49b2SChris Love  * RLINES
96*209e49b2SChris Love  *	REG	mmap the file and step back until reach the correct offset.
97*209e49b2SChris Love  *	NOREG	cyclically read lines into a wrap-around array of buffers
98*209e49b2SChris Love  */
99*209e49b2SChris Love void
100*209e49b2SChris Love forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
101*209e49b2SChris Love {
102*209e49b2SChris Love 	int ch;
103*209e49b2SChris Love 
104*209e49b2SChris Love 	switch (style) {
105*209e49b2SChris Love 	case FBYTES:
106*209e49b2SChris Love 		if (off == 0)
107*209e49b2SChris Love 			break;
108*209e49b2SChris Love 		if (S_ISREG(sbp->st_mode)) {
109*209e49b2SChris Love 			if (sbp->st_size < off)
110*209e49b2SChris Love 				off = sbp->st_size;
111*209e49b2SChris Love 			if (fseeko(fp, off, SEEK_SET) == -1) {
112*209e49b2SChris Love 				ierr(fn);
113*209e49b2SChris Love 				return;
114*209e49b2SChris Love 			}
115*209e49b2SChris Love 		} else while (off--)
116*209e49b2SChris Love 			if ((ch = getc(fp)) == EOF) {
117*209e49b2SChris Love 				if (ferror(fp)) {
118*209e49b2SChris Love 					ierr(fn);
119*209e49b2SChris Love 					return;
120*209e49b2SChris Love 				}
121*209e49b2SChris Love 				break;
122*209e49b2SChris Love 			}
123*209e49b2SChris Love 		break;
124*209e49b2SChris Love 	case FLINES:
125*209e49b2SChris Love 		if (off == 0)
126*209e49b2SChris Love 			break;
127*209e49b2SChris Love 		for (;;) {
128*209e49b2SChris Love 			if ((ch = getc(fp)) == EOF) {
129*209e49b2SChris Love 				if (ferror(fp)) {
130*209e49b2SChris Love 					ierr(fn);
131*209e49b2SChris Love 					return;
132*209e49b2SChris Love 				}
133*209e49b2SChris Love 				break;
134*209e49b2SChris Love 			}
135*209e49b2SChris Love 			if (ch == '\n' && !--off)
136*209e49b2SChris Love 				break;
137*209e49b2SChris Love 		}
138*209e49b2SChris Love 		break;
139*209e49b2SChris Love 	case RBYTES:
140*209e49b2SChris Love 		if (S_ISREG(sbp->st_mode)) {
141*209e49b2SChris Love 			if (sbp->st_size >= off &&
142*209e49b2SChris Love 			    fseeko(fp, -off, SEEK_END) == -1) {
143*209e49b2SChris Love 				ierr(fn);
144*209e49b2SChris Love 				return;
145*209e49b2SChris Love 			}
146*209e49b2SChris Love 		} else if (off == 0) {
147*209e49b2SChris Love 			while (getc(fp) != EOF)
148*209e49b2SChris Love 				;
149*209e49b2SChris Love 			if (ferror(fp)) {
150*209e49b2SChris Love 				ierr(fn);
151*209e49b2SChris Love 				return;
152*209e49b2SChris Love 			}
153*209e49b2SChris Love 		} else
154*209e49b2SChris Love 			if (bytes(fp, fn, off))
155*209e49b2SChris Love 				return;
156*209e49b2SChris Love 		break;
157*209e49b2SChris Love 	case RLINES:
158*209e49b2SChris Love 		if (S_ISREG(sbp->st_mode))
159*209e49b2SChris Love 			if (!off) {
160*209e49b2SChris Love 				if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
161*209e49b2SChris Love 					ierr(fn);
162*209e49b2SChris Love 					return;
163*209e49b2SChris Love 				}
164*209e49b2SChris Love 			} else
165*209e49b2SChris Love 				rlines(fp, fn, off, sbp);
166*209e49b2SChris Love 		else if (off == 0) {
167*209e49b2SChris Love 			while (getc(fp) != EOF)
168*209e49b2SChris Love 				;
169*209e49b2SChris Love 			if (ferror(fp)) {
170*209e49b2SChris Love 				ierr(fn);
171*209e49b2SChris Love 				return;
172*209e49b2SChris Love 			}
173*209e49b2SChris Love 		} else
174*209e49b2SChris Love 			if (lines(fp, fn, off))
175*209e49b2SChris Love 				return;
176*209e49b2SChris Love 		break;
177*209e49b2SChris Love 	default:
178*209e49b2SChris Love 		break;
179*209e49b2SChris Love 	}
180*209e49b2SChris Love 
181*209e49b2SChris Love 	while ((ch = getc(fp)) != EOF)
182*209e49b2SChris Love 		if (putchar(ch) == EOF)
183*209e49b2SChris Love 			oerr();
184*209e49b2SChris Love 	if (ferror(fp)) {
185*209e49b2SChris Love 		ierr(fn);
186*209e49b2SChris Love 		return;
187*209e49b2SChris Love 	}
188*209e49b2SChris Love 	(void) fflush(stdout);
189*209e49b2SChris Love }
190*209e49b2SChris Love 
191*209e49b2SChris Love /*
192*209e49b2SChris Love  * rlines -- display the last offset lines of the file.
193*209e49b2SChris Love  */
194*209e49b2SChris Love static void
195*209e49b2SChris Love rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
196*209e49b2SChris Love {
197*209e49b2SChris Love 	struct mapinfo map;
198*209e49b2SChris Love 	off_t curoff, size;
199*209e49b2SChris Love 	int i;
200*209e49b2SChris Love 
201*209e49b2SChris Love 	if ((size = sbp->st_size) == 0)
202*209e49b2SChris Love 		return;
203*209e49b2SChris Love 	map.start = NULL;
204*209e49b2SChris Love 	map.fd = fileno(fp);
205*209e49b2SChris Love 	map.mapoff = map.maxoff = size;
206*209e49b2SChris Love 
207*209e49b2SChris Love 	/*
208*209e49b2SChris Love 	 * Last char is special, ignore whether newline or not. Note that
209*209e49b2SChris Love 	 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
210*209e49b2SChris Love 	 */
211*209e49b2SChris Love 	curoff = size - 2;
212*209e49b2SChris Love 	while (curoff >= 0) {
213*209e49b2SChris Love 		if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
214*209e49b2SChris Love 			ierr(fn);
215*209e49b2SChris Love 			return;
216*209e49b2SChris Love 		}
217*209e49b2SChris Love 		for (i = curoff - map.mapoff; i >= 0; i--)
218*209e49b2SChris Love 			if (map.start[i] == '\n' && --off == 0)
219*209e49b2SChris Love 				break;
220*209e49b2SChris Love 		/* `i' is either the map offset of a '\n', or -1. */
221*209e49b2SChris Love 		curoff = map.mapoff + i;
222*209e49b2SChris Love 		if (i >= 0)
223*209e49b2SChris Love 			break;
224*209e49b2SChris Love 	}
225*209e49b2SChris Love 	curoff++;
226*209e49b2SChris Love 	if (mapprint(&map, curoff, size - curoff) != 0) {
227*209e49b2SChris Love 		ierr(fn);
228*209e49b2SChris Love 		exit(1);
229*209e49b2SChris Love 	}
230*209e49b2SChris Love 
231*209e49b2SChris Love 	/* Set the file pointer to reflect the length displayed. */
232*209e49b2SChris Love 	if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
233*209e49b2SChris Love 		ierr(fn);
234*209e49b2SChris Love 		return;
235*209e49b2SChris Love 	}
236*209e49b2SChris Love 	if (map.start != NULL && munmap(map.start, map.maplen)) {
237*209e49b2SChris Love 		ierr(fn);
238*209e49b2SChris Love 		return;
239*209e49b2SChris Love 	}
240*209e49b2SChris Love }
241*209e49b2SChris Love 
242*209e49b2SChris Love static int
243*209e49b2SChris Love show(file_info_t *file)
244*209e49b2SChris Love {
245*209e49b2SChris Love 	int ch;
246*209e49b2SChris Love 
247*209e49b2SChris Love 	while ((ch = getc(file->fp)) != EOF) {
248*209e49b2SChris Love 		if (last != file && no_files > 1) {
249*209e49b2SChris Love 			if (!qflag)
250*209e49b2SChris Love 				(void) printf("\n==> %s <==\n",
251*209e49b2SChris Love 				    file->file_name);
252*209e49b2SChris Love 			last = file;
253*209e49b2SChris Love 		}
254*209e49b2SChris Love 		if (putchar(ch) == EOF)
255*209e49b2SChris Love 			oerr();
256*209e49b2SChris Love 	}
257*209e49b2SChris Love 	(void) fflush(stdout);
258*209e49b2SChris Love 	if (ferror(file->fp)) {
259*209e49b2SChris Love 		(void) fclose(file->fp);
260*209e49b2SChris Love 		file->fp = NULL;
261*209e49b2SChris Love 		ierr(file->file_name);
262*209e49b2SChris Love 		return (0);
263*209e49b2SChris Love 	}
264*209e49b2SChris Love 	clearerr(file->fp);
265*209e49b2SChris Love 	return (1);
266*209e49b2SChris Love }
267*209e49b2SChris Love 
268*209e49b2SChris Love static void
269*209e49b2SChris Love set_events(file_info_t *files)
270*209e49b2SChris Love {
271*209e49b2SChris Love 	int i;
272*209e49b2SChris Love 	file_info_t *file;
273*209e49b2SChris Love 
274*209e49b2SChris Love 	action = USE_PORT;
275*209e49b2SChris Love 	for (i = 0, file = files; i < no_files; i++, file++) {
276*209e49b2SChris Love 		if (! file->fp)
277*209e49b2SChris Love 			continue;
278*209e49b2SChris Love 
279*209e49b2SChris Love 		(void) fstat(fileno(file->fp), &file->st);
280*209e49b2SChris Love 		/* For -f or -F will both use Solaris port interface */
281*209e49b2SChris Love 		if (fflag && (fileno(file->fp) != STDIN_FILENO)) {
282*209e49b2SChris Love 			(void) port_associate(port, PORT_SOURCE_FD,
283*209e49b2SChris Love 			    fileno(file->fp), POLLIN, (void*)file);
284*209e49b2SChris Love 		}
285*209e49b2SChris Love 	}
286*209e49b2SChris Love }
287*209e49b2SChris Love 
288*209e49b2SChris Love /*
289*209e49b2SChris Love  * follow -- display the file, from an offset, forward.
290*209e49b2SChris Love  *
291*209e49b2SChris Love  */
292*209e49b2SChris Love void
293*209e49b2SChris Love follow(file_info_t *files, enum STYLE style, off_t off)
294*209e49b2SChris Love {
295*209e49b2SChris Love 	int active, ev_change, i, n = -1;
296*209e49b2SChris Love 	struct stat sb2;
297*209e49b2SChris Love 	file_info_t *file;
298*209e49b2SChris Love 	struct timespec ts;
299*209e49b2SChris Love 
300*209e49b2SChris Love 	/* Position each of the files */
301*209e49b2SChris Love 
302*209e49b2SChris Love 	file = files;
303*209e49b2SChris Love 	active = 0;
304*209e49b2SChris Love 	n = 0;
305*209e49b2SChris Love 	for (i = 0; i < no_files; i++, file++) {
306*209e49b2SChris Love 		if (file->fp) {
307*209e49b2SChris Love 			active = 1;
308*209e49b2SChris Love 			n++;
309*209e49b2SChris Love 			if (no_files > 1 && !qflag)
310*209e49b2SChris Love 				(void) printf("\n==> %s <==\n",
311*209e49b2SChris Love 				    file->file_name);
312*209e49b2SChris Love 			forward(file->fp, file->file_name, style, off,
313*209e49b2SChris Love 			    &file->st);
314*209e49b2SChris Love 			if (Fflag && fileno(file->fp) != STDIN_FILENO)
315*209e49b2SChris Love 				n++;
316*209e49b2SChris Love 		}
317*209e49b2SChris Love 	}
318*209e49b2SChris Love 	if (!Fflag && !active)
319*209e49b2SChris Love 		return;
320*209e49b2SChris Love 
321*209e49b2SChris Love 	last = --file;
322*209e49b2SChris Love 	port = port_create();
323*209e49b2SChris Love 	set_events(files);
324*209e49b2SChris Love 
325*209e49b2SChris Love 	for (;;) {
326*209e49b2SChris Love 		ev_change = 0;
327*209e49b2SChris Love 		if (Fflag) {
328*209e49b2SChris Love 			for (i = 0, file = files; i < no_files; i++, file++) {
329*209e49b2SChris Love 				if (!file->fp) {
330*209e49b2SChris Love 					file->fp = fopen(file->file_name, "r");
331*209e49b2SChris Love 					if (file->fp != NULL &&
332*209e49b2SChris Love 					    fstat(fileno(file->fp), &file->st)
333*209e49b2SChris Love 					    == -1) {
334*209e49b2SChris Love 						(void) fclose(file->fp);
335*209e49b2SChris Love 						file->fp = NULL;
336*209e49b2SChris Love 					}
337*209e49b2SChris Love 					if (file->fp != NULL)
338*209e49b2SChris Love 						ev_change++;
339*209e49b2SChris Love 					continue;
340*209e49b2SChris Love 				}
341*209e49b2SChris Love 				if (fileno(file->fp) == STDIN_FILENO)
342*209e49b2SChris Love 					continue;
343*209e49b2SChris Love 				if (stat(file->file_name, &sb2) == -1) {
344*209e49b2SChris Love 					if (errno != ENOENT)
345*209e49b2SChris Love 						ierr(file->file_name);
346*209e49b2SChris Love 					(void) show(file);
347*209e49b2SChris Love 					(void) fclose(file->fp);
348*209e49b2SChris Love 					file->fp = NULL;
349*209e49b2SChris Love 					ev_change++;
350*209e49b2SChris Love 					continue;
351*209e49b2SChris Love 				}
352*209e49b2SChris Love 
353*209e49b2SChris Love 				if (sb2.st_ino != file->st.st_ino ||
354*209e49b2SChris Love 				    sb2.st_dev != file->st.st_dev ||
355*209e49b2SChris Love 				    sb2.st_nlink == 0) {
356*209e49b2SChris Love 					(void) show(file);
357*209e49b2SChris Love 					file->fp = freopen(file->file_name, "r",
358*209e49b2SChris Love 					    file->fp);
359*209e49b2SChris Love 					if (file->fp != NULL)
360*209e49b2SChris Love 						(void) memcpy(&file->st, &sb2,
361*209e49b2SChris Love 						    sizeof (struct stat));
362*209e49b2SChris Love 					else if (errno != ENOENT)
363*209e49b2SChris Love 						ierr(file->file_name);
364*209e49b2SChris Love 					ev_change++;
365*209e49b2SChris Love 				}
366*209e49b2SChris Love 			}
367*209e49b2SChris Love 		}
368*209e49b2SChris Love 
369*209e49b2SChris Love 		for (i = 0, file = files; i < no_files; i++, file++)
370*209e49b2SChris Love 			if (file->fp && !show(file))
371*209e49b2SChris Love 				ev_change++;
372*209e49b2SChris Love 
373*209e49b2SChris Love 		if (ev_change)
374*209e49b2SChris Love 			set_events(files);
375*209e49b2SChris Love 
376*209e49b2SChris Love 		switch (action) {
377*209e49b2SChris Love 		case USE_PORT:
378*209e49b2SChris Love 			ts.tv_sec = 1;
379*209e49b2SChris Love 			ts.tv_nsec = 0;
380*209e49b2SChris Love 			/*
381*209e49b2SChris Love 			 * In the -F case we set a timeout to ensure that
382*209e49b2SChris Love 			 * we re-stat the file at least once every second.
383*209e49b2SChris Love 			 */
384*209e49b2SChris Love 			n = port_get(port, &ev, Fflag? &ts : NULL);
385*209e49b2SChris Love 			if (n == 0) {
386*209e49b2SChris Love 				file = (file_info_t *)ev.portev_user;
387*209e49b2SChris Love 				(void) port_associate(port, PORT_SOURCE_FD,
388*209e49b2SChris Love 				    fileno(file->fp), POLLIN, (void*)file);
389*209e49b2SChris Love 			}
390*209e49b2SChris Love 			break;
391*209e49b2SChris Love 
392*209e49b2SChris Love 		case USE_SLEEP:
393*209e49b2SChris Love 			(void) usleep(250000);
394*209e49b2SChris Love 			break;
395*209e49b2SChris Love 		}
396*209e49b2SChris Love 	}
397*209e49b2SChris Love }
398