about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-05-26 15:29:13 +0000
committerLaurent Bercot <ska@appnovation.com>2023-05-26 15:29:13 +0000
commit6c936711c61df41eeb936a98e6bc43584776ab08 (patch)
tree5680cc78faeee3ab07be73dfaacf1c77437ee6d7
parent3457a5ce01d7df3bc4cdc6259736ca210b4d4765 (diff)
downloads6-6c936711c61df41eeb936a98e6bc43584776ab08.tar.gz
s6-6c936711c61df41eeb936a98e6bc43584776ab08.tar.xz
s6-6c936711c61df41eeb936a98e6bc43584776ab08.zip
Add -t lastlinetimeout option to s6-log
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--NEWS1
-rw-r--r--doc/s6-log.html11
-rw-r--r--doc/upgrade.html2
-rw-r--r--src/daemontools-extras/s6-log.c71
4 files changed, 53 insertions, 32 deletions
diff --git a/NEWS b/NEWS
index 12b716d..20acc4c 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ In 2.11.4.0
 -----------
 
  - New option to s6-svc: -s, to specify a signal by name (or number).
+ - New option to s6-log: -t, to specify a timeout for partial last lines.
 
 
 In 2.11.3.2
diff --git a/doc/s6-log.html b/doc/s6-log.html
index a76f9aa..6e24e4c 100644
--- a/doc/s6-log.html
+++ b/doc/s6-log.html
@@ -27,7 +27,7 @@ with full POSIX regular expression support.
 <h2> Interface </h2>
 
 <pre>
-     s6-log [ -d <em>notif</em> ] [ -q | -v ] [ -b ] [ -p ] [ -l <em>linelimit</em> ] [ -- ] <em>logging script</em>
+     s6-log [ -d <em>notif</em> ] [ -q | -v ] [ -b ] [ -p ] [ -l <em>linelimit</em> ] [ -t <em>lastlinetimeout</em> ] [ -- ] <em>logging script</em>
 </pre>
 
 <p>
@@ -75,6 +75,15 @@ etc. <em>linelimit</em> cannot be less than 48, unless it is 0 (which means
 infinite). The default is 8192 bytes. Setting <em>linelimit</em> to 0 ensures
 that lines will never be split; this may cause important memory consumption
 by s6-log if it is fed extremely long lines, so use with caution. </li>
+ <li> <tt>-t&nbsp;<em>lastlinetimeout</em></tt>&nbsp;: if s6-log receives
+a termination signal but has a read a partial line in its buffer, it will
+wait for at most <em>lastlinetimeout</em> milliseconds for its service
+to send it the remainder of the line; if it still hasn't read a newline
+character by then, it will add a newline character itself and process the
+line, then exit. By default, <em>lastlinetimeout</em> is 2000, which means
+s6-log will wait for at most 2 seconds for completion of its last partial line.
+If <em>lastlinetimeout</em> is given as <strong>0</strong>, then s6-log
+will wait forever; it won't exit until it actually reads a newline or EOF. </li>
 </ul>
 
 <h2> Logdirs </h2>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index 4307f3e..2922250 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -23,6 +23,8 @@
 <ul>
  <li> <a href="//skarnet.org/software/skalibs/">skalibs</a>
 dependency bumped to 2.13.2.0. </li>
+ <li> New <tt>-s</tt> option to <a href="s6-svc.html">s6-svc</a>. </li>
+ <li> New <tt>-t</tt> option to <a href="s6-log.html">s6-log</a>. </li>
 </ul>
 
 <h2> in 2.11.3.2 </h2>
diff --git a/src/daemontools-extras/s6-log.c b/src/daemontools-extras/s6-log.c
index 94b67b0..45a46f0 100644
--- a/src/daemontools-extras/s6-log.c
+++ b/src/daemontools-extras/s6-log.c
@@ -39,7 +39,7 @@
 #include <execline/config.h>
 #endif
 
-#define USAGE "s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -- ] logging_script"
+#define USAGE "s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -t lastlinetimeout ] [ -- ] logging_script"
 #define dieusage() strerr_dieusage(100, USAGE)
 #define dienomem() strerr_diefu1sys(111, "stralloc_catb")
 
@@ -49,10 +49,11 @@ static mode_t mask ;
 static int flagprotect = 0 ;
 static int flagexiting = 0 ;
 static unsigned int verbosity = 1 ;
+static tain lastlinetto = TAIN_INFINITE_RELATIVE ;
+static tain exit_deadline = TAIN_INFINITE ;
 
 static stralloc indata = STRALLOC_ZERO ;
 
-
  /* Data types */
 
 typedef int qcmp_func (void const *, void const *) ;
@@ -1087,6 +1088,13 @@ static void normal_stdin (scriptelem_t const *script, unsigned int scriptlen, si
   }
 }
 
+static void process_partial_line (scriptelem_t const *script, unsigned int scriptlen, unsigned int gflags)
+{
+  if (!stralloc_0(&indata)) dienomem() ;
+  script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ;
+  indata.len = 0 ;
+}
+
 static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags)
 {
   for (;;)
@@ -1097,25 +1105,22 @@ static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, size
       case 0 : return ;
       case -1 :
         if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ;
-        if (!indata.len) goto eof ;
- addfinalnewline:
-        c = '\n' ;
+        if (indata.len) goto lastline ;
+        goto end ;
       case 1 :
+        if (c == '\n' || !c) goto lastline ;
         if (!stralloc_catb(&indata, &c, 1)) dienomem() ;
-        if (c == '\n')
-        {
-          script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ;
-          goto eof ;
-        }
-        else if (linelimit && indata.len > linelimit)
+        if (linelimit && indata.len >= linelimit)
         {
           if (verbosity) strerr_warnw2x("input line too long, ", "stopping before the end") ;
-          goto addfinalnewline ;
+          goto lastline ;
         }
-        break ;
+        else break ;
     }
   }
- eof:
+ lastline:
+  process_partial_line(script, scriptlen, gflags) ;
+ end:
   prepare_to_exit() ;
 }
 
@@ -1170,6 +1175,7 @@ static inline int handle_signals (void)
         if (flagprotect) break ;
       case SIGHUP :
         handle_stdin = &last_stdin ;
+        tain_add_g(&exit_deadline, &lastlinetto) ;
         if (!indata.len) { prepare_to_exit() ; e = 1 ; }
         break ;
       case SIGCHLD :
@@ -1203,9 +1209,10 @@ int main (int argc, char const *const *argv)
   PROG = "s6-log" ;
   {
     subgetopt l = SUBGETOPT_ZERO ;
+    unsigned int t = 2000 ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "qvbpl:d:", &l) ;
+      int opt = subgetopt_r(argc, argv, "qvbpl:d:t:", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
@@ -1219,10 +1226,13 @@ int main (int argc, char const *const *argv)
           if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ;
           if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ;
           break ;
+        case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
         default : dieusage() ;
       }
     }
     argc -= l.ind ; argv += l.ind ;
+    if (t) tain_from_millisecs(&lastlinetto, t) ;
+    else lastlinetto = tain_infinite_relative ;
   }
   if (!argc) dieusage() ;
   if (linelimit && linelimit < LINELIMIT_MIN) linelimit = LINELIMIT_MIN ;
@@ -1265,11 +1275,10 @@ int main (int argc, char const *const *argv)
 
     for (;;)
     {
-      tain deadline ;
+      tain deadline = exit_deadline ;
       int r = 0 ;
       unsigned int xindex0, xindex1 ;
       unsigned int i = 0, j = 1 ;
-      tain_add_g(&deadline, &tain_infinite_relative) ;
       if (bufalloc_1->fd == 1 && bufalloc_len(bufalloc_1))
       {
         r = 1 ;
@@ -1285,13 +1294,7 @@ int main (int argc, char const *const *argv)
         if (bufalloc_len(&logdirs[i].out) || (logdirs[i].rstate != ROTSTATE_WRITABLE))
         {
           r = 1 ;
-          if (!tain_future(&logdirs[i].deadline))
-          {
-            x[j].fd = logdirs[i].fd ;
-            x[j].events = IOPAUSE_WRITE ;
-            logdirs[i].xindex = j++ ;
-          }
-          else if (tain_less(&logdirs[i].deadline, &deadline))
+          if (tain_less(&logdirs[i].deadline, &deadline))
             deadline = logdirs[i].deadline ;
         }
       }
@@ -1307,7 +1310,18 @@ int main (int argc, char const *const *argv)
 
       r = iopause_g(x, j, &deadline) ;
       if (r < 0) strerr_diefu1sys(111, "iopause") ;
-      else if (!r) continue ;
+      else if (!r)
+      {
+        if (!tain_future(&exit_deadline))
+        {
+          if (indata.len) process_partial_line(script, scriptlen, gflags) ;
+          prepare_to_exit() ;
+        }
+        for (i = 0 ; i < llen ; i++)
+          if (!tain_future(&logdirs[i].deadline))
+            rotate_or_flush(logdirs + i) ;
+        continue ;
+      }
 
       if (x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT) && handle_signals()) continue ;
 
@@ -1336,13 +1350,8 @@ int main (int argc, char const *const *argv)
           (*handle_stdin)(script, scriptlen, linelimit, gflags) ;
         else
         {
+          if (indata.len) process_partial_line(script, scriptlen, gflags) ;
           prepare_to_exit() ;
-          if (indata.len)
-          {
-            if (!stralloc_0(&indata)) dienomem() ;
-            script_run(script, scriptlen, indata.s, indata.len-1, gflags) ;
-            indata.len = 0 ;
-          }
         }
       }
     }