diff options
author | Elliott Hughes <enh@google.com> | 2019-05-23 16:28:19 -0700 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2019-05-25 18:59:30 -0500 |
commit | c82d3108947c93c9e27553a44f319171776e92a3 (patch) | |
tree | c85d419eba73989ced08e21d934dce7ba18792d0 | |
parent | cf624717b45049b3d394aeeda85a3ee9b89388af (diff) | |
download | toybox-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.c | 87 | ||||
-rw-r--r-- | lib/portability.h | 5 | ||||
-rw-r--r-- | toys/posix/tail.c | 31 |
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); |