aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-05-23 16:28:19 -0700
committerRob Landley <rob@landley.net>2019-05-25 18:59:30 -0500
commitc82d3108947c93c9e27553a44f319171776e92a3 (patch)
treec85d419eba73989ced08e21d934dce7ba18792d0
parentcf624717b45049b3d394aeeda85a3ee9b89388af (diff)
downloadtoybox-c82d3108947c93c9e27553a44f319171776e92a3.tar.gz
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...
-rw-r--r--lib/notify.c87
-rw-r--r--lib/portability.h5
-rw-r--r--toys/posix/tail.c31
3 files changed, 101 insertions, 22 deletions
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);