diff options
Diffstat (limited to 'src/minutils')
-rw-r--r-- | src/minutils/s6-logwatch.c | 204 |
1 files changed, 109 insertions, 95 deletions
diff --git a/src/minutils/s6-logwatch.c b/src/minutils/s6-logwatch.c index 42214d7..6100cbd 100644 --- a/src/minutils/s6-logwatch.c +++ b/src/minutils/s6-logwatch.c @@ -2,69 +2,112 @@ #include <sys/uio.h> #include <errno.h> +#include <limits.h> #include <string.h> #include <unistd.h> #include <signal.h> -#include <sys/ioctl.h> #include <sys/inotify.h> +#include <skalibs/types.h> #include <skalibs/allreadwrite.h> #include <skalibs/sgetopt.h> #include <skalibs/strerr2.h> #include <skalibs/error.h> #include <skalibs/buffer.h> -#include <skalibs/bufalloc.h> #include <skalibs/sig.h> #include <skalibs/djbunix.h> #include <skalibs/iopause.h> -#include <skalibs/types.h> -#define USAGE "s6-logwatch [ -m maxbuffer ] logdir" +#define USAGE "s6-logwatch [ logdir ]" #define dieusage() strerr_dieusage(100, USAGE) -#define N 4096 -#define IESIZE 100 +#define B_READING 0 +#define B_BLOCKING 1 +#define B_WAITING 2 +static unsigned int state ; +static int fd ; +static int newcurrent = 0 ; -typedef enum bstate_e bstate_t, *bstate_t_ref ; -enum bstate_e +union inotify_event_u { - B_TAILING = 0, - B_WAITING = 1 + struct inotify_event event ; + char buf[sizeof(struct inotify_event) + NAME_MAX + 1] ; } ; -static void X (void) +static void goteof (void) { - strerr_diefu1x(101, "follow file state changes (race condition triggered). Sorry.") ; + if (newcurrent) + { + fd_close(fd) ; + fd = open_read("current") ; + if (fd < 0) strerr_diefu1sys(111, "current") ; + newcurrent = 0 ; + state = B_READING ; + } + else state = B_BLOCKING ; } -static size_t nbcat (int fdcurrent) +static int readit (int fd) { - char buf[N+1] ; - buffer b = BUFFER_INIT(&fd_readv, fdcurrent, buf, N+1) ; struct iovec v[2] ; - size_t bytes = 0 ; - for (;;) + ssize_t r ; + buffer_wpeek(buffer_1, v) ; + r = fd_readv(fd, v, 2) ; + switch (r) + { + case -1 : return 0 ; + case 0 : goteof() ; break ; + default : buffer_wseek(buffer_1, r) ; + } + return 1 ; +} + +static void maketransition (unsigned int transition) +{ + static unsigned char const table[3][3] = { + { 0x10, 0x00, 0x00 }, + { 0x60, 0x22, 0x00 }, + { 0x40, 0x03, 0x02 } + } ; + unsigned char c = table[state][transition] ; + state = c & 0x0f ; + if (state == 3) strerr_dief1x(101, "current moved twice without being recreated") ; + if (c & 0x10) newcurrent = 1 ; + if (c & 0x20) { fd_close(fd) ; fd = -1 ; } + if (c & 0x40) { - ssize_t r = sanitize_read(buffer_fill(&b)) ; - if (!r) break ; - if (r < 0) + fd = open_read("current") ; + if (fd < 0) strerr_diefu1sys(111, "current") ; + } +} + +static void handle_event (int ifd, int watch) +{ + ssize_t r ; + size_t offset = 0 ; + union inotify_event_u u ; + r = read(ifd, u.buf, sizeof(u.buf)) ; + while (r > 0) + { + struct inotify_event *event = (struct inotify_event *)(u.buf + offset) ; + offset += sizeof(struct inotify_event) + event->len ; + r -= sizeof(struct inotify_event) + event->len ; + if (event->wd == watch && !strcmp(event->name, "current")) { - if (errno == EPIPE) break ; - else strerr_diefu1sys(111, "buffer_fill") ; + int transition = -1 ; + if (event->mask & IN_CREATE) transition = 0 ; + else if (event->mask & IN_MOVED_FROM) transition = 1 ; + else if (event->mask & IN_MODIFY) transition = 2 ; + if (transition >= 0) maketransition(transition) ; } - buffer_rpeek(&b, v) ; - if (!bufalloc_putv(bufalloc_1, v, 2)) - strerr_diefu1sys(111, "bufalloc_putv") ; - buffer_rseek(&b, r) ; - bytes += r ; } - return bytes ; } - int main (int argc, char const *const *argv) { + iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 1 } } ; char const *dir = "." ; - unsigned long maxlen = 4000 ; + int watch ; + unsigned int maxlen = 4096 ; PROG = "s6-logwatch" ; { subgetopt_t l = SUBGETOPT_ZERO ; @@ -74,7 +117,10 @@ int main (int argc, char const *const *argv) if (opt == -1) break ; switch (opt) { - case 'm' : if (!ulong0_scan(l.arg, &maxlen)) dieusage() ; break ; + case 'm' : + if (!uint0_scan(l.arg, &maxlen)) dieusage() ; + strerr_warnw1x("the -m option is deprecated") ; + break ; default : dieusage() ; } } @@ -83,74 +129,42 @@ int main (int argc, char const *const *argv) if (argc) dir = *argv ; if (chdir(dir) < 0) strerr_diefu2sys(111, "chdir to ", dir) ; + + x[0].fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC) ; + if (x[0].fd < 0) strerr_diefu1sys(111, "inotify_init") ; + watch = inotify_add_watch(x[0].fd, ".", IN_CREATE | IN_MOVED_FROM | IN_MODIFY) ; + if (watch < 0) strerr_diefu1sys(111, "inotify_add_watch") ; + fd = open_readb("current") ; + if (fd < 0) { - iopause_fd x[1] = { { -1, IOPAUSE_READ, 0 } } ; - size_t pos = 0 ; - int fdcurrent = -1 ; - int w ; - bstate_t state = B_TAILING ; - x[0].fd = inotify_init() ; - if (x[0].fd < 0) strerr_diefu1sys(111, "inotify_init") ; - if (ndelay_on(x[0].fd) < 0) strerr_diefu1sys(111, "ndelay_on inotify fd") ; - w = inotify_add_watch(x[0].fd, ".", IN_CREATE | IN_MODIFY | IN_CLOSE_WRITE) ; - if (w < 0) strerr_diefu1sys(111, "inotify_add_watch") ; - if (sig_ignore(SIGPIPE) == -1) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; - fdcurrent = open_readb("current") ; - if (fdcurrent < 0) - if (errno != ENOENT) strerr_diefu1sys(111, "open_readb current") ; - else state = B_WAITING ; - else pos = nbcat(fdcurrent) ; + if (errno != ENOENT) strerr_diefu3sys(111, "open ", dir, "/current") ; + state = B_WAITING ; + } + else state = B_READING ; + if (sig_ignore(SIGPIPE) == -1) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; + if (state == B_READING) + { + if (!readit(fd)) strerr_diefu3sys(111, "read from ", dir, "/current") ; + } - for (;;) + for (;;) + { + int r ; + x[1].events = buffer_len(buffer_1) ? IOPAUSE_WRITE : 0 ; + r = iopause(x, 2, 0, 0) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_EXCEPT) x[0].revents |= IOPAUSE_READ ; + if (x[1].revents & IOPAUSE_EXCEPT) x[1].revents |= IOPAUSE_WRITE ; + if (x[1].revents & IOPAUSE_WRITE) { - int rr ; - if (!bufalloc_flush(bufalloc_1)) strerr_diefu1sys(111, "write to stdout") ; - rr = iopause(x, 1, 0, 0) ; - if (rr < 0) strerr_diefu1sys(111, "iopause") ; - if (x[0].revents & IOPAUSE_READ) - { - char iebuf[IESIZE] ; - while (bufalloc_len(bufalloc_1) < maxlen) - { - size_t i = 0 ; - ssize_t r = sanitize_read(fd_read(x[0].fd, iebuf, IESIZE)) ; - if (r < 0) strerr_diefu1sys(111, "read from inotify fd") ; - if (!r) break ; - while (i < (size_t)r) - { - struct inotify_event *ie = (struct inotify_event *)(iebuf + i) ; - if ((ie->wd != w) || !ie->len || strcmp(ie->name, "current")) goto cont ; - if (ie->mask & IN_MODIFY) - { - if (state) X() ; - pos += nbcat(fdcurrent) ; - } - else if (ie->mask & IN_CLOSE_WRITE) - { - if (state) X() ; - fd_close(fdcurrent) ; - fdcurrent = -1 ; - pos = 0 ; - state = B_WAITING ; - } - else if (ie->mask & IN_CREATE) - { - if (!state) X() ; - fdcurrent = open_readb("current") ; - if (fdcurrent < 0) - { - if (errno != ENOENT) strerr_diefu1sys(111, "open_readb current") ; - else goto cont ; - } - pos = nbcat(fdcurrent) ; - state = B_TAILING ; - } - cont: - i += sizeof(struct inotify_event) + ie->len ; - } - } - } + if (!buffer_flush(buffer_1) && !error_isagain(errno)) + strerr_diefu1sys(111, "write to stdout") ; + } + if (state == B_READING && buffer_available(buffer_1)) + { + if (!readit(fd)) strerr_diefu3sys(111, "read from ", dir, "/current") ; } + if (x[0].revents & IOPAUSE_READ) handle_event(x[0].fd, watch) ; } return 0 ; } |