about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/minutils/s6-logwatch.c204
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 ;
 }