Factor out the inotify code and add a kqueue equivalent.

Specifically tested on macOS 10.14, but I assume this works for other
BSDs too, given that I worked from the FreeBSD man page...
---
 lib/notify.c      | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/portability.h |  5 +++
 toys/posix/tail.c | 31 +++++------------
 3 files changed, 101 insertions(+), 22 deletions(-)
 create mode 100644 lib/notify.c
From c4fdcf6952407fd51d76328435fd52413b38618f Mon Sep 17 00:00:00 2001
From: Elliott Hughes <e...@google.com>
Date: Thu, 23 May 2019 16:28:19 -0700
Subject: [PATCH] tail: support -f on BSD too.

Factor out the inotify code and add a kqueue equivalent.

Specifically tested on macOS 10.14, but I assume this works for other
BSDs too, given that I worked from the FreeBSD man page...
---
 lib/notify.c      | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/portability.h |  5 +++
 toys/posix/tail.c | 31 +++++------------
 3 files changed, 101 insertions(+), 22 deletions(-)
 create mode 100644 lib/notify.c

diff --git a/lib/notify.c b/lib/notify.c
new file mode 100644
index 00000000..a7993e2a
--- /dev/null
+++ b/lib/notify.c
@@ -0,0 +1,87 @@
+#include "toys.h"
+
+static char **paths;
+static int *fds;
+static int count;
+
+#ifdef __APPLE__
+
+#include <sys/event.h>
+
+static int kq = -1;
+
+void notify_init(int max)
+{
+  if ((kq = kqueue()) == -1) perror_exit("kqueue");
+  paths = xmalloc(max * sizeof(char *));
+  fds = xmalloc(max * sizeof(int));
+}
+
+int notify_add(int fd, char *path)
+{
+  struct kevent event;
+
+  EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
+  if (kevent(kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
+    return -1;
+  paths[count] = path;
+  fds[count++] = fd;
+  return 0;
+}
+
+int notify_wait(char **path)
+{
+  struct kevent event;
+  int i;
+
+  for (;;) {
+    if (kevent(kq, NULL, 0, &event, 1, NULL) != -1) {
+      // We get the fd for free, but still have to search for the path.
+      for (i=0; i<count; i++) if (fds[i]==event.ident) {
+        *path = paths[i];
+        return event.ident;
+      }
+    }
+  }
+}
+
+#else
+
+#include <sys/inotify.h>
+
+static int ffd = -1;
+static int *ids;
+
+void notify_init(int max)
+{
+  if ((ffd = inotify_init()) < 0) perror_exit("inotify_init");
+  fds = xmalloc(max * sizeof(int));
+  ids = xmalloc(max * sizeof(int));
+  paths = xmalloc(max * sizeof(char *));
+}
+
+int notify_add(int fd, char *path)
+{
+  ids[count] = inotify_add_watch(ffd, path, IN_MODIFY);
+  if (ids[count] == -1) return -1;
+  paths[count] = path;
+  fds[count++] = fd;
+  return 0;
+}
+
+int notify_wait(char **path)
+{
+  struct inotify_event ev;
+  int i;
+
+  for (;;) {
+    if (sizeof(ev)!=read(ffd, &ev, sizeof(ev))) perror_exit("inotify");
+
+    for (i=0; i<count; i++) if (ev.wd==ids[i]) {
+      *path = paths[i];
+      return fds[i];
+    }
+  }
+}
+
+#endif
diff --git a/lib/portability.h b/lib/portability.h
index ccb1b1c5..f4a2b327 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -307,3 +307,8 @@ int xgetrandom(void *buf, unsigned len, unsigned flags);
 #include <string.h>
 static inline void confstr(int a, char *b, int c) {strcpy(b, a ? "POSIXLY_CORRECT=1" : "/bin:/usr/bin");}
 #endif
+
+// Paper over the differences between BSD kqueue and Linux inotify for tail.
+void notify_init(int max);
+int notify_add(int fd, char *path);
+int notify_wait(char **path);
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
index 8c6548d3..49a7f69b 100644
--- a/toys/posix/tail.c
+++ b/toys/posix/tail.c
@@ -31,12 +31,11 @@ config TAIL_SEEK
 
 #define FOR_tail
 #include "toys.h"
-#include <sys/inotify.h>
 
 GLOBALS(
   long n, c;
 
-  int file_no, ffd, *files;
+  int file_no, last_fd;
 )
 
 struct line_list {
@@ -137,16 +136,14 @@ static void do_tail(int fd, char *name)
   int linepop = 1;
 
   if (FLAG(f)) {
-    int f = TT.file_no*2;
     char *s = name;
 
     if (!fd) sprintf(s = toybuf, "/proc/self/fd/%d", fd);
-    TT.files[f++] = fd;
-    if (0 > (TT.files[f] = inotify_add_watch(TT.ffd, s, IN_MODIFY)))
-      perror_msg("bad -f on '%s'", name);
+    if (notify_add(fd, s) == -1) perror_exit("-f on '%s' failed", s);
   }
 
   if (TT.file_no++) xputc('\n');
+  TT.last_fd = fd;
   if (toys.optc > 1) xprintf("==> %s <==\n", name);
 
   // Are we measuring from the end of the file?
@@ -236,29 +233,19 @@ void tail_main(void)
     }
   }
 
-  // Allocate 2 ints per optarg for -f
-  if (FLAG(f)) {
-    if ((TT.ffd = inotify_init()) < 0) perror_exit("inotify_init");
-    TT.files = xmalloc(toys.optc*8);
-  }
+  if (FLAG(f)) notify_init(toys.optc);
   loopfiles_rw(args, O_RDONLY|WARN_ONLY|(O_CLOEXEC*!FLAG(f)), 0, do_tail);
 
   if (FLAG(f) && TT.file_no) {
-    int len, last_fd = TT.files[(TT.file_no-1)*2], i, fd;
-    struct inotify_event ev;
-
     for (;;) {
-      if (sizeof(ev)!=read(TT.ffd, &ev, sizeof(ev))) perror_exit("inotify");
-
-      for (i = 0; i<TT.file_no && ev.wd!=TT.files[(i*2)+1]; i++);
-      if (i==TT.file_no) continue;
-      fd = TT.files[i*2];
+      char *path;
+      int fd = notify_wait(&path), len;
 
       // Read new data.
       while ((len = read(fd, toybuf, sizeof(toybuf)))>0) {
-        if (last_fd != fd) {
-          last_fd = fd;
-          xprintf("\n==> %s <==\n", args[i]);
+        if (TT.last_fd != fd) {
+          TT.last_fd = fd;
+          xprintf("\n==> %s <==\n", path);
         }
 
         xwrite(1, toybuf, len);
-- 
2.22.0.rc1.257.g3120a18244-goog

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to