s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

commit 83853a80eb18238796154164f9ea776b0c167ab7
parent 87c5b2118efcee65eeda3f743d081ea9c2b866d9
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date:   Fri, 16 Jan 2015 01:36:48 +0000

 - s6-svlisten and s6-svlisten1
 - Synchronous s6-svc
 - version 2.0.2.0, rc

Diffstat:
Mdoc/index.html | 2++
Mdoc/s6-svc.html | 37+++++++++++++++++++++++++++++--------
Adoc/s6-svlisten.html | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/s6-svlisten1.html | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackage/deps.mak | 8+++++++-
Mpackage/modes | 2++
Mpackage/targets.mak | 2++
Msrc/libs6/s6_svc_main.c | 1-
Msrc/pipe-tools/s6-ftrig-listen.c | 4++--
Asrc/supervision/deps-exe/s6-svlisten | 5+++++
Asrc/supervision/deps-exe/s6-svlisten1 | 4++++
Msrc/supervision/s6-svc.c | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Asrc/supervision/s6-svlisten.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/supervision/s6-svlisten1.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/supervision/s6-svwait.c | 14++++++--------
15 files changed, 601 insertions(+), 23 deletions(-)

diff --git a/doc/index.html b/doc/index.html @@ -115,6 +115,8 @@ a user interface to control those processes and monitor service states. <li><a href="s6-svok.html">The <tt>s6-svok</tt> program</a></li> <li><a href="s6-svstat.html">The <tt>s6-svstat</tt> program</a></li> <li><a href="s6-svwait.html">The <tt>s6-svwait</tt> program</a></li> +<li><a href="s6-svlisten1.html">The <tt>s6-svlisten1</tt> program</a></li> +<li><a href="s6-svlisten.html">The <tt>s6-svlisten</tt> program</a></li> <li><a href="s6-notifywhenup.html">The <tt>s6-notifywhenup</tt> program</a> </li> </ul> diff --git a/doc/s6-svc.html b/doc/s6-svc.html @@ -27,7 +27,7 @@ knowing their PIDs, and without using horrible hacks such as .pid files. <h2> Interface </h2> <pre> - s6-svc [ -abqhkti12fFpcoduxO ] <em>servicedir</em> + s6-svc [ -D | -U ] [ -t <em>timeout</em> ] [ -abqhkti12pcoduxO ] <em>servicedir</em> </pre> <p> @@ -62,7 +62,17 @@ normally never be used on a working system. </li> <li> <tt>-O</tt>&nbsp;: Once at most. Do not restart the supervised process when it dies. If it is down when the command is received, do not even start it. </li> - <li> <tt>-f</tt>,&nbsp;<tt>-F</tt>&nbsp;: unused for now. </li> + <li> <tt>-t&nbsp;<em>timeout</em></tt>&nbsp;: if the <tt>-D</tt> or +<tt>-U</tt> option has been given, <tt>-t</tt> specifies a timeout +(in milliseconds) after which s6-svc will exit 1 with an error message if +the service still hasn't reached the desired state. By default, the +timeout is 0, which means that s6-svc will block indefinitely. </li> + <li> <tt>-D</tt>&nbsp;: s6-svc will not exit until the service is down. </li> + <li> <tt>-U</tt>&nbsp;: s6-svc will not exit until the service is up and +<a href="notifywhenup.html">ready</a> as notified by the daemon itself. +Be careful to only use this command on services that send readiness +notifications and are managed by <a href="s6-notifywhenup.html">s6-notifywhenup</a>, +else the command will never be successful. </li> </ul> <h2> Usage examples </h2> @@ -81,9 +91,15 @@ the process represented by the <tt>/service/sshd</tt> service directory - typically the sshd server. </p> -<pre> s6-svc -d /service/ftpd </pre> +<pre> s6-svc -Dd /service/ftpd </pre> <p> - Take down the ftpd server. + Take down the ftpd server and block until the process is really down. +</p> + +<pre> s6-svc -Uu -t 5000 /service/ftpd </pre> +<p> + Bring up the ftpd server and block until it has sent notification that it +is ready. Exit 1 if it is still not ready after 5 seconds. </p> <pre> s6-svc -a /service/httpd/log </pre> @@ -94,11 +110,16 @@ process is <a href="s6-log.html">s6-log</a>, this triggers a log rotation. <h2> Internals </h2> -<p> -s6-svc writes control commands into the <tt><em>servicedir</em>/supervise/control</tt> +<ul> + <li> s6-svc writes control commands into the <tt><em>servicedir</em>/supervise/control</tt> FIFO. A s6-supervise process running on <em>servicedir</em> will be listening to this FIFO, -and will read and interpret those commands. -</p> +and will read and interpret those commands. </li> + <li> When invoked with the <tt>-D</tt> or <tt>-U</tt> option, s6-svc executes into +<a href="s6-svlisten1.html">s6-svlisten1</a>, which will listen to service state +changes and spawn another s6-svc instance (without the <tt>-D</tt> or <tt>-U</tt> +option) that will send the commands to the service. Any error message written during +the waiting period will mention it is being written by s6-svlisten1; this is normal. </li> +</ul> </body> </html> diff --git a/doc/s6-svlisten.html b/doc/s6-svlisten.html @@ -0,0 +1,100 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-svlisten program</title> + <meta name="Description" content="s6: the s6-svlisten program" /> + <meta name="Keywords" content="s6 command s6-svlisten notification service waiting" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The s6-svlisten program </h1> + +<p> + s6-svlisten runs a program while listening on notifications from +a collection of supervised services, and blocks until they all go up, or down. +</p> + +<p> + s6-svlisten only waits for notifications; it never polls. +</p> + +<h2> Interface </h2> + +<p> + In an execline script: +</p> + +<pre> + s6-svlisten [ -U | -u | -d ] [ -a | -o ] [ -t <em>timeout</em> ] { <em>servicedir</em> <em>servicedir...</em> } <em>prog...</em> +</pre> + +<p> + Outside of an execline script: +</p> + +<pre> + s6-svlisten [ -U | -u | -d ] [ -a | -o ] [ -t <em>timeout</em> ] <em>servicedir</em> <em>servicedir...</em> "" <em>prog...</em> +</pre> + +<ul> + <li> s6-svlisten checks the state of one or more <a href="servicedir.html">service +directories</a> given as arguments in the first block and monitor +their state changes. </li> + <li> It spawns <em>prog...</em> as a child right after getting the +initial state of all the monitored services. </li> + <li> It then blocks until the wanted state happens, then exits 0. </li> +</p> + +<h2> Options </h2> + +<ul> + <li> <tt>-u</tt>&nbsp;: up. s6-svlisten will wait until the services are up, as +reported by s6-supervise. +This is the default; it is not reliable, but it does not depend on specific +support in the service programs. See <a href="notifywhenup.html">this page</a> +for details. </li> + <li> <tt>-U</tt>&nbsp;: really up. s6-svlisten will wait until the services are +up <em>and</em> ready as reported by the services themselves. This requires +specific support in the service programs, and the use of +<a href="s6-notifywhenup.html">s6-notifywhenup</a> in the service's run script. +See the explanation on <a href="notifywhenup.html">this page</a>. </li> + <li> <tt>-d</tt>&nbsp;: down. s6-svlisten will wait until the services are down. </li> + <li> <tt>-o</tt>&nbsp;: or. s6-svlisten will wait until <em>one</em> of the +given services comes up or down. </li> + <li> <tt>-a</tt>&nbsp;: and. s6-svlisten will wait until <em>all</em> of the +given services come up or down. This is the default. </li> + <li> <tt>-t&nbsp;<em>timeout</em></tt>&nbsp;: 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 +limit. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-svlisten is the service-specific version of +<a href="s6-ftrig-listen.html">s6-ftrig-listen</a>. The point of s6-svlisten +is to use it to spawn a program such as <a href="s6-svc.html">s6-svc</a>, +in order to send signals to services while making sure to catch their +state changes - thus avoiding the race condition that occurs when running +<a href="s6-svc.html">s6-svc</a> then <a href="s6-svwait.html">s6-svwait</a> +sequentially. </li> + <li> s6-svlisten needs to handle a variable length list of service directories. +For that, it uses an encoding provided by +<a href="http://skarnet.org/software/execline/">execline</a>, so it's best +to only use it in execline scripts (only the execline syntax is guaranteed +to not change). There is a variant of s6-svlisten that does not use execline +syntax, but only handles one service directory: +<a href="s6-svlisten1.html">s6-svlisten1</a>. </li> +</ul> + +</body> +</html> diff --git a/doc/s6-svlisten1.html b/doc/s6-svlisten1.html @@ -0,0 +1,81 @@ +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>s6: the s6-svlisten1 program</title> + <meta name="Description" content="s6: the s6-svlisten1 program" /> + <meta name="Keywords" content="s6 command s6-svlisten1 notification service waiting" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> + </head> +<body> + +<p> +<a href="index.html">s6</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The s6-svlisten1 program </h1> + +<p> + s6-svlisten1 runs a program while listening on notifications from a +supervised service, and blocks until said service goes up, or down. +</p> + +<p> + s6-svlisten1 only waits for notifications; it never polls. +</p> + +<h2> Interface </h2> + +<pre> + s6-svlisten [ -U | -u | -d ] [ -t <em>timeout</em> ] <em>servicedir</em> <em>prog...</em> +</pre> + +<ul> + <li> s6-svlisten1 checks the state of the <em>servicedir</em> +<a href="servicedir.html">service directory</a> and monitor its +state changes. </li> + <li> It spawns <em>prog...</em> as a child right after getting the +initial state of the service. </li> + <li> It then blocks until the wanted state happens, then exits 0. </li> +</p> + +<h2> Options </h2> + +<ul> + <li> <tt>-u</tt>&nbsp;: up. s6-svlisten1 will wait until the service is up, as +reported by s6-supervise. +This is the default; it is not reliable, but it does not depend on specific +support in the service programs. See <a href="notifywhenup.html">this page</a> +for details. </li> + <li> <tt>-U</tt>&nbsp;: really up. s6-svlisten1 will wait until the service is +up <em>and</em> ready as reported by the daemon itself. This requires +specific support in the daemon program, and the use of +<a href="s6-notifywhenup.html">s6-notifywhenup</a> in the service's run script. +See the explanation on <a href="notifywhenup.html">this page</a>. </li> + <li> <tt>-d</tt>&nbsp;: down. s6-svlisten1 will wait until the service is down. </li> + <li> <tt>-t&nbsp;<em>timeout</em></tt>&nbsp;: 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 +limit. </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> s6-svlisten1 is the service-specific version of +<a href="s6-ftrig-listen1.html">s6-ftrig-listen1</a>. The point of s6-svlisten1 +is to use it to spawn a program such as <a href="s6-svc.html">s6-svc</a>, +in order to send signals to a service while making sure to catch its +state changes - thus avoiding the race condition that occurs when running +<a href="s6-svc.html">s6-svc</a> then <a href="s6-svwait.html">s6-svwait</a> +sequentially. </li> + <li> The <a href="s6-svlisten.html">s6-svlisten</a> program is an extension +of s6-svlisten1. It can watch the state of several services at once; however, +its syntax makes it best used in <a href="http://skarnet.org/software/execline/">execline</a> +scripts only. </li> +</ul> + +</body> +</html> diff --git a/package/deps.mak b/package/deps.mak @@ -83,7 +83,9 @@ src/pipe-tools/s6-ftrig-notify.o src/pipe-tools/s6-ftrig-notify.lo: src/pipe-too src/pipe-tools/s6-ftrig-wait.o src/pipe-tools/s6-ftrig-wait.lo: src/pipe-tools/s6-ftrig-wait.c src/include/s6/ftrigr.h src/pipe-tools/s6-mkfifodir.o src/pipe-tools/s6-mkfifodir.lo: src/pipe-tools/s6-mkfifodir.c src/include/s6/ftrigw.h src/supervision/s6-supervise.o src/supervision/s6-supervise.lo: src/supervision/s6-supervise.c src/include/s6/ftrigw.h src/include/s6/s6-supervise.h -src/supervision/s6-svc.o src/supervision/s6-svc.lo: src/supervision/s6-svc.c src/include/s6/s6-supervise.h +src/supervision/s6-svc.o src/supervision/s6-svc.lo: src/supervision/s6-svc.c src/include/s6/config.h src/include/s6/s6-supervise.h +src/supervision/s6-svlisten.o src/supervision/s6-svlisten.lo: src/supervision/s6-svlisten.c src/include/s6/ftrigr.h src/include/s6/s6-supervise.h +src/supervision/s6-svlisten1.o src/supervision/s6-svlisten1.lo: src/supervision/s6-svlisten1.c src/include/s6/ftrigr.h src/include/s6/s6-supervise.h src/supervision/s6-svok.o src/supervision/s6-svok.lo: src/supervision/s6-svok.c src/include/s6/s6-supervise.h src/supervision/s6-svscan.o src/supervision/s6-svscan.lo: src/supervision/s6-svscan.c src/include/s6/config.h src/include/s6/s6-supervise.h src/supervision/s6-svscanctl.o src/supervision/s6-svscanctl.lo: src/supervision/s6-svscanctl.c src/include/s6/s6-supervise.h @@ -164,6 +166,10 @@ s6-supervise: private EXTRA_LIBS := ${TAINNOW_LIB} s6-supervise: src/supervision/s6-supervise.o ${LIBS6} -lskarnet s6-svc: private EXTRA_LIBS := s6-svc: src/supervision/s6-svc.o ${LIBS6} -lskarnet +s6-svlisten: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-svlisten: src/supervision/s6-svlisten.o ${LIBS6} -lexecline -lskarnet +s6-svlisten1: private EXTRA_LIBS := ${SOCKET_LIB} ${TAINNOW_LIB} +s6-svlisten1: src/supervision/s6-svlisten1.o ${LIBS6} -lskarnet s6-svok: private EXTRA_LIBS := s6-svok: src/supervision/s6-svok.o -lskarnet s6-svscan: private EXTRA_LIBS := ${TAINNOW_LIB} diff --git a/package/modes b/package/modes @@ -15,6 +15,8 @@ s6-svscanctl 0755 s6-svok 0755 s6-svstat 0755 s6-svwait 0755 +s6-svlisten1 0755 +s6-svlisten 0755 s6-applyuidgid 0700 s6-envdir 0755 s6-envuidgid 0755 diff --git a/package/targets.mak b/package/targets.mak @@ -15,6 +15,8 @@ s6-svscanctl \ s6-svok \ s6-svstat \ s6-svwait \ +s6-svlisten1 \ +s6-svlisten \ s6-envdir \ s6-envuidgid \ s6-fghack \ diff --git a/src/libs6/s6_svc_main.c b/src/libs6/s6_svc_main.c @@ -3,7 +3,6 @@ #include <skalibs/bytestr.h> #include <skalibs/sgetopt.h> #include <skalibs/strerr2.h> -#include <skalibs/skamisc.h> #include <s6/s6-supervise.h> #define DATASIZE 256 diff --git a/src/pipe-tools/s6-ftrig-listen.c b/src/pipe-tools/s6-ftrig-listen.c @@ -14,7 +14,7 @@ #include <execline/execline.h> #include <s6/ftrigr.h> -#define USAGE "s6-ftrig-listen [ -a | -o ] [ -t timeout ] ~fifodir1 ~regexp1 ... ; prog..." +#define USAGE "s6-ftrig-listen [ -a | -o ] [ -t timeout ] fifodir1 regexp1 ... \"\" prog..." #define dieusage() strerr_dieusage(100, USAGE) static void handle_signals (void) @@ -54,7 +54,7 @@ int main (int argc, char const **argv, char const *const *envp) if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ; } - if (argc < 2) dieusage() ; + if (argc < 4) dieusage() ; argc1 = el_semicolon(argv) ; if (!argc1 || (argc1 & 1) || (argc == argc1 + 1)) dieusage() ; if (argc1 >= argc) strerr_dief1x(100, "unterminated fifodir+regex block") ; diff --git a/src/supervision/deps-exe/s6-svlisten b/src/supervision/deps-exe/s6-svlisten @@ -0,0 +1,5 @@ +${LIBS6} +-lexecline +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/supervision/deps-exe/s6-svlisten1 b/src/supervision/deps-exe/s6-svlisten1 @@ -0,0 +1,4 @@ +${LIBS6} +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/supervision/s6-svc.c b/src/supervision/s6-svc.c @@ -1,12 +1,97 @@ /* ISC license. */ +#include <skalibs/uint.h> +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> #include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <s6/config.h> #include <s6/s6-supervise.h> -#define USAGE "s6-svc [ -abqhkti12fFpcoduxO ] servicedir" +#define USAGE "s6-svc [ -D | -U ] [ -t timeout ] [ -abqhkti12pcoduxO ] servicedir" +#define dieusage() strerr_dieusage(100, USAGE) -int main (int argc, char const *const *argv) +#define DATASIZE 63 + +int main (int argc, char const *const *argv, char const *const *envp) { + char data[DATASIZE+1] = "-" ; + unsigned int datalen = 1 ; + unsigned int timeout = 0 ; + char updown[3] = "-\0" ; PROG = "s6-svc" ; - return s6_svc_main(argc, argv, "abqhkti12fFpcoduxO", USAGE, "supervise") ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "DUabqhkti12pcoduxOT:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'D' : updown[1] = 'd' ; break ; + case 'U' : updown[1] = 'U' ; break ; + case 'a' : + case 'b' : + case 'q' : + case 'h' : + case 'k' : + case 't' : + case 'i' : + case '1' : + case '2' : + case 'p' : + case 'c' : + case 'o' : + case 'd' : + case 'u' : + case 'x' : + case 'O' : + { + if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ; + data[datalen++] = opt ; + break ; + } + case 'T' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + if (updown[1]) + { + char const *newargv[11] ; + unsigned int m = 0 ; + char fmt[UINT_FMT] ; + newargv[m++] = S6_BINPREFIX "s6-svlisten1" ; + newargv[m++] = updown ; + if (timeout) + { + fmt[uint_fmt(fmt, timeout)] = 0 ; + newargv[m++] = "-t" ; + newargv[m++] = fmt ; + } + newargv[m++] = "--" ; + newargv[m++] = argv[0] ; + newargv[m++] = S6_BINPREFIX "s6-svc" ; + newargv[m++] = data ; + newargv[m++] = "--" ; + newargv[m++] = argv[0] ; + newargv[m++] = 0 ; + pathexec_run(newargv[0], newargv, envp) ; + strerr_dieexec(111, newargv[0]) ; + } + else if (datalen > 1) + { + unsigned int arglen = str_len(argv[0]) ; + char tmp[arglen + 9 + sizeof(S6_SUPERVISE_CTLDIR)] ; + register int r ; + byte_copy(tmp, arglen, argv[0]) ; + tmp[arglen] = '/' ; + byte_copy(tmp + arglen + 1, 8 + sizeof(S6_SUPERVISE_CTLDIR), S6_SUPERVISE_CTLDIR "/control") ; + r = s6_svc_write(tmp, data + 1, datalen - 1) ; + if (r < 0) strerr_diefu2sys(111, "control ", argv[0]) ; + else if (!r) strerr_diefu3x(100, "control ", argv[0], ": supervisor not listening") ; + } + return 0 ; } diff --git a/src/supervision/s6-svlisten.c b/src/supervision/s6-svlisten.c @@ -0,0 +1,152 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint16.h> +#include <skalibs/uint.h> +#include <skalibs/bitarray.h> +#include <skalibs/tai.h> +#include <skalibs/strerr2.h> +#include <skalibs/iopause.h> +#include <skalibs/djbunix.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <execline/execline.h> +#include <s6/ftrigr.h> +#include <s6/s6-supervise.h> + +#define USAGE "s6-svlisten [ -U | -u | -d ] [ -A | -a | -o ] [ -t timeout ] servicedir... \"\" prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +static inline int check (unsigned char const *ba, unsigned int n, int wantup, int or) +{ + return (bitarray_first(ba, n, or == wantup) < n) == or ; +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGCHLD : wait_reap() ; break ; + default : strerr_dief1x(101, "unexpected data in selfpipe") ; + } +} + +int main (int argc, char const **argv, char const *const *envp) +{ + ftrigr_t a = FTRIGR_ZERO ; + tain_t deadline, tto ; + int spfd ; + int argc1 ; + int or = 0 ; + int wantup = 1 ; + char re[4] = "u|d" ; + PROG = "s6-svlisten" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "uUdAaot:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'U' : wantup = 1 ; re[0] = 'U' ; break ; + case 'u' : wantup = 1 ; re[0] = 'u' ; break ; + case 'd' : wantup = 0 ; break ; + case 'a' : or = 0 ; break ; + case 'o' : or = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; + } + if (argc < 3) dieusage() ; + + argc1 = el_semicolon(argv) ; + if (!argc1 || argc == argc1 + 1) dieusage() ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated servicedir block") ; + + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "selfpipe_trap") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore") ; + + tain_now_g() ; + tain_add_g(&deadline, &tto) ; + + if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; + + { + iopause_fd x[2] = { { .fd = spfd, .events = IOPAUSE_READ }, { .fd = ftrigr_fd(&a), .events = IOPAUSE_READ } } ; + pid_t pid ; + unsigned int i = 0 ; + uint16 list[argc1] ; + unsigned char states[bitarray_div8(argc1)] ; + for (; i < (unsigned int)argc1 ; i++) + { + unsigned int len = str_len(argv[i]) ; + char s[len + 1 + sizeof(S6_SUPERVISE_EVENTDIR)] ; + byte_copy(s, len, argv[i]) ; + s[len] = '/' ; + byte_copy(s + len + 1, sizeof(S6_SUPERVISE_EVENTDIR), S6_SUPERVISE_EVENTDIR) ; + list[i] = ftrigr_subscribe_g(&a, s, re, FTRIGR_REPEAT, &deadline) ; + if (!list[i]) strerr_diefu2sys(111, "subscribe to events for ", argv[i]) ; + } + + for (i = 0 ; i < (unsigned int)argc1 ; i++) + { + s6_svstatus_t st = S6_SVSTATUS_ZERO ; + int isup ; + if (!s6_svstatus_read(argv[i], &st)) strerr_diefu1sys(111, "s6_svstatus_read") ; + isup = !!st.pid ; + if (re[0] == 'U' && isup) + { + unsigned int len = str_len(argv[i]) ; + char s[len + 1 + sizeof(S6_SUPERVISE_READY_FILENAME)] ; + byte_copy(s, len, argv[i]) ; + s[len] = '/' ; + byte_copy(s + len + 1, sizeof(S6_SUPERVISE_READY_FILENAME), S6_SUPERVISE_READY_FILENAME) ; + if (access(s, F_OK) < 0) + { + if (errno == ENOENT) isup = 0 ; + else strerr_warnwu2sys("check ", s) ; + } + } + bitarray_poke(states, i, isup) ; + } + + pid = child_spawn0(argv[argc1 + 1], argv + argc1 + 1, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ; + + for (;;) + { + register int r ; + if (check(states, argc1, wantup, or)) break ; + r = iopause_g(x, 2, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + else if (!r) strerr_dief1x(1, "timed out") ; + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (x[1].revents & IOPAUSE_READ) + { + if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; + for (i = 0 ; i < (unsigned int)argc1 ; i++) + { + char what ; + register int r = ftrigr_check(&a, list[i], &what) ; + if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; + if (r) bitarray_poke(states, i, what == re[0]) ; + } + } + } + } + return 0 ; +} diff --git a/src/supervision/s6-svlisten1.c b/src/supervision/s6-svlisten1.c @@ -0,0 +1,121 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/sgetopt.h> +#include <skalibs/bytestr.h> +#include <skalibs/uint16.h> +#include <skalibs/uint.h> +#include <skalibs/tai.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> +#include <skalibs/iopause.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <s6/ftrigr.h> +#include <s6/s6-supervise.h> + +#define USAGE "s6-svlisten1 [ -U | -u | -d ] [ -t timeout ] servicedir prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGCHLD : wait_reap() ; break ; + default : strerr_dief1x(101, "unexpected data in selfpipe") ; + } +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + ftrigr_t a = FTRIGR_ZERO ; + tain_t deadline, tto ; + int spfd ; + int wantup = 1 ; + char re[4] = "u|d" ; + PROG = "s6-svlisten1" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "uUdt:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'U' : wantup = 1 ; re[0] = 'U' ; break ; + case 'u' : wantup = 1 ; re[0] = 'u' ; break ; + case 'd' : wantup = 0 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; + } + if (!argc) dieusage() ; + + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "selfpipe_trap") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore") ; + + tain_now_g() ; + tain_add_g(&deadline, &tto) ; + + if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ; + + { + iopause_fd x[2] = { { .fd = spfd, .events = IOPAUSE_READ }, { .fd = ftrigr_fd(&a), .events = IOPAUSE_READ } } ; + unsigned int arglen = str_len(argv[0]) ; + pid_t pid ; + int isup ; + s6_svstatus_t st = S6_SVSTATUS_ZERO ; + uint16 id ; + char s[arglen + 1 + sizeof(S6_SUPERVISE_EVENTDIR)] ; + byte_copy(s, arglen, argv[0]) ; + s[arglen] = '/' ; + byte_copy(s + arglen + 1, sizeof(S6_SUPERVISE_EVENTDIR), S6_SUPERVISE_EVENTDIR) ; + id = ftrigr_subscribe_g(&a, s, re, FTRIGR_REPEAT, &deadline) ; + if (!id) strerr_diefu2sys(111, "subscribe to events for ", argv[0]) ; + if (!s6_svstatus_read(argv[0], &st)) strerr_diefu1sys(111, "s6_svstatus_read") ; + isup = !!st.pid ; + if (re[0] == 'U' && isup) + { + byte_copy(s + arglen + 1, sizeof(S6_SUPERVISE_READY_FILENAME), S6_SUPERVISE_READY_FILENAME) ; + if (access(s, F_OK) < 0) + { + if (errno == ENOENT) isup = 0 ; + else strerr_warnwu2sys("check ", s) ; + } + } + + pid = child_spawn0(argv[1], argv + 1, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[2]) ; + + for (;;) + { + register int r ; + if (isup == wantup) break ; + r = iopause_g(x, 2, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + else if (!r) strerr_dief1x(1, "timed out") ; + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (x[1].revents & IOPAUSE_READ) + { + char what ; + if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ; + r = ftrigr_check(&a, id, &what) ; + if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ; + if (r) isup = what == re[0] ; + } + } + } + return 0 ; +} diff --git a/src/supervision/s6-svwait.c b/src/supervision/s6-svwait.c @@ -13,7 +13,7 @@ #include <s6/ftrigr.h> #include <s6/s6-supervise.h> -#define USAGE "s6-svwait [ -U | -u | -d ] [ -A | -a | -o ] [ -t timeout ] servicedir..." +#define USAGE "s6-svwait [ -U | -u | -d ] [ -a | -o ] [ -t timeout ] servicedir..." #define dieusage() strerr_dieusage(100, USAGE) static inline int check (unsigned char const *ba, unsigned int n, int wantup, int or) @@ -25,7 +25,6 @@ int main (int argc, char const *const *argv) { tain_t deadline, tto ; ftrigr_t a = FTRIGR_ZERO ; - uint32 options = FTRIGR_REPEAT ; int or = 0 ; int wantup = 1 ; char re[4] = "u|d" ; @@ -35,16 +34,15 @@ int main (int argc, char const *const *argv) unsigned int t = 0 ; for (;;) { - register int opt = subgetopt_r(argc, argv, "uUdAaot:", &l) ; + register int opt = subgetopt_r(argc, argv, "uUdaot:", &l) ; if (opt == -1) break ; switch (opt) { case 'U' : wantup = 1 ; re[0] = 'U' ; break ; case 'u' : wantup = 1 ; re[0] = 'u' ; break ; case 'd' : wantup = 0 ; break ; - case 'A' : or = 0 ; options |= FTRIGR_REPEAT ; break ; - case 'a' : or = 0 ; options &= ~FTRIGR_REPEAT ; break ; - case 'o' : or = 1 ; options &= ~FTRIGR_REPEAT ; break ; + case 'a' : or = 0 ; break ; + case 'o' : or = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; default : dieusage() ; } @@ -72,8 +70,8 @@ int main (int argc, char const *const *argv) byte_copy(s, len, argv[i]) ; s[len] = '/' ; byte_copy(s + len + 1, sizeof(S6_SUPERVISE_EVENTDIR), S6_SUPERVISE_EVENTDIR) ; - list[i] = ftrigr_subscribe_g(&a, s, re, options, &deadline) ; - if (!list[i]) strerr_diefu2sys(111, "ftrigr_subscribe to ", argv[i]) ; + list[i] = ftrigr_subscribe_g(&a, s, re, FTRIGR_REPEAT, &deadline) ; + if (!list[i]) strerr_diefu2sys(111, "subscribe to events for", argv[i]) ; } for (i = 0 ; i < (unsigned int)argc ; i++)