commit 9a669c4b3973118dfcce2a2b1fb4babd0aaa6af7
parent 2654165a84b90af2cbfef967e6e230b2975b644c
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date: Sat, 25 Mar 2017 11:25:15 +0000
Stop using flagwant; new s6-svstat API; s6-svwait's timeout exitcode is now 99
Diffstat:
8 files changed, 319 insertions(+), 58 deletions(-)
diff --git a/doc/s6-svlisten.html b/doc/s6-svlisten.html
@@ -85,7 +85,7 @@ given services comes up or down. </li>
given services come up or down. This is the default. </li>
<li> <tt>-t <em>timeout</em></tt> : if the requested events have not
happened after <em>timeout</em> milliseconds, s6-svlisten will print a message
-to stderr and exit 1. By default, <em>timeout</em> is 0, which means no time
+to stderr and exit 99. By default, <em>timeout</em> is 0, which means no time
limit. </li>
</ul>
diff --git a/doc/s6-svlisten1.html b/doc/s6-svlisten1.html
@@ -68,7 +68,7 @@ the service has been started or restarted and has notified
readiness. </li>
<li> <tt>-t <em>timeout</em></tt> : if the requested event has not
happened after <em>timeout</em> milliseconds, s6-svlisten1 will print a message
-to stderr and exit 1. By default, <em>timeout</em> is 0, which means no time
+to stderr and exit 99. By default, <em>timeout</em> is 0, which means no time
limit. </li>
</ul>
diff --git a/doc/s6-svstat.html b/doc/s6-svstat.html
@@ -26,13 +26,22 @@ monitored by <a href="s6-supervise.html">s6-supervise</a>.
<h2> Interface </h2>
<pre>
- s6-svstat [ -n ] <em>servicedir</em>
+ s6-svstat [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] <em>servicedir</em>
</pre>
<p>
s6-svstat gives information about the process being monitored
at the <em>servicedir</em> <a href="servicedir.html">service directory</a>, then
-exits 0. The information includes the following:
+exits 0.
+</p>
+
+<p>
+ When s6-svstat is invoked without options, or with only the <tt>-n</tt> option,
+it prints a human-readable summary of all the
+available information on the service. In this case, the <tt>-n</tt> option
+instructs it to print a signal number (instead of a signal name) if the
+supervised process was killed by a signal. The summary includes the
+following data:
</p>
<ul>
@@ -42,8 +51,6 @@ seconds that it has been up. </li>
signal, if it is down </li>
<li> what its default state is, if it is different from its current state </li>
<li> the number of seconds since it last changed states </li>
- <li> if the current state is transient and will change as soon as the
-kernel's scheduler picks up s6-supervise </li>
<li> whether the service is <a href="notifywhenup.html">ready</a>,
as notified by the daemon itself, and
if it is, the number of seconds that it has been.
@@ -53,12 +60,66 @@ cleanup phase, i.e. the <tt>./finish</tt> script is still being executed. </li>
</li>
</ul>
+<p>
+ When s6-svstat is invoked with one or several options other than <tt>-n</tt>,
+it outputs programmatically parsable information instead. The output is a series of
+space-separated values, one value per requested field. The valid options
+are as follows.
+</p>
+
<h2> Options </h2>
<ul>
- <li> <tt>-n</tt> : if the monitored process has been killed by a signal,
-print the signal number. By default, a symbolic name for the signal will be
-printed instead. </li>
+ <li> <tt>-o </tt><em>fields</em> : list fields to print.
+<em>fields</em> is a list of comma-separated field names. The valid field
+names are the following:
+ <ul>
+ <li> <tt>up</tt>: print <tt>true</tt> if the service is up and <tt>false</tt> if it is down. </li>
+ <li> <tt>wantedup</tt>: print <tt>true</tt> if <a href="s6-supervise.html">s6-supervise</a>
+is currently instructed to (re)start the service when it is down, and <tt>false</tt> if
+<a href="s6-supervise.html">s6-supervise</a> is currently instructed to leave the service alone. </li>
+ <li> <tt>normallyup</tt>: print <tt>true</tt> if the service is supposed to start when
+<a href="s6-supervise.html">s6-supervise</a> starts (i.e. no <tt>./down</tt> file), and <tt>false</tt>
+if it is not (i.e. there is a <tt>./down</tt> file). </li>
+ <li> <tt>ready</tt>: print <tt>true</tt> if the service is ready, and <tt>false</tt> if
+it is not. Note that a service can be both <em>down</em> and <em>ready</em>: it simply means that
+it is ready to be started (i.e. no <tt>./finish</tt> script is currently running). To check for
+service readiness, you should give this option along with <tt>up</tt>: the service is ready iff
+<tt>s6-svstat -o up,ready</tt> prints <tt>true true</tt>. </li>
+ <li> <tt>paused</tt>: print <tt>true</tt> if the service is paused (i.e. the process is
+currently stopped) and <tt>false</tt> if it is not. It is a rare flag, you shouldn't normally
+need to use this option. </li>
+ <li> <tt>pid</tt>: print the pid of the supervised process. If the service is currently down,
+<tt>-1</tt> is printed instead. </li>
+ <li> <tt>pid</tt>: print the exit code of the last execution of <tt>./run</tt>. If the
+service is currently up, or the last <tt>./run</tt> process was killed by a signal,
+<tt>-1</tt> is printed instead. </li>
+ <li> <tt>signal</tt>: print the name of the signal the last <tt>./run</tt> process was
+killed with. If the service is currently up, or the last <tt>./run</tt> process was not
+killed by a signal, <tt>NA</tt> is printed instead. </li>
+ <li> <tt>signum</tt>: print the number of the signal the last <tt>./run</tt> process was
+killed with. If the service is currently up, or the last <tt>./run</tt> process was not
+killed by a signal, <tt>-1</tt> is printed instead. </li>
+ <li> <tt>updownsince</tt>: print a <a href="http://cr.yp.to/libtai/tai64.html">TAI64N
+label</a> representing the absolute date when the service last changed states. </li>
+ <li> <tt>readysince</tt>: print a <a href="http://cr.yp.to/libtai/tai64.html">TAI64N
+label</a> representing the absolute date when the service last became ready. Note that
+this can either mean "service readiness" (if the service is currently up and ready), or
+"down readiness", i.e. the last time when the service was down and ready to be started
+(if the service is <em>not</em> currently up and ready). </li>
+ <li> <tt>updownfor</tt>: print the number of seconds that have elapsed since the
+service last changed states. </li>
+ <li> <tt>readyfor</tt>: print the number of seconds that have elapsed since the
+service last became ready (or ready to be started if it's currently not up and ready). </li>
+ </ul>
+ <li> <tt>-u</tt>: equivalent to <tt>-o up</tt>. </li>
+ <li> <tt>-w</tt>: equivalent to <tt>-o wantedup</tt>. </li>
+ <li> <tt>-N</tt>: equivalent to <tt>-o normallyup</tt>. </li>
+ <li> <tt>-r</tt>: equivalent to <tt>-o ready</tt>. </li>
+ <li> <tt>-p</tt>: equivalent to <tt>-o pid</tt>. </li>
+ <li> <tt>-e</tt>: equivalent to <tt>-o exitcode</tt>. </li>
+ <li> <tt>-s</tt>: equivalent to <tt>-o signal</tt>. </li>
+ <li> <tt>-t</tt>: equivalent to <tt>-o updownfor</tt>. </li>
</ul>
<h2> Exit codes </h2>
@@ -70,5 +131,23 @@ printed instead. </li>
<li> 111: system call failed </li>
</ul>
+<h2> Examples </h2>
+
+<ul>
+ <li> <tt>s6-svstat -o up,ready</tt> (or its equivalent <tt>s6-svstat -ur</tt>)
+will print <tt>true true</tt> if the service is up and
+ready, <tt>true false</tt> if the service has been started but has not notified
+readiness yet, <tt>false true</tt> if it is down and can be started, and
+<tt>false false</tt> if it is down and there's a <tt>./finish</tt> script running
+that needs to exit before the service can be restarted. </li>
+ <li> <tt>s6-svstat -o pid,exitcode,signal</tt> (or its equivalent <tt>s6-svstat -pes</tt>)
+will print <tt>42 -1 NA</tt> if the service has
+been started and <tt>./run</tt>'s pid is 42; it will print <tt>-1 0 NA</tt> if the
+service is down and <tt>./run</tt> last exited 0; it will print <tt>-1 -1 SIGTERM</tt>
+if the service is down and <tt>./run</tt> was last killed by a SIGTERM - as can
+happen, for instance, when you down the service via a call to
+<a href="s6-svc.html">s6-svc -d</a>. </li>
+</ul>
+
</body>
</html>
diff --git a/doc/s6-svwait.html b/doc/s6-svwait.html
@@ -64,7 +64,7 @@ given services comes up or down. </li>
given services come up or down. This is the default. </li>
<li> <tt>-t <em>timeout</em></tt> : if the requested events have not
happened after <em>timeout</em> milliseconds, s6-svwait will print a message
-to stderr and exit 1. By default, <em>timeout</em> is 0, which means no time
+to stderr and exit 99. By default, <em>timeout</em> is 0, which means no time
limit. </li>
</ul>
diff --git a/src/include/s6/s6-supervise.h b/src/include/s6/s6-supervise.h
@@ -26,7 +26,7 @@ struct s6_svstatus_s
int wstat ;
unsigned int flagpaused : 1 ;
unsigned int flagfinishing : 1 ;
- unsigned int flagwant : 1 ;
+ unsigned int flagwant : 1 ; /* unused */
unsigned int flagwantup : 1 ;
unsigned int flagready : 1 ;
} ;
diff --git a/src/supervision/s6-supervise.c b/src/supervision/s6-supervise.c
@@ -297,13 +297,13 @@ static void trystart (void)
static void downtimeout (void)
{
- if (status.flagwant && status.flagwantup) trystart() ;
+ if (status.flagwantup) trystart() ;
else settimeout_infinite() ;
}
static void down_O (void)
{
- status.flagwant = 0 ;
+ status.flagwantup = 0 ;
announce() ;
}
@@ -315,7 +315,6 @@ static void down_o (void)
static void down_u (void)
{
- status.flagwant = 1 ;
status.flagwantup = 1 ;
announce() ;
trystart() ;
@@ -323,7 +322,6 @@ static void down_u (void)
static void down_d (void)
{
- status.flagwant = 1 ;
status.flagwantup = 0 ;
announce() ;
}
@@ -391,13 +389,12 @@ static void uptimeout (void)
static void up_o (void)
{
- status.flagwant = 0 ;
+ status.flagwantup = 0 ;
announce() ;
}
static void up_d (void)
{
- status.flagwant = 1 ;
status.flagwantup = 0 ;
killt() ;
killc() ;
@@ -405,7 +402,6 @@ static void up_d (void)
static void up_u (void)
{
- status.flagwant = 1 ;
status.flagwantup = 1 ;
announce() ;
}
@@ -439,7 +435,7 @@ static void finish_z (void)
int wstat = (int)status.pid ;
if (WIFEXITED(wstat) && WEXITSTATUS(wstat) == 125)
{
- status.flagwant = 0 ;
+ status.flagwantup = 0 ;
set_down_and_ready("OD", 2) ;
}
else set_down_and_ready("D", 1) ;
@@ -447,7 +443,6 @@ static void finish_z (void)
static void finish_u (void)
{
- status.flagwant = 1 ;
status.flagwantup = 1 ;
announce() ;
}
diff --git a/src/supervision/s6-svstat.c b/src/supervision/s6-svstat.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <skalibs/uint64.h>
#include <skalibs/types.h>
+#include <skalibs/bytestr.h>
#include <skalibs/buffer.h>
#include <skalibs/strerr2.h>
#include <skalibs/sgetopt.h>
@@ -14,53 +15,169 @@
#include <skalibs/djbunix.h>
#include <s6/s6-supervise.h>
-#define USAGE "s6-svstat [ -n ] servicedir"
+#define USAGE "s6-svstat [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] servicedir"
#define dieusage() strerr_dieusage(100, USAGE)
-int main (int argc, char const *const *argv)
+#define MAXFIELDS 16
+#define checkfields() if (n >= MAXFIELDS) strerr_dief1x(100, "too many option fields")
+
+static int normallyup ;
+
+typedef void prfunc_t (buffer *, s6_svstatus_t const *) ;
+typedef prfunc_t * prfunc_t_ref ;
+
+typedef struct funcmap_s funcmap_t ;
+struct funcmap_s
{
- s6_svstatus_t status ;
- int flagnum = 0 ;
- int isup, normallyup ;
+ char const *s ;
+ prfunc_t_ref f ;
+} ;
+
+static void pr_up (buffer *b, s6_svstatus_t const *st)
+{
+ buffer_putsnoflush(b, st->pid && !st->flagfinishing ? "true" : "false") ;
+}
+
+static void pr_wantedup (buffer *b, s6_svstatus_t const *st)
+{
+ buffer_putsnoflush(b, st->flagwantup ? "true" : "false") ;
+}
+
+static void pr_ready (buffer *b, s6_svstatus_t const *st)
+{
+ buffer_putsnoflush(b, st->pid && st->flagready ? "true" : "false") ;
+}
+
+static void pr_paused (buffer *b, s6_svstatus_t const *st)
+{
+ buffer_putsnoflush(b, st->flagpaused ? "true" : "false") ;
+}
+
+static void pr_pid (buffer *b, s6_svstatus_t const *st)
+{
+ if (st->pid && !st->flagfinishing)
+ {
+ char fmt[PID_FMT] ;
+ buffer_putnoflush(b, fmt, pid_fmt(fmt, st->pid)) ;
+ }
+ else buffer_putsnoflush(b, "-1") ;
+}
+
+static void pr_tain (buffer *b, tain_t const *a)
+{
+ char fmt[TIMESTAMP] ;
+ buffer_putnoflush(b, fmt, timestamp_fmt(fmt, a)) ;
+}
+
+static void pr_stamp (buffer *b, s6_svstatus_t const *st)
+{
+ pr_tain(b, &st->stamp) ;
+}
+
+static void pr_readystamp (buffer *b, s6_svstatus_t const *st)
+{
+ pr_tain(b, &st->readystamp) ;
+}
+
+static void pr_seconds (buffer *b, tain_t const *a)
+{
+ tain_t d ;
char fmt[UINT64_FMT] ;
- PROG = "s6-svstat" ;
+ tain_sub(&d, &STAMP, a) ;
+ buffer_putnoflush(b, fmt, uint64_fmt(fmt, tai_sec(tain_secp(&d)))) ;
+}
+
+static void pr_upseconds (buffer *b, s6_svstatus_t const *st)
+{
+ pr_seconds(b, &st->stamp) ;
+}
+
+static void pr_readyseconds (buffer *b, s6_svstatus_t const *st)
+{
+ pr_seconds(b, &st->readystamp) ;
+}
+
+static void pr_exitcode (buffer *b, s6_svstatus_t const *st)
+{
+ int e = st->pid && !st->flagfinishing ? -1 :
+ WIFEXITED(st->wstat) ? WEXITSTATUS(st->wstat) : -1 ;
+ char fmt[INT_FMT] ;
+ buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ;
+}
+
+static void pr_signum (buffer *b, s6_svstatus_t const *st)
+{
+ int e = st->pid && !st->flagfinishing ? -1 :
+ WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ;
+ char fmt[INT_FMT] ;
+ buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ;
+}
+
+static void pr_signal (buffer *b, s6_svstatus_t const *st)
+{
+ int e = st->pid && !st->flagfinishing ? -1 :
+ WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ;
+ if (e == -1) buffer_putsnoflush(b, "NA") ;
+ else
{
- subgetopt_t l = SUBGETOPT_ZERO ;
- for (;;)
- {
- int opt = subgetopt_r(argc, argv, "n", &l) ;
- if (opt == -1) break ;
- switch (opt)
- {
- case 'n' : flagnum = 1 ; break ;
- default : dieusage() ;
- }
- }
- argc -= l.ind ; argv += l.ind ;
+ buffer_putsnoflush(b, "SIG") ;
+ buffer_putsnoflush(b, sig_name(e)) ;
}
- if (!argc) dieusage() ;
+}
- if (!s6_svstatus_read(*argv, &status))
- strerr_diefu2sys(111, "read status for ", *argv) ;
- isup = s6_svc_ok(argv[0]) ;
- if (isup < 0) strerr_diefu2sys(111, "check ", argv[0]) ;
- if (!isup) strerr_diefu3x(1, "read status for ", argv[0], ": s6-supervise not running") ;
+static void pr_normallyup (buffer *b, s6_svstatus_t const *st)
+{
+ buffer_putsnoflush(b, normallyup ? "true" : "false") ;
+ (void)st ;
+}
- tain_now_g() ;
- if (tain_future(&status.stamp)) tain_copynow(&status.stamp) ;
+static funcmap_t const fmtable[] =
+{
+ { .s = "up", .f = &pr_up },
+ { .s = "wantedup", .f = &pr_wantedup },
+ { .s = "normallyup", .f = &pr_normallyup },
+ { .s = "ready", .f = &pr_ready },
+ { .s = "paused", .f = &pr_paused },
+ { .s = "pid", .f = &pr_pid },
+ { .s = "exitcode", .f = &pr_exitcode },
+ { .s = "signal", .f = &pr_signal },
+ { .s = "signum", .f = &pr_signum },
+ { .s = "updownsince", .f = &pr_stamp },
+ { .s = "readysince", .f = &pr_readystamp },
+ { .s = "updownfor", .f = &pr_upseconds },
+ { .s = "readyfor", .f = &pr_readyseconds },
+ { .s = 0, .f = 0 }
+} ;
+
+static unsigned int parse_options (char const *arg, prfunc_t_ref *fields, unsigned int n)
+{
+ while (*arg)
{
- size_t dirlen = strlen(*argv) ;
- char fn[dirlen + 6] ;
- memcpy(fn, *argv, dirlen) ;
- memcpy(fn + dirlen, "/down", 6) ;
- if (access(fn, F_OK) < 0)
- if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ;
- else normallyup = 1 ;
- else normallyup = 0 ;
+ size_t pos = str_chr(arg, ',') ;
+ funcmap_t const *p = fmtable ;
+ if (!pos) strerr_dief1x(100, "invalid null option field") ;
+ for (; p->s ; p++) if (!strncmp(arg, p->s, pos)) break ;
+ if (!p->s)
+ {
+ char blah[pos+1] ;
+ memcpy(blah, arg, pos) ;
+ blah[pos] = 0 ;
+ strerr_dief2x(100, "invalid option field: ", blah) ;
+ }
+ checkfields() ;
+ fields[n++] = p->f ;
+ arg += pos ; if (*arg) arg++ ;
}
+ return n ;
+}
+
+static void legacy (s6_svstatus_t *st, int flagnum)
+{
+ s6_svstatus_t status = *st ;
+ int isup = status.pid && !status.flagfinishing ;
+ char fmt[UINT64_FMT] ;
- isup = status.pid && !status.flagfinishing ;
if (isup)
{
buffer_putnoflush(buffer_1small,"up (pid ", 8) ;
@@ -99,9 +216,9 @@ int main (int argc, char const *const *argv)
buffer_putnoflush(buffer_1small, ", normally up", 13) ;
if (isup && status.flagpaused)
buffer_putnoflush(buffer_1small, ", paused", 8) ;
- if (!isup && status.flagwant)
+ if (!isup && status.flagwantup)
buffer_putnoflush(buffer_1small, ", want up", 10) ;
- if (isup && !status.flagwant)
+ if (isup && !status.flagwantup)
buffer_putnoflush(buffer_1small, ", want down", 12) ;
if (status.flagready)
@@ -111,6 +228,76 @@ int main (int argc, char const *const *argv)
buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.readystamp.sec.x)) ;
buffer_putnoflush(buffer_1small, " seconds", 8) ;
}
+}
+
+int main (int argc, char const *const *argv)
+{
+ s6_svstatus_t status ;
+ int flagnum = 0 ;
+ prfunc_t_ref fields[MAXFIELDS] ;
+ unsigned int n = 0 ;
+ PROG = "s6-svstat" ;
+
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "no:uWNrpest", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : flagnum = 1 ; break ;
+ case 'o' : n = parse_options(l.arg, fields, n) ; break ;
+ case 'u' : checkfields() ; fields[n++] = &pr_up ; break ;
+ case 'w' : checkfields() ; fields[n++] = &pr_wantedup ; break ;
+ case 'N' : checkfields() ; fields[n++] = &pr_normallyup ; break ;
+ case 'r' : checkfields() ; fields[n++] = &pr_ready ; break ;
+ case 'p' : checkfields() ; fields[n++] = &pr_pid ; break ;
+ case 'e' : checkfields() ; fields[n++] = &pr_exitcode ; break ;
+ case 's' : checkfields() ; fields[n++] = &pr_signal ; break ;
+ case 't' : checkfields() ; fields[n++] = &pr_upseconds ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+ fields[n] = 0 ;
+
+ {
+ int r = s6_svc_ok(argv[0]) ;
+ if (r < 0) strerr_diefu2sys(111, "check ", argv[0]) ;
+ if (!r) strerr_diefu3x(1, "read status for ", argv[0], ": s6-supervise not running") ;
+ }
+ if (!s6_svstatus_read(argv[0], &status))
+ strerr_diefu2sys(111, "read status for ", argv[0]) ;
+
+ tain_now_g() ;
+ if (tain_future(&status.stamp)) tain_copynow(&status.stamp) ;
+
+ {
+ size_t dirlen = strlen(*argv) ;
+ char fn[dirlen + 6] ;
+ memcpy(fn, *argv, dirlen) ;
+ memcpy(fn + dirlen, "/down", 6) ;
+ if (access(fn, F_OK) < 0)
+ if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ;
+ else normallyup = 1 ;
+ else normallyup = 0 ;
+ }
+
+ if (!n) legacy(&status, flagnum) ;
+ else
+ {
+ unsigned int i = 0 ;
+ for (; fields[i] ; i++)
+ {
+ (*fields[i])(buffer_1small, &status) ;
+ buffer_putsnoflush(buffer_1small, " ") ;
+ }
+ buffer_unput(buffer_1small, 1) ;
+ }
+
if (buffer_putflush(buffer_1small, "\n", 1) < 0)
strerr_diefu1sys(111, "write to stdout") ;
return 0 ;
diff --git a/src/supervision/s6_svlisten_loop.c b/src/supervision/s6_svlisten_loop.c
@@ -52,7 +52,7 @@ int s6_svlisten_loop (s6_svlisten_t *foo, int wantup, int wantready, int or, tai
{
int r = iopause_g(x, 1 + (spfd >= 0), deadline) ;
if (r < 0) strerr_diefu1sys(111, "iopause") ;
- else if (!r) strerr_dief1x(1, "timed out") ;
+ else if (!r) strerr_dief1x(99, "timed out") ;
if (x[1].revents & IOPAUSE_READ) (*handler)() ;
if (x[0].revents & IOPAUSE_READ)
{