Print this page
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
@@ -29,13 +29,11 @@
* 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()
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/types.h>
@@ -51,10 +49,11 @@
#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,13 +60,15 @@
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 action = USE_SLEEP;
+int port;
+int action = USE_PORT;
static const file_info_t *last;
/*
* forward -- display the file, from an offset, forward.
@@ -259,10 +260,69 @@
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,10 +329,12 @@
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,10 +344,12 @@
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,10 +369,16 @@
}
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,16 +409,17 @@
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)
+ if (file->fp != NULL) {
(void) memcpy(&file->st, &sb2,
sizeof (struct stat));
- else if (errno != ENOENT)
+ } else if (errno != ENOENT)
ierr(file->file_name);
ev_change++;
}
}
}
@@ -359,10 +430,30 @@
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;
}
}