commit 6c936711c61df41eeb936a98e6bc43584776ab08
parent 3457a5ce01d7df3bc4cdc6259736ca210b4d4765
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date: Fri, 26 May 2023 15:29:13 +0000
Add -t lastlinetimeout option to s6-log
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat:
4 files changed, 53 insertions(+), 32 deletions(-)
diff --git 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
@@ -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 <em>lastlinetimeout</em></tt> : 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
@@ -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
@@ -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 ;
- }
}
}
}