2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Edward Sze-Tyan Wang.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
34 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/types.h>
41 #include <sys/statfs.h>
42 #include <sys/statvfs.h>
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
);
63 /* defines for inner loop actions */
69 int action
= USE_PORT
;
71 static const file_info_t
*last
;
74 * forward -- display the file, from an offset, forward.
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.
79 * FBYTES byte offset from the beginning of the file
81 * NOREG read, counting bytes
83 * FLINES line offset from the beginning of the file
84 * REG read, counting lines
85 * NOREG read, counting lines
87 * RBYTES byte offset from the end of the file
89 * NOREG cyclically read characters into a wrap-around buffer
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
96 forward(FILE *fp
, const char *fn
, enum STYLE style
, off_t off
, struct stat
*sbp
)
104 if (S_ISREG(sbp
->st_mode
)) {
105 if (sbp
->st_size
< off
)
107 if (fseeko(fp
, off
, SEEK_SET
) == -1) {
112 if ((ch
= getc(fp
)) == EOF
) {
124 if ((ch
= getc(fp
)) == EOF
) {
131 if (ch
== '\n' && !--off
)
136 if (S_ISREG(sbp
->st_mode
)) {
137 if (sbp
->st_size
>= off
&&
138 fseeko(fp
, -off
, SEEK_END
) == -1) {
142 } else if (off
== 0) {
143 while (getc(fp
) != EOF
)
150 if (bytes(fp
, fn
, off
))
154 if (S_ISREG(sbp
->st_mode
))
156 if (fseeko(fp
, (off_t
)0, SEEK_END
) == -1) {
161 rlines(fp
, fn
, off
, sbp
);
163 while (getc(fp
) != EOF
)
170 if (lines(fp
, fn
, off
))
177 while ((ch
= getc(fp
)) != EOF
)
178 if (putchar(ch
) == EOF
)
184 (void) fflush(stdout
);
188 * rlines -- display the last offset lines of the file.
191 rlines(FILE *fp
, const char *fn
, off_t off
, struct stat
*sbp
)
197 if ((size
= sbp
->st_size
) == 0)
201 map
.mapoff
= map
.maxoff
= size
;
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.
208 while (curoff
>= 0) {
209 if (curoff
< map
.mapoff
&& maparound(&map
, curoff
) != 0) {
213 for (i
= curoff
- map
.mapoff
; i
>= 0; i
--)
214 if (map
.start
[i
] == '\n' && --off
== 0)
216 /* `i' is either the map offset of a '\n', or -1. */
217 curoff
= map
.mapoff
+ i
;
222 if (mapprint(&map
, curoff
, size
- curoff
) != 0) {
227 /* Set the file pointer to reflect the length displayed. */
228 if (fseeko(fp
, sbp
->st_size
, SEEK_SET
) == -1) {
232 if (map
.start
!= NULL
&& munmap(map
.start
, map
.maplen
)) {
239 show(file_info_t
*file
)
243 while ((ch
= getc(file
->fp
)) != EOF
) {
244 if (last
!= file
&& no_files
> 1) {
246 (void) printf("\n==> %s <==\n",
250 if (putchar(ch
) == EOF
)
253 (void) fflush(stdout
);
254 if (ferror(file
->fp
)) {
255 (void) fclose(file
->fp
);
257 ierr(file
->file_name
);
265 associate(file_info_t
*file
, boolean_t assoc
, port_event_t
*ev
)
270 if (action
!= USE_PORT
|| file
->fp
== NULL
)
273 if (!S_ISREG(file
->st
.st_mode
)) {
275 * For FIFOs, we use PORT_SOURCE_FD as our port event source.
278 (void) port_associate(port
, PORT_SOURCE_FD
,
279 fileno(file
->fp
), POLLIN
, file
);
281 (void) port_dissociate(port
, PORT_SOURCE_FD
,
288 bzero(&file
->fobj
, sizeof (file
->fobj
));
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
310 sizeof (buf
), "/proc/self/fd/%d", fileno(file
->fp
));
313 name
= file
->file_name
;
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.
324 file
->fobj
[0].fo_name
= name
;
325 file
->fobj
[1].fo_name
= name
;
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.
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]);
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
);
358 for (i
= 0; i
<= 1; i
++) {
359 (void) port_dissociate(port
, PORT_SOURCE_FILE
,
360 (uintptr_t)&file
->fobj
[i
]);
366 set_events(file_info_t
*files
)
371 for (i
= 0, file
= files
; i
< no_files
; i
++, file
++) {
375 (void) fstat(fileno(file
->fp
), &file
->st
);
377 associate(file
, B_TRUE
, NULL
);
382 * follow -- display the file, from an offset, forward.
386 follow(file_info_t
*files
, enum STYLE style
, off_t off
)
388 int active
, ev_change
, i
, n
= -1;
394 /* Position each of the files */
399 for (i
= 0; i
< no_files
; i
++, file
++) {
403 if (no_files
> 1 && !qflag
)
404 (void) printf("\n==> %s <==\n",
406 forward(file
->fp
, file
->file_name
, style
, off
,
408 if (Fflag
&& fileno(file
->fp
) != STDIN_FILENO
)
412 if (!Fflag
&& !active
)
417 if (action
== USE_PORT
&&
418 (stat("/proc/self/fd", &sb2
) == -1 || !S_ISDIR(sb2
.st_mode
) ||
419 (port
= port_create()) == -1))
427 for (i
= 0, file
= files
; i
< no_files
; i
++, file
++) {
429 file
->fp
= fopen(file
->file_name
, "r");
430 if (file
->fp
!= NULL
&&
431 fstat(fileno(file
->fp
), &file
->st
)
433 (void) fclose(file
->fp
);
436 if (file
->fp
!= NULL
)
440 if (fileno(file
->fp
) == STDIN_FILENO
)
442 if (stat(file
->file_name
, &sb2
) == -1) {
444 ierr(file
->file_name
);
446 (void) fclose(file
->fp
);
452 if (sb2
.st_ino
!= file
->st
.st_ino
||
453 sb2
.st_dev
!= file
->st
.st_dev
||
456 associate(file
, B_FALSE
, NULL
);
457 file
->fp
= freopen(file
->file_name
, "r",
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
);
469 for (i
= 0, file
= files
; i
< no_files
; i
++, file
++)
470 if (file
->fp
&& !show(file
))
482 * In the -F case we set a timeout to ensure that
483 * we re-stat the file at least once every second.
485 n
= port_get(port
, &ev
, Fflag
? &ts
: NULL
);
488 file
= (file_info_t
*)ev
.portev_user
;
489 associate(file
, B_TRUE
, &ev
);
491 if (ev
.portev_events
& FILE_TRUNC
)
492 (void) fseek(file
->fp
, 0, SEEK_SET
);
498 (void) usleep(250000);