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 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
35 */
36
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/statfs.h>
42 #include <sys/statvfs.h>
43 #include <sys/time.h>
44 #include <sys/mman.h>
45 #include <sys/poll.h>
46 #include <port.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <strings.h>
55 #include <unistd.h>
56
57 #include "extern.h"
58
59 static void rlines(FILE *, const char *fn, off_t, struct stat *);
60 static int show(file_info_t *);
61 static void set_events(file_info_t *files);
62
63 /* defines for inner loop actions */
64 #define USE_SLEEP 0
65 #define USE_PORT 1
66 #define ADD_EVENTS 2
67
68 int port;
69 int action = USE_PORT;
70
71 static const file_info_t *last;
72
73 /*
74 * forward -- display the file, from an offset, forward.
75 *
76 * There are eight separate cases for this -- regular and non-regular
77 * files, by bytes or lines and from the beginning or end of the file.
78 *
79 * FBYTES byte offset from the beginning of the file
80 * REG seek
81 * NOREG read, counting bytes
82 *
83 * FLINES line offset from the beginning of the file
84 * REG read, counting lines
85 * NOREG read, counting lines
86 *
87 * RBYTES byte offset from the end of the file
88 * REG seek
89 * NOREG cyclically read characters into a wrap-around buffer
90 *
91 * RLINES
92 * REG mmap the file and step back until reach the correct offset.
93 * NOREG cyclically read lines into a wrap-around array of buffers
94 */
95 void
96 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
97 {
98 int ch;
99
100 switch (style) {
101 case FBYTES:
102 if (off == 0)
103 break;
104 if (S_ISREG(sbp->st_mode)) {
105 if (sbp->st_size < off)
106 off = sbp->st_size;
107 if (fseeko(fp, off, SEEK_SET) == -1) {
108 ierr(fn);
109 return;
110 }
111 } else while (off--)
112 if ((ch = getc(fp)) == EOF) {
113 if (ferror(fp)) {
114 ierr(fn);
115 return;
116 }
117 break;
118 }
119 break;
120 case FLINES:
121 if (off == 0)
122 break;
123 for (;;) {
124 if ((ch = getc(fp)) == EOF) {
125 if (ferror(fp)) {
126 ierr(fn);
127 return;
128 }
129 break;
130 }
131 if (ch == '\n' && !--off)
132 break;
133 }
134 break;
135 case RBYTES:
136 if (S_ISREG(sbp->st_mode)) {
137 if (sbp->st_size >= off &&
138 fseeko(fp, -off, SEEK_END) == -1) {
139 ierr(fn);
140 return;
141 }
142 } else if (off == 0) {
143 while (getc(fp) != EOF)
144 ;
145 if (ferror(fp)) {
146 ierr(fn);
147 return;
148 }
149 } else
150 if (bytes(fp, fn, off))
151 return;
152 break;
153 case RLINES:
154 if (S_ISREG(sbp->st_mode))
155 if (!off) {
156 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
157 ierr(fn);
158 return;
159 }
160 } else
161 rlines(fp, fn, off, sbp);
162 else if (off == 0) {
163 while (getc(fp) != EOF)
164 ;
165 if (ferror(fp)) {
166 ierr(fn);
167 return;
168 }
169 } else
170 if (lines(fp, fn, off))
171 return;
172 break;
173 default:
174 break;
175 }
176
177 while ((ch = getc(fp)) != EOF)
178 if (putchar(ch) == EOF)
179 oerr();
180 if (ferror(fp)) {
181 ierr(fn);
182 return;
183 }
184 (void) fflush(stdout);
185 }
186
187 /*
188 * rlines -- display the last offset lines of the file.
189 */
190 static void
191 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
192 {
193 struct mapinfo map;
194 off_t curoff, size;
195 int i;
196
197 if ((size = sbp->st_size) == 0)
198 return;
199 map.start = NULL;
200 map.fd = fileno(fp);
201 map.mapoff = map.maxoff = size;
202
203 /*
204 * Last char is special, ignore whether newline or not. Note that
205 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
206 */
207 curoff = size - 2;
208 while (curoff >= 0) {
209 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
210 ierr(fn);
211 return;
212 }
213 for (i = curoff - map.mapoff; i >= 0; i--)
214 if (map.start[i] == '\n' && --off == 0)
215 break;
216 /* `i' is either the map offset of a '\n', or -1. */
217 curoff = map.mapoff + i;
218 if (i >= 0)
219 break;
220 }
221 curoff++;
222 if (mapprint(&map, curoff, size - curoff) != 0) {
223 ierr(fn);
224 exit(1);
225 }
226
227 /* Set the file pointer to reflect the length displayed. */
228 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
229 ierr(fn);
230 return;
231 }
232 if (map.start != NULL && munmap(map.start, map.maplen)) {
233 ierr(fn);
234 return;
235 }
236 }
237
238 static int
239 show(file_info_t *file)
240 {
241 int ch;
242
243 while ((ch = getc(file->fp)) != EOF) {
244 if (last != file && no_files > 1) {
245 if (!qflag)
246 (void) printf("\n==> %s <==\n",
247 file->file_name);
248 last = file;
249 }
250 if (putchar(ch) == EOF)
251 oerr();
252 }
253 (void) fflush(stdout);
254 if (ferror(file->fp)) {
255 (void) fclose(file->fp);
256 file->fp = NULL;
257 ierr(file->file_name);
258 return (0);
259 }
260 clearerr(file->fp);
261 return (1);
262 }
263
264 static void
265 associate(file_info_t *file, boolean_t assoc)
266 {
267 char buf[64];
268
269 if (action != USE_PORT || file->fp == NULL)
270 return;
271
272 if (!S_ISREG(file->st.st_mode)) {
273 /*
274 * For FIFOs, we use PORT_SOURCE_FD as our port event source.
275 */
276 if (assoc) {
277 (void) port_associate(port, PORT_SOURCE_FD,
278 fileno(file->fp), POLLIN, file);
279 } else {
280 (void) port_dissociate(port, PORT_SOURCE_FD,
281 fileno(file->fp));
282 }
283
284 return;
285 }
286
287 bzero(&file->fobj, sizeof (file->fobj));
288
289 /*
290 * We pull a bit of a stunt here. PORT_SOURCE_FILE only allows us to
291 * specify a file name -- not a file descriptor. If we were to specify
292 * the name of the file to port_associate() and that file were moved
293 * aside, we would not be able to reassociate an event because we would
294 * not know a name that would resolve to the new file (indeed, there
295 * might not be such a name -- the file may have been unlinked). But
296 * there _is_ a name that we know maps to the file and doesn't change:
297 * the name of the representation of the open file descriptor in /proc.
298 * We therefore associate with this name (and the underlying file),
299 * not the name of the file as specified at the command line.
300 */
301 (void) snprintf(buf,
302 sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
303
304 /*
305 * Note that portfs uses the address of the specified file_obj_t to
306 * tag an association; if one creates a different association with a
307 * (different) file_ob_t that happens to be at the same address,
308 * the first association will be implicitly removed. To assure that
309 * each file has a disjoint file_obj_t, we allocate the memory for it
310 * in the file_info, not on the stack.
311 */
312 file->fobj.fo_name = buf;
313
314 if (assoc) {
315 (void) port_associate(port, PORT_SOURCE_FILE,
316 (uintptr_t)&file->fobj, FILE_MODIFIED | FILE_TRUNC, file);
317 } else {
318 (void) port_dissociate(port, PORT_SOURCE_FILE,
319 (uintptr_t)&file->fobj);
320 }
321 }
322
323 static void
324 set_events(file_info_t *files)
325 {
326 int i;
327 file_info_t *file;
328
329 for (i = 0, file = files; i < no_files; i++, file++) {
330 if (! file->fp)
331 continue;
332
333 (void) fstat(fileno(file->fp), &file->st);
334
335 associate(file, B_TRUE);
336 }
337 }
338
339 /*
340 * follow -- display the file, from an offset, forward.
341 *
342 */
343 void
344 follow(file_info_t *files, enum STYLE style, off_t off)
345 {
346 int active, ev_change, i, n = -1;
347 struct stat sb2;
348 file_info_t *file;
349 struct timespec ts;
350 port_event_t ev;
351
352 /* Position each of the files */
353
354 file = files;
355 active = 0;
356 n = 0;
357 for (i = 0; i < no_files; i++, file++) {
358 if (file->fp) {
359 active = 1;
360 n++;
361 if (no_files > 1 && !qflag)
362 (void) printf("\n==> %s <==\n",
363 file->file_name);
364 forward(file->fp, file->file_name, style, off,
365 &file->st);
366 if (Fflag && fileno(file->fp) != STDIN_FILENO)
367 n++;
368 }
369 }
370 if (!Fflag && !active)
371 return;
372
373 last = --file;
374
375 if (action == USE_PORT &&
376 (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
377 (port = port_create()) == -1))
378 action = USE_SLEEP;
379
380 set_events(files);
381
382 for (;;) {
383 ev_change = 0;
384 if (Fflag) {
385 for (i = 0, file = files; i < no_files; i++, file++) {
386 if (!file->fp) {
387 file->fp = fopen(file->file_name, "r");
388 if (file->fp != NULL &&
389 fstat(fileno(file->fp), &file->st)
390 == -1) {
391 (void) fclose(file->fp);
392 file->fp = NULL;
393 }
394 if (file->fp != NULL)
395 ev_change++;
396 continue;
397 }
398 if (fileno(file->fp) == STDIN_FILENO)
399 continue;
400 if (stat(file->file_name, &sb2) == -1) {
401 if (errno != ENOENT)
402 ierr(file->file_name);
403 (void) show(file);
404 (void) fclose(file->fp);
405 file->fp = NULL;
406 ev_change++;
407 continue;
408 }
409
410 if (sb2.st_ino != file->st.st_ino ||
411 sb2.st_dev != file->st.st_dev ||
412 sb2.st_nlink == 0) {
413 (void) show(file);
414 associate(file, B_FALSE);
415 file->fp = freopen(file->file_name, "r",
416 file->fp);
417 if (file->fp != NULL) {
418 (void) memcpy(&file->st, &sb2,
419 sizeof (struct stat));
420 } else if (errno != ENOENT)
421 ierr(file->file_name);
422 ev_change++;
423 }
424 }
425 }
426
427 for (i = 0, file = files; i < no_files; i++, file++)
428 if (file->fp && !show(file))
429 ev_change++;
430
431 if (ev_change)
432 set_events(files);
433
434 switch (action) {
435 case USE_PORT:
436 ts.tv_sec = 1;
437 ts.tv_nsec = 0;
438
439 /*
440 * In the -F case we set a timeout to ensure that
441 * we re-stat the file at least once every second.
442 */
443 n = port_get(port, &ev, Fflag ? &ts : NULL);
444
445 if (n == 0) {
446 file = (file_info_t *)ev.portev_user;
447 associate(file, B_TRUE);
448
449 if (ev.portev_events & FILE_TRUNC)
450 (void) fseek(file->fp, 0, SEEK_SET);
451 }
452
453 break;
454
455 case USE_SLEEP:
456 (void) usleep(250000);
457 break;
458 }
459 }
460 }