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
forward(FILE * fp,const char * fn,enum STYLE style,off_t off,struct stat * sbp)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
rlines(FILE * fp,const char * fn,off_t off,struct stat * sbp)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
show(file_info_t * file)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
associate(file_info_t * file,boolean_t assoc,port_event_t * ev)265 associate(file_info_t *file, boolean_t assoc, port_event_t *ev)
266 {
267 char buf[64], *name;
268 int i;
269
270 if (action != USE_PORT || file->fp == NULL)
271 return;
272
273 if (!S_ISREG(file->st.st_mode)) {
274 /*
275 * For FIFOs, we use PORT_SOURCE_FD as our port event source.
276 */
277 if (assoc) {
278 (void) port_associate(port, PORT_SOURCE_FD,
279 fileno(file->fp), POLLIN, file);
280 } else {
281 (void) port_dissociate(port, PORT_SOURCE_FD,
282 fileno(file->fp));
283 }
284
285 return;
286 }
287
288 bzero(&file->fobj, sizeof (file->fobj));
289
290 if (!Fflag) {
291 /*
292 * PORT_SOURCE_FILE only allows us to specify a file name, not
293 * a file descriptor. If we are following a specific file (as
294 * opposed to a file name) and we were to specify the name of
295 * the file to port_associate() and that file were moved
296 * aside, we would not be able to reassociate an event because
297 * we would not know a name that would resolve to the new file
298 * (indeed, there might not be such a name -- the file may
299 * have been unlinked). But there _is_ a name that we know
300 * maps to the file and doesn't change: the name of the
301 * representation of the open file descriptor in /proc. We
302 * therefore associate with this name (and the underlying
303 * file), not the name of the file as specified at the command
304 * line. This also has the (desirable) side-effect of
305 * insulating against FILE_RENAME_FROM and FILE_RENAME_TO
306 * events that we need to ignore to assure that we don't lose
307 * FILE_TRUNC events.
308 */
309 (void) snprintf(buf,
310 sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
311 name = buf;
312 } else {
313 name = file->file_name;
314 }
315
316 /*
317 * Note that portfs uses the address of the specified file_obj_t to
318 * tag an association; if one creates a different association with a
319 * (different) file_obj_t that happens to be at the same address,
320 * the first association will be implicitly removed. To assure that
321 * each association has a disjoint file_obj_t, we allocate the memory
322 * for each in the file_info, not on the stack.
323 */
324 file->fobj[0].fo_name = name;
325 file->fobj[1].fo_name = name;
326
327 if (assoc) {
328 /*
329 * To assure that we cannot possibly drop a FILE_TRUNC event,
330 * we have two different PORT_SOURCE_FILE associations with the
331 * port: one to get only FILE_MODIFIED events and another to
332 * get only FILE_TRUNC events. This assures that we always
333 * have an active association for FILE_TRUNC events when the
334 * seek offset is non-zero. Note that the association order
335 * _must_ be FILE_TRUNC followed by FILE_MODIFIED: if a single
336 * event induces both a FILE_TRUNC and a FILE_MODIFIED (as
337 * a VE_CREATE vnode event does), we must process the
338 * FILE_TRUNC before FILE_MODIFIED -- and the order in which
339 * these are processed will be the association order. So
340 * if we see a FILE_TRUNC, we dissociate/reassociate the
341 * FILE_MODIFIED association.
342 */
343 if (ev == NULL || (ev->portev_events & FILE_TRUNC) ||
344 !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) {
345 (void) port_associate(port, PORT_SOURCE_FILE,
346 (uintptr_t)&file->fobj[0], FILE_TRUNC, file);
347 (void) port_dissociate(port, PORT_SOURCE_FILE,
348 (uintptr_t)&file->fobj[1]);
349 ev = NULL;
350 }
351
352 if (ev == NULL || (ev->portev_events & FILE_MODIFIED) ||
353 !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) {
354 (void) port_associate(port, PORT_SOURCE_FILE,
355 (uintptr_t)&file->fobj[1], FILE_MODIFIED, file);
356 }
357 } else {
358 for (i = 0; i <= 1; i++) {
359 (void) port_dissociate(port, PORT_SOURCE_FILE,
360 (uintptr_t)&file->fobj[i]);
361 }
362 }
363 }
364
365 static void
set_events(file_info_t * files)366 set_events(file_info_t *files)
367 {
368 int i;
369 file_info_t *file;
370
371 for (i = 0, file = files; i < no_files; i++, file++) {
372 if (! file->fp)
373 continue;
374
375 (void) fstat(fileno(file->fp), &file->st);
376
377 associate(file, B_TRUE, NULL);
378 }
379 }
380
381 /*
382 * follow -- display the file, from an offset, forward.
383 *
384 */
385 void
follow(file_info_t * files,enum STYLE style,off_t off)386 follow(file_info_t *files, enum STYLE style, off_t off)
387 {
388 int active, ev_change, i, n = -1;
389 struct stat sb2;
390 file_info_t *file;
391 struct timespec ts;
392 port_event_t ev;
393
394 /* Position each of the files */
395
396 file = files;
397 active = 0;
398 n = 0;
399 for (i = 0; i < no_files; i++, file++) {
400 if (file->fp) {
401 active = 1;
402 n++;
403 if (no_files > 1 && !qflag)
404 (void) printf("\n==> %s <==\n",
405 file->file_name);
406 forward(file->fp, file->file_name, style, off,
407 &file->st);
408 if (Fflag && fileno(file->fp) != STDIN_FILENO)
409 n++;
410 }
411 }
412 if (!Fflag && !active)
413 return;
414
415 last = --file;
416
417 if (action == USE_PORT &&
418 (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
419 (port = port_create()) == -1))
420 action = USE_SLEEP;
421
422 set_events(files);
423
424 for (;;) {
425 ev_change = 0;
426 if (Fflag) {
427 for (i = 0, file = files; i < no_files; i++, file++) {
428 if (!file->fp) {
429 file->fp = fopen(file->file_name, "r");
430 if (file->fp != NULL &&
431 fstat(fileno(file->fp), &file->st)
432 == -1) {
433 (void) fclose(file->fp);
434 file->fp = NULL;
435 }
436 if (file->fp != NULL)
437 ev_change++;
438 continue;
439 }
440 if (fileno(file->fp) == STDIN_FILENO)
441 continue;
442 if (stat(file->file_name, &sb2) == -1) {
443 if (errno != ENOENT)
444 ierr(file->file_name);
445 (void) show(file);
446 (void) fclose(file->fp);
447 file->fp = NULL;
448 ev_change++;
449 continue;
450 }
451
452 if (sb2.st_ino != file->st.st_ino ||
453 sb2.st_dev != file->st.st_dev ||
454 sb2.st_nlink == 0) {
455 (void) show(file);
456 associate(file, B_FALSE, NULL);
457 file->fp = freopen(file->file_name, "r",
458 file->fp);
459 if (file->fp != NULL) {
460 (void) memcpy(&file->st, &sb2,
461 sizeof (struct stat));
462 } else if (errno != ENOENT)
463 ierr(file->file_name);
464 ev_change++;
465 }
466 }
467 }
468
469 for (i = 0, file = files; i < no_files; i++, file++)
470 if (file->fp && !show(file))
471 ev_change++;
472
473 if (ev_change)
474 set_events(files);
475
476 switch (action) {
477 case USE_PORT:
478 ts.tv_sec = 1;
479 ts.tv_nsec = 0;
480
481 /*
482 * In the -F case we set a timeout to ensure that
483 * we re-stat the file at least once every second.
484 */
485 n = port_get(port, &ev, Fflag ? &ts : NULL);
486
487 if (n == 0) {
488 file = (file_info_t *)ev.portev_user;
489 associate(file, B_TRUE, &ev);
490
491 if (ev.portev_events & FILE_TRUNC)
492 (void) fseek(file->fp, 0, SEEK_SET);
493 }
494
495 break;
496
497 case USE_SLEEP:
498 (void) usleep(250000);
499 break;
500 }
501 }
502 }
503