From 0a39023fd7229371dd3e505208a1c0e073414ac2 Mon Sep 17 00:00:00 2001
From: Laurent Bercot
Date: Mon, 15 Jun 2015 18:27:25 +0000
Subject: - Readiness notification support in s6-supervise - s6-notifywhenup
deprecated - Change abundantly documented - rc for 2.1.4.0
---
INSTALL | 4 +-
doc/index.html | 9 +--
doc/notifywhenup.html | 23 +++----
doc/s6-notifywhenup.html | 21 +++++++
doc/s6-supervise.html | 41 ++++++++++++
doc/s6-svlisten.html | 5 +-
doc/s6-svlisten1.html | 5 +-
doc/s6-svwait.html | 5 +-
doc/servicedir.html | 14 +++++
doc/upgrade.html | 12 ++++
package/info | 2 +-
src/supervision/s6-notifywhenup.c | 1 +
src/supervision/s6-supervise.c | 127 ++++++++++++++++++++++++++++++--------
13 files changed, 217 insertions(+), 52 deletions(-)
diff --git a/INSTALL b/INSTALL
index dddeb0c..684b138 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,8 +6,8 @@ Build Instructions
- A POSIX-compliant C development environment
- GNU make version 4.0 or later
- - skalibs version 2.3.2.0 or later: http://skarnet.org/software/skalibs/
- - execline version 2.1.1.0 or later: http://skarnet.org/software/execline/
+ - skalibs version 2.3.5.1 or later: http://skarnet.org/software/skalibs/
+ - execline version 2.1.2.2 or later: http://skarnet.org/software/execline/
This software will run on any operating system that implements
POSIX.1-2008, available at:
diff --git a/doc/index.html b/doc/index.html
index 9f49abb..29e8181 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -84,11 +84,11 @@ with s6
GNU make, version 4.0 or later. Please be aware that s6 will not build
with an earlier version.
skalibs version
-2.3.2.0 or later. It's a build-time requirement. It's also a run-time
+2.3.5.1 or later. It's a build-time requirement. It's also a run-time
requirement if you link against the shared version of the skalibs
library.
execline version
-2.1.1.0 or later. It's a build-time and run-time requirement.
+2.1.2.2 or later. It's a build-time and run-time requirement.
Licensing
@@ -101,7 +101,7 @@ library.
Download
Daemontools-like utilities
diff --git a/doc/notifywhenup.html b/doc/notifywhenup.html
index 6f9b06b..9a288ac 100644
--- a/doc/notifywhenup.html
+++ b/doc/notifywhenup.html
@@ -60,19 +60,16 @@ against the s6 library or use any s6-specific construct:
daemons can simply write a line to a file descriptor of their choice,
then close that file descriptor, when they're ready to serve. This is
a generic mechanism that some daemons already implement.
-The administrator can
-then run the daemon under s6-notifywhenup,
-which will properly catch the daemon's message and update a state file
-itself, then notify all the subscribers
-with a 'U' event, meaning that the service is now up.
- Note that there is still a small race condition remaining:
-if the daemon writes a line then instantly dies, and the supervisor
-picks up the death before the s6-notifywhenup
-program picks up the line, it is possible for the event sequence written
-to the fifodir to be wrong - 'd' before 'U'. This should be extremely
-rare, but unfortunately the race condition is unavoidable. The only
-way to be absolutely race-free is to have the daemon perform its
-readiness notification itself, which requires specific support.
+
+
+
+ s6 supports that mechanism natively: when the
+service directory for the daemon contains
+a valid notification-fd file, the daemon's supervisor, i.e. the
+s6-supervise program, will properly catch
+the daemon's message, update a state file (supervise/ready), then
+then notify all the subscribers
+with a 'U' event, meaning that the service is now up and ready.
diff --git a/doc/s6-notifywhenup.html b/doc/s6-notifywhenup.html
index cdd487c..cdfa693 100644
--- a/doc/s6-notifywhenup.html
+++ b/doc/s6-notifywhenup.html
@@ -18,6 +18,27 @@
The s6-notifywhenup program
+
+
+Starting with s6-2.1.4.0, the s6-notifywhenup program has been deprecated,
+because there was still a case (albeit extremely rare) where a race
+condition would occur and readiness would be improperly advertised.
+Readiness notification for a service can now be achieved via a
+notification-fd file in the
+service directory, containing the
+number of the descriptor the service will write its readiness
+notification newline to. The notification will directly be picked by
+s6-supervise.
+
+
+
+ Quick upgrade recipe: for every service using s6-notifywhenup,
+replace the s6-notifywhenup invocation in your run script with
+fdmove 1 3, then perform echo 3 > notification-fd.
+Done!
+
+
+
s6-notifywhenup launches a daemon while listening to a file descriptor,
and sends a 'U' event to a fifodir when it
diff --git a/doc/s6-supervise.html b/doc/s6-supervise.html
index 0c40aa1..78db1fa 100644
--- a/doc/s6-supervise.html
+++ b/doc/s6-supervise.html
@@ -59,6 +59,47 @@ if ./run exits too quickly.
sends a 'x' event to ./event before exiting 0.
+
Options
+
+
+ s6-supervise does not support options, because it is normally not run
+manually via a command line; it is usually launched by its own
+supervisor, s6-svscan.
+ However, the behaviour of an instance of s6-supervise can be tuned via
+various configuration files in the service directory. These files, and
+what they do, are listed on the
+service directory documentation page.
+
+
+ Readiness notification support
+
+
+ If the service directory contains a valid
+notification-fd file when the service is started, or restarted,
+s6-supervise creates and listens to an additional pipe from the service
+for readiness notification. When the
+notification occurs, s6-supervise creates a ./supervise/ready
+file containing the absolute time when readiness occurred, then sends
+a 'U' event to ./event. The ./supervise/ready
+file is deleted on service death.
+
+
+
+ If the service is logged, i.e. if the service directory has a
+log subdirectory that is also a service directory, and the
+s6-supervise process has been launched by
+that is also s6-svscan, then by default
+the service's stdout goes into the logging pipe. If you set
+notification-fd to 1, the logging pipe will be overwritten
+by the notification pipe, which is probably not what you want. Instead,
+if your daemon writes a notification message to its stdout, you should
+set notification-fd to (for instance) 3, and redirect outputs
+in your run script. For instance, to redirect stderr to the logger and
+stdout to a notification-fd set to 3, you would start your
+daemon as fdmove -c 2 1 fdmove 1 3 prog... (in execline), or
+exec 2>&1 1>&3 3<&- prog... (in shell).
+
+
Signals
diff --git a/doc/s6-svlisten.html b/doc/s6-svlisten.html
index c107b8e..2316a2b 100644
--- a/doc/s6-svlisten.html
+++ b/doc/s6-svlisten.html
@@ -64,8 +64,9 @@ support in the service programs. See this page
for details.
-U : really up. s6-svlisten will wait until the services are
up and ready as reported by the services themselves. This requires
-specific support in the service programs, and the use of
-s6-notifywhenup in the service's run script.
+specific support in the service programs, and the use of the
+notification-fd file in the
+service directory.
See the explanation on this page.
-d : down. s6-svlisten will wait until the services are down.
-o : or. s6-svlisten will wait until one of the
diff --git a/doc/s6-svlisten1.html b/doc/s6-svlisten1.html
index 5a45fc8..a9e147a 100644
--- a/doc/s6-svlisten1.html
+++ b/doc/s6-svlisten1.html
@@ -52,8 +52,9 @@ support in the service programs. See this page
for details.
-U : really up. s6-svlisten1 will wait until the service is
up and ready as reported by the daemon itself. This requires
-specific support in the daemon program, and the use of
-s6-notifywhenup in the service's run script.
+specific support in the service programs, and the use of the
+notification-fd file in the
+service directory.
See the explanation on this page.
-d : down. s6-svlisten1 will wait until the service is down.
-t timeout : if the requested event has not
diff --git a/doc/s6-svwait.html b/doc/s6-svwait.html
index 90bc7eb..de209ea 100644
--- a/doc/s6-svwait.html
+++ b/doc/s6-svwait.html
@@ -48,8 +48,9 @@ support in the service programs. See this page
for details.
-U : really up. s6-svwait will wait until the services are
up and ready as reported by the services themselves. This requires
-specific support in the service programs, and the use of
-s6-notifywhenup in the service's run script.
+specific support in the service programs, and the use of the
+notification-fd file in the
+service directory.
See the explanation on this page.
-d : down. s6-svwait will wait until the services are down.
-o : or. s6-svwait will wait until one of the
diff --git a/doc/servicedir.html b/doc/servicedir.html
index 501017b..7cebd53 100644
--- a/doc/servicedir.html
+++ b/doc/servicedir.html
@@ -103,6 +103,20 @@ automatically start it until it receives a s6-svc -u command. If no
s6-supervise will not make the service a process group and session leader; the service
will be run in the same process group as s6-supervise. If no nosetsid file
exists, the service has its own process group and is started as a session leader.
+ An optional regular file named notification-fd. If such a file
+exists, it means that the service supports
+readiness notification. The file must only
+ contain an unsigned integer, which is the number of the file descriptor that
+the service writes its readiness notification to. (For instance, it should
+be 1 if the daemon is s6-ipcserverd run with the
+-1 option.)
+ when a service is started, or restarted, by s6-supervise, if this file
+exists and contains a valid descriptor number, s6-supervise will wait for the
+notification from the service and broadcast readiness, i.e. any
+s6-svwait -U,
+s6-svlisten1 -U or
+s6-svlisten -U processes will be
+triggered.
A fifodir named event. It is automatically
created by s6-supervise if it does not exist.
foo/event
diff --git a/doc/upgrade.html b/doc/upgrade.html
index 67e0ba1..a124800 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -18,6 +18,18 @@
What has changed in s6
+ in 2.1.4.0
+
+
+
in 2.1.3.0
diff --git a/package/info b/package/info
index 97e35d3..da7ff9f 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
package=s6
-version=2.1.3.0
+version=2.1.4.0
category=admin
package_macro_name=S6
diff --git a/src/supervision/s6-notifywhenup.c b/src/supervision/s6-notifywhenup.c
index 6d66fe1..8c45926 100644
--- a/src/supervision/s6-notifywhenup.c
+++ b/src/supervision/s6-notifywhenup.c
@@ -72,6 +72,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
argc -= l.ind ; argv += l.ind ;
}
if (!argc) dieusage() ;
+ strerr_warnw1x("this program is deprecated. Use a notification-fd file instead.") ;
{
int p[2] ;
diff --git a/src/supervision/s6-supervise.c b/src/supervision/s6-supervise.c
index 4aaf371..13c751e 100644
--- a/src/supervision/s6-supervise.c
+++ b/src/supervision/s6-supervise.c
@@ -1,7 +1,6 @@
/* ISC license. */
#include
-#include
#include
#include
#include
@@ -47,8 +46,8 @@ typedef action_t *action_t_ref ;
static tain_t deadline ;
static s6_svstatus_t status = { .stamp = TAIN_ZERO, .pid = 0, .flagwant = 1, .flagwantup = 1, .flagpaused = 0, .flagfinishing = 0, .wstat = 0 } ;
static state_t state = DOWN ;
-static int flagsetsid = 1 ;
static int cont = 1 ;
+static int notifyfd = -1 ;
static inline void settimeout (int secs)
{
@@ -137,21 +136,64 @@ static void killc (void)
announce() ;
}
+static void failcoe (int fd)
+{
+ register int e = errno ;
+ fd_write(fd, "", 1) ;
+ errno = e ;
+}
+
+static int maybesetsid (void)
+{
+ if (access("nosetsid", F_OK) < 0)
+ {
+ if (errno != ENOENT) return 0 ;
+ setsid() ;
+ }
+ return 1 ;
+}
+
static void trystart (void)
{
int p[2] ;
+ int notifyp[2] = { -1, -1 } ;
+ unsigned int fd ;
pid_t pid ;
if (pipecoe(p) < 0)
{
settimeout(60) ;
- strerr_warnwu1sys("pipecoe (waiting 60 seconds)") ;
+ strerr_warnwu1sys("pipe (waiting 60 seconds)") ;
return ;
}
+ {
+ char buf[UINT_FMT + 1] ;
+ register int r = openreadnclose("notification-fd", buf, UINT_FMT) ;
+ if (r < 0)
+ {
+ if (errno != ENOENT)
+ strerr_warnwu1sys("open notification-fd") ;
+ }
+ else
+ {
+ buf[byte_chr(buf, r, '\n')] = 0 ;
+ if (!uint0_scan(buf, &fd))
+ strerr_warnw1x("invalid notification-fd") ;
+ else if (pipe(notifyp) < 0)
+ {
+ settimeout(60) ;
+ strerr_warnwu1sys("pipe (waiting 60 seconds)") ;
+ fd_close(p[1]) ; fd_close(p[0]) ;
+ return ;
+ }
+ }
+ }
pid = fork() ;
if (pid < 0)
{
settimeout(60) ;
strerr_warnwu1sys("fork (waiting 60 seconds)") ;
+ if (notifyp[1] >= 0) fd_close(notifyp[1]) ;
+ if (notifyp[0] >= 0) fd_close(notifyp[0]) ;
fd_close(p[1]) ; fd_close(p[0]) ;
return ;
}
@@ -160,14 +202,25 @@ static void trystart (void)
char const *cargv[2] = { "run", 0 } ;
PROG = "s6-supervise (child)" ;
selfpipe_finish() ;
- fd_close(p[0]) ;
+ if (notifyp[0] >= 0) close(notifyp[0]) ;
+ close(p[0]) ;
if (unlink(S6_SUPERVISE_READY_FILENAME) < 0 && errno != ENOENT)
strerr_warnwu1sys("unlink " S6_SUPERVISE_READY_FILENAME) ;
- if (flagsetsid) setsid() ;
+ if (notifyp[1] >= 0 && fd_move((int)fd, notifyp[1]) < 0)
+ {
+ failcoe(p[1]) ;
+ strerr_diefu1sys(127, "move notification descriptor") ;
+ }
+ if (!maybesetsid())
+ {
+ failcoe(p[1]) ;
+ strerr_diefu1sys(127, "access ./nosetsid") ;
+ }
execve("./run", (char *const *)cargv, (char *const *)environ) ;
- fd_write(p[1], "", 1) ;
+ failcoe(p[1]) ;
strerr_dieexec(127, "run") ;
}
+ if (notifyp[1] >= 0) fd_close(notifyp[1]) ;
fd_close(p[1]) ;
{
char c ;
@@ -189,6 +242,7 @@ static void trystart (void)
}
}
fd_close(p[0]) ;
+ notifyfd = notifyp[0] ;
settimeout_infinite() ;
state = UP ;
status.pid = pid ;
@@ -249,7 +303,7 @@ static inline void tryfinish (int islast)
selfpipe_finish() ;
fmt0[uint_fmt(fmt0, WIFSIGNALED(status.wstat) ? 256 : WEXITSTATUS(status.wstat))] = 0 ;
fmt1[uint_fmt(fmt1, WTERMSIG(status.wstat))] = 0 ;
- if (flagsetsid) setsid() ;
+ maybesetsid() ;
execve("./finish", cargv, (char *const *)environ) ;
_exit(127) ;
}
@@ -269,6 +323,11 @@ static void uplastup_z (int islast)
status.wstat = status.pid ;
status.pid = 0 ;
tain_copynow(&status.stamp) ;
+ if (notifyfd >= 0)
+ {
+ fd_close(notifyfd) ;
+ notifyfd = -1 ;
+ }
tryfinish(islast) ;
announce() ;
ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "d", 1) ;
@@ -368,11 +427,36 @@ static action_t_ref const actions[5][23] =
} ;
+
/* The main loop.
It just loops around the iopause(), calling snippets of code in "actions" when needed. */
-static void handle_signals (void)
+static inline void handle_notifyfd (void)
+{
+ char buf[4096] ;
+ register int r = 1 ;
+ while (r > 0)
+ {
+ r = sanitize_read(fd_read(notifyfd, buf, 4096)) ;
+ if (r > 0 && byte_chr(buf, r, '\n') < r)
+ {
+ char pack[TAIN_PACK] ;
+ tain_pack(pack, &STAMP) ;
+ if (!openwritenclose_suffix(S6_SUPERVISE_READY_FILENAME, pack, TAIN_PACK, ".new"))
+ strerr_warnwu3sys("open ", S6_SUPERVISE_READY_FILENAME, " for writing") ;
+ ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "U", 1) ;
+ r = -1 ;
+ }
+ if (r < 0)
+ {
+ fd_close(notifyfd) ;
+ notifyfd = -1 ;
+ }
+ }
+}
+
+static inline void handle_signals (void)
{
for (;;)
{
@@ -410,7 +494,7 @@ static void handle_signals (void)
}
}
-static void handle_control (int fd)
+static inline void handle_control (int fd)
{
for (;;)
{
@@ -428,7 +512,7 @@ static void handle_control (int fd)
int main (int argc, char const *const *argv)
{
- iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ iopause_fd x[3] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
PROG = "s6-supervise" ;
if (argc < 2) strerr_dieusage(100, USAGE) ;
if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ;
@@ -460,21 +544,9 @@ int main (int argc, char const *const *argv)
if (!ftrigw_clean(S6_SUPERVISE_EVENTDIR))
strerr_warnwu2sys("ftrigw_clean ", S6_SUPERVISE_EVENTDIR) ;
- {
- struct stat st ;
- if (stat("down", &st) == -1)
- {
- if (errno != ENOENT)
- strerr_diefu1sys(111, "stat down") ;
- }
- else status.flagwantup = 0 ;
- if (stat("nosetsid", &st) == -1)
- {
- if (errno != ENOENT)
- strerr_diefu1sys(111, "stat nosetsid") ;
- }
- else flagsetsid = 0 ;
- }
+ if (access("down", F_OK) == 0) status.flagwantup = 0 ;
+ else if (errno != ENOENT)
+ strerr_diefu1sys(111, "access ./down") ;
tain_now_g() ;
settimeout(0) ;
@@ -484,13 +556,16 @@ int main (int argc, char const *const *argv)
while (cont)
{
- register int r = iopause_g(x, 2, &deadline) ;
+ register int r ;
+ x[2].fd = notifyfd ;
+ r = iopause_g(x, 2 + (notifyfd >= 0), &deadline) ;
if (r < 0) strerr_diefu1sys(111, "iopause") ;
else if (!r) (*actions[state][V_TIMEOUT])() ;
else
{
if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT)
strerr_diefu1x(111, "iopause: trouble with pipes") ;
+ if (notifyfd >= 0 && x[2].revents & IOPAUSE_READ) handle_notifyfd() ;
if (x[0].revents & IOPAUSE_READ) handle_signals() ;
else if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ;
}
--
cgit v1.2.3