Print this page
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>

*** 29,41 **** * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* ! * Solaris porting notes: the original FreeBSD version made use of the ! * BSD kqueue event notification framework; this ! * was changed to use usleep() */ #include <sys/param.h> #include <sys/mount.h> #include <sys/types.h> --- 29,39 ---- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* ! * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/mount.h> #include <sys/types.h>
*** 51,60 **** --- 49,59 ---- #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> + #include <strings.h> #include <unistd.h> #include "extern.h" static void rlines(FILE *, const char *fn, off_t, struct stat *);
*** 61,73 **** static int show(file_info_t *); static void set_events(file_info_t *files); /* defines for inner loop actions */ #define USE_SLEEP 0 #define ADD_EVENTS 2 ! int action = USE_SLEEP; static const file_info_t *last; /* * forward -- display the file, from an offset, forward. --- 60,74 ---- static int show(file_info_t *); static void set_events(file_info_t *files); /* defines for inner loop actions */ #define USE_SLEEP 0 + #define USE_PORT 1 #define ADD_EVENTS 2 ! int port; ! int action = USE_PORT; static const file_info_t *last; /* * forward -- display the file, from an offset, forward.
*** 259,268 **** --- 260,328 ---- clearerr(file->fp); return (1); } static void + associate(file_info_t *file, boolean_t assoc) + { + char buf[64]; + + if (action != USE_PORT || file->fp == NULL) + return; + + if (!S_ISREG(file->st.st_mode)) { + /* + * For FIFOs, we use PORT_SOURCE_FD as our port event source. + */ + if (assoc) { + (void) port_associate(port, PORT_SOURCE_FD, + fileno(file->fp), POLLIN, file); + } else { + (void) port_dissociate(port, PORT_SOURCE_FD, + fileno(file->fp)); + } + + return; + } + + bzero(&file->fobj, sizeof (file->fobj)); + + /* + * We pull a bit of a stunt here. PORT_SOURCE_FILE only allows us to + * specify a file name -- not a file descriptor. If we were to specify + * the name of the file to port_associate() and that file were moved + * aside, we would not be able to reassociate an event because we would + * not know a name that would resolve to the new file (indeed, there + * might not be such a name -- the file may have been unlinked). But + * there _is_ a name that we know maps to the file and doesn't change: + * the name of the representation of the open file descriptor in /proc. + * We therefore associate with this name (and the underlying file), + * not the name of the file as specified at the command line. + */ + (void) snprintf(buf, + sizeof (buf), "/proc/self/fd/%d", fileno(file->fp)); + + /* + * Note that portfs uses the address of the specified file_obj_t to + * tag an association; if one creates a different association with a + * (different) file_ob_t that happens to be at the same address, + * the first association will be implicitly removed. To assure that + * each file has a disjoint file_obj_t, we allocate the memory for it + * in the file_info, not on the stack. + */ + file->fobj.fo_name = buf; + + if (assoc) { + (void) port_associate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj, FILE_MODIFIED | FILE_TRUNC, file); + } else { + (void) port_dissociate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj); + } + } + + static void set_events(file_info_t *files) { int i; file_info_t *file;
*** 269,278 **** --- 329,340 ---- for (i = 0, file = files; i < no_files; i++, file++) { if (! file->fp) continue; (void) fstat(fileno(file->fp), &file->st); + + associate(file, B_TRUE); } } /* * follow -- display the file, from an offset, forward.
*** 282,291 **** --- 344,355 ---- follow(file_info_t *files, enum STYLE style, off_t off) { int active, ev_change, i, n = -1; struct stat sb2; file_info_t *file; + struct timespec ts; + port_event_t ev; /* Position each of the files */ file = files; active = 0;
*** 305,314 **** --- 369,384 ---- } if (!Fflag && !active) return; last = --file; + + if (action == USE_PORT && + (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) || + (port = port_create()) == -1)) + action = USE_SLEEP; + set_events(files); for (;;) { ev_change = 0; if (Fflag) {
*** 339,354 **** if (sb2.st_ino != file->st.st_ino || sb2.st_dev != file->st.st_dev || sb2.st_nlink == 0) { (void) show(file); file->fp = freopen(file->file_name, "r", file->fp); ! if (file->fp != NULL) (void) memcpy(&file->st, &sb2, sizeof (struct stat)); ! else if (errno != ENOENT) ierr(file->file_name); ev_change++; } } } --- 409,425 ---- if (sb2.st_ino != file->st.st_ino || sb2.st_dev != file->st.st_dev || sb2.st_nlink == 0) { (void) show(file); + associate(file, B_FALSE); file->fp = freopen(file->file_name, "r", file->fp); ! if (file->fp != NULL) { (void) memcpy(&file->st, &sb2, sizeof (struct stat)); ! } else if (errno != ENOENT) ierr(file->file_name); ev_change++; } } }
*** 359,368 **** --- 430,459 ---- if (ev_change) set_events(files); switch (action) { + case USE_PORT: + ts.tv_sec = 1; + ts.tv_nsec = 0; + + /* + * In the -F case we set a timeout to ensure that + * we re-stat the file at least once every second. + */ + n = port_get(port, &ev, Fflag ? &ts : NULL); + + if (n == 0) { + file = (file_info_t *)ev.portev_user; + associate(file, B_TRUE); + + if (ev.portev_events & FILE_TRUNC) + (void) fseek(file->fp, 0, SEEK_SET); + } + + break; + case USE_SLEEP: (void) usleep(250000); break; } }