Add 'notify:pid' style readiness notifaction and 'readiness none'

This change expands the readiness notification system in Finit with the
native 'pid' style, which will remain the default readiness in Finit 4.x

For systems that want to transition to Finit 5.x early, a global option
to set 'readiness none' in /etc/finit.conf, has been added.  This change
the service default notification mode to 'notify:none', which can also
be set by Finit 4.x ('readiness pid') for select services.

Fixes #386.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2023-11-12 19:07:12 +01:00
1 parent 6443995fc9
commit 0b5c555c7f
10 files changed
+88 -50

No files matched your search

+19 -5
View File
@@ -356,17 +356,31 @@ most notably systemd and s6, other methods for signaling "readiness"
arrived and daemons were adapted to these new schemes to a larger arrived and daemons were adapted to these new schemes to a larger
extent. extent.
As of Finit v4.4 partial support for systemd and s6 is available. The As of Finit v4.4 partial support for systemd and s6 style readiness
native PID file mode of operation is still default, the other's can be notification is available, and the native PID file mode of operation is,
defined per service, like this: as of Finit v4.6 optional, by default it is still enabled, but this can
be changed in `finit.conf`:
readiness none
This will be made the default in Finit 5.0. In this mode of operation,
every service needs to explicitly declare their readiness notification,
like this:
service notify:pid watchdogd
service notify:systemd foo service notify:systemd foo
service notify:s6 bar service notify:s6 bar
service notify:none qux
The `notify:none` syntax is for completeness in systems which run in
`readiness pid` mode (default). Services declared with `notify:none`
will assert ready condition as soon as Finit has started them, e.g.,
`service/qux/ready`.
To synchronize two services the following condition can be used: To synchronize two services the following condition can be used:
service notify:systemd A service notify:pid watchdogd
service <service/A/ready> B service <service/watchdogd/ready> stress-ng --cpu 8
For details on the syntax and options, see below. For details on the syntax and options, see below.
+2 -2
View File
@@ -150,11 +150,11 @@ static void setup(void *arg)
* Note: dbus drops privs after starting up. * Note: dbus drops privs after starting up.
*/ */
if (pidfn) { if (pidfn) {
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus pid:!%s %s %s -- %s", snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus pid:!%s %s %s -- %s",
pidfn, cmd, DBUS_ARGS, DBUS_DESC); pidfn, cmd, DBUS_ARGS, DBUS_DESC);
free(pidfn); free(pidfn);
} else } else
snprintf(line, sizeof(line), "[S123456789] cgroup.system name:dbus %s %s -- %s", snprintf(line, sizeof(line), "[S123456789] cgroup.system notify:none name:dbus %s %s -- %s",
cmd, DBUS_ARGS, DBUS_DESC); cmd, DBUS_ARGS, DBUS_DESC);
conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf"); conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf");
+4 -4
View File
@@ -88,7 +88,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
* systemd style services rely on their respective * systemd style services rely on their respective
* readiness notification. Issue #343 * readiness notification. Issue #343
*/ */
if (!svc->notify) if (svc->notify == SVC_NOTIFY_PID)
svc_started(svc); svc_started(svc);
if (!svc_has_pidfile(svc)) { if (!svc_has_pidfile(svc)) {
@@ -121,11 +121,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
} }
cond_set(cond); cond_set(cond);
if (!svc->notify) if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc); service_ready(svc);
} else if (mask & IN_DELETE) { } else if (mask & IN_DELETE) {
cond_clear(cond); cond_clear(cond);
if (!svc->notify) if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc); service_ready(svc);
} }
} }
@@ -240,7 +240,7 @@ static void pidfile_reconf(void *arg)
if (svc_is_changed(svc) || svc_is_starting(svc)) if (svc_is_changed(svc) || svc_is_starting(svc))
continue; continue;
if (!svc->notify) if (svc->notify == SVC_NOTIFY_PID)
service_ready(svc); service_ready(svc);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
+15 -3
View File
@@ -958,6 +958,18 @@ static int parse_static(char *line, int is_rcsd)
return 0; return 0;
} }
/*
* Before Finit 5 the default readiness notification is PID.
* This default can be changed by setting 'readiness none' in
* the file /etc/finit.conf, only read once at bootstrap.
*/
if (BOOTSTRAP && MATCH_CMD(line, "readiness ", x)) {
char *token = strip_line(x);
if (!strcmp(token, "none"))
readiness = SVC_NOTIFY_NONE;
}
if (MATCH_CMD(line, "reboot-delay ", x)) { if (MATCH_CMD(line, "reboot-delay ", x)) {
syncsec = strtonum(strip_line(x), 0, 60, NULL); syncsec = strtonum(strip_line(x), 0, 60, NULL);
return 0; return 0;
@@ -1579,7 +1591,7 @@ int conf_init(uev_ctx_t *ctx)
*/ */
#ifdef WDT_DEVNODE #ifdef WDT_DEVNODE
if (whichp(FINIT_EXECPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) { if (whichp(FINIT_EXECPATH_ "/watchdogd") && fexist(WDT_DEVNODE)) {
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init name:watchdog :finit " conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init notify:none name:watchdog :finit "
FINIT_EXECPATH_ "/watchdogd -- Finit watchdog daemon", "watchdogd.conf"); FINIT_EXECPATH_ "/watchdogd -- Finit watchdog daemon", "watchdogd.conf");
} }
#endif #endif
@@ -1587,7 +1599,7 @@ int conf_init(uev_ctx_t *ctx)
* Start kernel event daemon as soon as possible, if enabled * Start kernel event daemon as soon as possible, if enabled
*/ */
if (whichp(FINIT_EXECPATH_ "/keventd")) if (whichp(FINIT_EXECPATH_ "/keventd"))
conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init " conf_save_service(SVC_TYPE_SERVICE, "[S0123456789] cgroup.init notify:none "
FINIT_EXECPATH_ "/keventd -- Finit kernel event daemon", "keventd.conf"); FINIT_EXECPATH_ "/keventd -- Finit kernel event daemon", "keventd.conf");
dbg("Allow plugins to register early runlevel 1 run/task/services ..."); dbg("Allow plugins to register early runlevel 1 run/task/services ...");
@@ -1621,7 +1633,7 @@ int conf_init(uev_ctx_t *ctx)
if (runparts_sysv) if (runparts_sysv)
strlcat(args, "-s ", sizeof(args)); strlcat(args, "-s ", sizeof(args));
snprintf(conf, sizeof(conf), "[S] <int/bootstrap> log:console %s %s %s" snprintf(conf, sizeof(conf), "[S] <int/bootstrap> notify:none log:console %s %s %s"
" -- Calling runparts %s in the background", " -- Calling runparts %s in the background",
_PATH_RUNPARTS, args, runparts, runparts); _PATH_RUNPARTS, args, runparts, runparts);
conf_save_service(SVC_TYPE_TASK, conf, "runparts.conf"); conf_save_service(SVC_TYPE_TASK, conf, "runparts.conf");
+1
View File
@@ -69,6 +69,7 @@ int single = 0; /* single user mode from kernel cmdline */
int bootstrap = 1; /* set while bootrapping (for TTYs) */ int bootstrap = 1; /* set while bootrapping (for TTYs) */
int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */
int syncsec = 0; /* reboot delay */ int syncsec = 0; /* reboot delay */
int readiness = SVC_NOTIFY_PID;
char *finit_conf= NULL; char *finit_conf= NULL;
char *finit_rcsd= NULL; char *finit_rcsd= NULL;
char *fstab = NULL; char *fstab = NULL;
+1
View File
@@ -121,6 +121,7 @@ extern int single;
extern int bootstrap; extern int bootstrap;
extern int kerndebug; extern int kerndebug;
extern int syncsec; extern int syncsec;
extern int readiness;
extern char *fstab; extern char *fstab;
extern char *sdown; extern char *sdown;
extern char *network; extern char *network;
+32 -30
View File
@@ -701,11 +701,12 @@ static int service_start(svc_t *svc)
wordexp_t we = { 0 }; wordexp_t we = { 0 };
int rc; int rc;
if (svc->notify) { if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) {
if (client_command(INIT_CMD_NOTIFY_SOCKET)) { if (client_command(INIT_CMD_NOTIFY_SOCKET)) {
err(1, "%s: failed creating notify socket", svc_ident(svc, NULL, 0)); err(1, "%s: failed creating notify socket", svc_ident(svc, NULL, 0));
client_disconnect(); client_disconnect();
svc->notify = 0; /* does not change parent, restarting may work. */ /* does not change parent, restarting may work. */
svc->notify = SVC_NOTIFY_NONE;
} else { } else {
char val[20]; char val[20];
@@ -730,7 +731,7 @@ static int service_start(svc_t *svc)
if (len == 0) if (len == 0)
break; break;
if (svc->notify) { if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) {
char *ptr = strstr(arg, "%n"); char *ptr = strstr(arg, "%n");
if (ptr) { if (ptr) {
@@ -907,6 +908,19 @@ static void service_kill(svc_t *svc)
print(2, NULL); print(2, NULL);
} }
/* Ensure we don't have any notify socket lingering */
static void service_notify_stop(svc_t *svc)
{
if (svc->notify != SVC_NOTIFY_SYSTEMD && svc->notify != SVC_NOTIFY_S6)
return;
uev_io_stop(&svc->notify_watcher);
if (svc->notify_watcher.fd > 0) {
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
}
/* /*
* Clean up any lingering state from dead/killed services * Clean up any lingering state from dead/killed services
*/ */
@@ -922,12 +936,7 @@ static void service_cleanup(svc_t *svc)
logit(LOG_CRIT, "Failed removing service %s pidfile %s", logit(LOG_CRIT, "Failed removing service %s pidfile %s",
svc_ident(svc, NULL, 0), fn); svc_ident(svc, NULL, 0), fn);
/* Ensure we don't have any notify socket lingering */ service_notify_stop(svc);
if (svc->notify && svc->notify_watcher.fd > 0) {
uev_io_stop(&svc->notify_watcher);
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
/* No longer running, update books. */ /* No longer running, update books. */
if (svc_is_tty(svc) && svc->pid > 1) if (svc_is_tty(svc) && svc->pid > 1)
@@ -1188,13 +1197,15 @@ static void parse_log(svc_t *svc, char *arg)
} }
} }
static int parse_notify(char *arg) static svc_notify_t parse_notify(char *arg)
{ {
if (!strcmp(arg, "systemd")) if (!strcmp(arg, "systemd"))
return 1; return SVC_NOTIFY_SYSTEMD;
if (!strcmp(arg, "s6")) if (!strcmp(arg, "s6"))
return 2; return SVC_NOTIFY_S6;
return 0; /* unsupported/disabled */ if (!strcmp(arg, "pid"))
return SVC_NOTIFY_PID;
return SVC_NOTIFY_NONE; /* unsupported/none */
} }
static void parse_env(svc_t *svc, char *env) static void parse_env(svc_t *svc, char *env)
@@ -1759,17 +1770,12 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
else else
svc->log.enabled = 0; svc->log.enabled = 0;
} }
if (notify) {
int type = parse_notify(notify);
if (type <= 0) if (notify)
goto disable_notify; svc->notify = parse_notify(notify);
svc->notify = type; else
} else if (svc->notify) { svc->notify = readiness;
disable_notify:
/* XXX: close existing socket */
svc->notify = 0;
}
if (desc) if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc)); strlcpy(svc->desc, desc, sizeof(svc->desc));
else if (type == SVC_TYPE_TTY) else if (type == SVC_TYPE_TTY)
@@ -2373,11 +2379,7 @@ restart:
char condstr[MAX_COND_LEN]; char condstr[MAX_COND_LEN];
dbg("%s: stopped, cleaning up timers and conditions ...", svc_ident(svc, NULL, 0)); dbg("%s: stopped, cleaning up timers and conditions ...", svc_ident(svc, NULL, 0));
if (svc->notify && svc->notify_watcher.fd > 0) { service_notify_stop(svc);
uev_io_stop(&svc->notify_watcher);
close(svc->notify_watcher.fd);
svc->notify_watcher.fd = 0;
}
service_timeout_cancel(svc); service_timeout_cancel(svc);
cond_clear(mkcond(svc, condstr, sizeof(condstr))); cond_clear(mkcond(svc, condstr, sizeof(condstr)));
@@ -2686,7 +2688,7 @@ void service_notify_reconf(void)
svc_t *svc, *iter = NULL; svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc->notify) if (svc->notify == SVC_NOTIFY_PID)
continue; /* managed by pidfile plugin */ continue; /* managed by pidfile plugin */
if (svc->state != SVC_RUNNING_STATE) if (svc->state != SVC_RUNNING_STATE)
@@ -2739,7 +2741,7 @@ void service_notify_cb(uev_t *w, void *arg, int events)
service_ready(svc); service_ready(svc);
/* s6 applications close their socket after notification */ /* s6 applications close their socket after notification */
if (svc->notify == 2) { if (svc->notify == SVC_NOTIFY_S6) {
uev_io_stop(w); uev_io_stop(w);
close(w->fd); close(w->fd);
w->fd = 0; w->fd = 0;
+9 -1
View File
@@ -83,6 +83,14 @@ typedef enum {
SVC_ONCRASH_SCRIPT, SVC_ONCRASH_SCRIPT,
} svc_oncrash_action_t; } svc_oncrash_action_t;
/* 0: none, 1: systemd, 2: s6 */
typedef enum {
SVC_NOTIFY_NONE = 0,
SVC_NOTIFY_PID,
SVC_NOTIFY_SYSTEMD,
SVC_NOTIFY_S6,
} svc_notify_t;
#define MAX_ID_LEN 16 #define MAX_ID_LEN 16
#define MAX_ARG_LEN 64 #define MAX_ARG_LEN 64
#define MAX_CMD_LEN 256 #define MAX_CMD_LEN 256
@@ -202,7 +210,7 @@ typedef struct svc {
/* /*
* Readiness notification socket: systemd, s6 * Readiness notification socket: systemd, s6
*/ */
int notify; /* 0: none, 1: systemd, 2: s6 */ svc_notify_t notify;
uev_t notify_watcher; /* i/o watcher */ uev_t notify_watcher; /* i/o watcher */
/* time at svc_del(), used by gc timer */ /* time at svc_del(), used by gc timer */
+4 -4
View File
@@ -18,8 +18,8 @@
# change the contents any way you like, it can even be empty. # change the contents any way you like, it can even be empty.
# Check for systemd-udevd and eudev, if we find both, we opt for the latter. # Check for systemd-udevd and eudev, if we find both, we opt for the latter.
service nowarn [S12345789] cgroup.system name:udevd pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev) service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev)
service nowarn [S12345789] cgroup.system name:udevd pid:udevd log udevd -D -- Device event daemon (udev) service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -D -- Device event daemon (udev)
# Wait for udevd to start, then trigger coldplug events and module loading. # Wait for udevd to start, then trigger coldplug events and module loading.
# The last 'settle' call waits for it to finalize processing all uevents. # The last 'settle' call waits for it to finalize processing all uevents.
@@ -34,7 +34,7 @@ run nowarn if:udevd cgroup.init :5 [S] <pid/udevd> log udevadm settle -t 30
# <run/coldplug/success> (same as for plain mdev below) can be relied on as a # <run/coldplug/success> (same as for plain mdev below) can be relied on as a
# barrier for services that, e.g., need to probe for switchcore ports or PHYs # barrier for services that, e.g., need to probe for switchcore ports or PHYs
# that require firmware loading before they show up. # that require firmware loading before they show up.
service nowarn conflict:udevd,mdev cgroup.init if:!udevd \ service nowarn conflict:udevd,mdev cgroup.init notify:none if:!udevd \
[S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd) [S12345789] notify:s6 mdevd -O 4 -D %n -- Device event daemon (mdevd)
run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/ready> \ run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/ready> \
[S] mdevd-coldplug -- Cold plugging system [S] mdevd-coldplug -- Cold plugging system
@@ -42,7 +42,7 @@ run nowarn conflict:udevd,mdev cgroup.init name:coldplug <service/mdevd/read
# If neither udevd or mdevd are available, fall back to mdev. The new daemon # If neither udevd or mdevd are available, fall back to mdev. The new daemon
# mode calls `mdev -s` internally but does not create a PID file when that is # mode calls `mdev -s` internally but does not create a PID file when that is
# done, so synchronization with coldplug is fragile. # done, so synchronization with coldplug is fragile.
service nowarn conflict:udevd,mdevd cgroup.init if:!udevd,!mdevd \ service nowarn conflict:udevd,mdevd cgroup.init notify:none if:!udevd,!mdevd \
[S12345789] mdev -df -S -- Device event daemon (mdev) [S12345789] mdev -df -S -- Device event daemon (mdev)
# Trigger a replay of all uevents from boot, before userspace was up and the # Trigger a replay of all uevents from boot, before userspace was up and the
+1 -1
View File
@@ -1,4 +1,4 @@
# Test service, only used for `make check`, not for public use # Test service, only used for `make check`, not for public use
service [S123456789] name:testserv pid:!/run/testserv.pid \ service [S123456789] name:testserv pid:!/run/testserv.pid \
<boot/testserv> cgroup.system \ <boot/testserv> cgroup.system notify:none \
/sbin/serv -n -p -i testserv -- Test serv daemon /sbin/serv -n -p -i testserv -- Test serv daemon