From 0b5c555c7fca01fdf109514fba461bc2070af6a9 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 12 Nov 2023 18:07:42 +0100 Subject: [PATCH] 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 --- doc/config.md | 24 +++++++++++---- plugins/dbus.c | 4 +-- plugins/pidfile.c | 8 ++--- src/conf.c | 18 ++++++++++-- src/finit.c | 1 + src/finit.h | 1 + src/service.c | 62 ++++++++++++++++++++------------------- src/svc.h | 10 ++++++- system/10-hotplug.conf.in | 8 ++--- system/90-testserv.conf | 2 +- 10 files changed, 88 insertions(+), 50 deletions(-) diff --git a/doc/config.md b/doc/config.md index ce13a974..808e7d39 100644 --- a/doc/config.md +++ b/doc/config.md @@ -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 extent. -As of Finit v4.4 partial support for systemd and s6 is available. The -native PID file mode of operation is still default, the other's can be -defined per service, like this: +As of Finit v4.4 partial support for systemd and s6 style readiness +notification is available, and the native PID file mode of operation is, +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: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: - service notify:systemd A - service B + service notify:pid watchdogd + service stress-ng --cpu 8 For details on the syntax and options, see below. diff --git a/plugins/dbus.c b/plugins/dbus.c index 1161ddbc..08911041 100644 --- a/plugins/dbus.c +++ b/plugins/dbus.c @@ -150,11 +150,11 @@ static void setup(void *arg) * Note: dbus drops privs after starting up. */ 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); free(pidfn); } 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); conf_save_service(SVC_TYPE_SERVICE, line, "dbus.conf"); diff --git a/plugins/pidfile.c b/plugins/pidfile.c index 11a80590..dc511047 100644 --- a/plugins/pidfile.c +++ b/plugins/pidfile.c @@ -88,7 +88,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask) * systemd style services rely on their respective * readiness notification. Issue #343 */ - if (!svc->notify) + if (svc->notify == SVC_NOTIFY_PID) svc_started(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); - if (!svc->notify) + if (svc->notify == SVC_NOTIFY_PID) service_ready(svc); } else if (mask & IN_DELETE) { cond_clear(cond); - if (!svc->notify) + if (svc->notify == SVC_NOTIFY_PID) service_ready(svc); } } @@ -240,7 +240,7 @@ static void pidfile_reconf(void *arg) if (svc_is_changed(svc) || svc_is_starting(svc)) continue; - if (!svc->notify) + if (svc->notify == SVC_NOTIFY_PID) service_ready(svc); mkcond(svc, cond, sizeof(cond)); diff --git a/src/conf.c b/src/conf.c index e57a5503..3cefbd0f 100644 --- a/src/conf.c +++ b/src/conf.c @@ -958,6 +958,18 @@ static int parse_static(char *line, int is_rcsd) 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)) { syncsec = strtonum(strip_line(x), 0, 60, NULL); return 0; @@ -1579,7 +1591,7 @@ int conf_init(uev_ctx_t *ctx) */ #ifdef 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"); } #endif @@ -1587,7 +1599,7 @@ int conf_init(uev_ctx_t *ctx) * Start kernel event daemon as soon as possible, if enabled */ 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"); 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) strlcat(args, "-s ", sizeof(args)); - snprintf(conf, sizeof(conf), "[S] log:console %s %s %s" + snprintf(conf, sizeof(conf), "[S] notify:none log:console %s %s %s" " -- Calling runparts %s in the background", _PATH_RUNPARTS, args, runparts, runparts); conf_save_service(SVC_TYPE_TASK, conf, "runparts.conf"); diff --git a/src/finit.c b/src/finit.c index d3d0bd25..5f8c2598 100644 --- a/src/finit.c +++ b/src/finit.c @@ -69,6 +69,7 @@ int single = 0; /* single user mode from kernel cmdline */ int bootstrap = 1; /* set while bootrapping (for TTYs) */ int kerndebug = 0; /* set if /proc/sys/kernel/printk > 7 */ int syncsec = 0; /* reboot delay */ +int readiness = SVC_NOTIFY_PID; char *finit_conf= NULL; char *finit_rcsd= NULL; char *fstab = NULL; diff --git a/src/finit.h b/src/finit.h index 02f29be1..99423ef7 100644 --- a/src/finit.h +++ b/src/finit.h @@ -121,6 +121,7 @@ extern int single; extern int bootstrap; extern int kerndebug; extern int syncsec; +extern int readiness; extern char *fstab; extern char *sdown; extern char *network; diff --git a/src/service.c b/src/service.c index d3513b78..30f7ffe0 100644 --- a/src/service.c +++ b/src/service.c @@ -701,11 +701,12 @@ static int service_start(svc_t *svc) wordexp_t we = { 0 }; int rc; - if (svc->notify) { + if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) { if (client_command(INIT_CMD_NOTIFY_SOCKET)) { err(1, "%s: failed creating notify socket", svc_ident(svc, NULL, 0)); 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 { char val[20]; @@ -730,7 +731,7 @@ static int service_start(svc_t *svc) if (len == 0) break; - if (svc->notify) { + if (svc->notify == SVC_NOTIFY_SYSTEMD || svc->notify == SVC_NOTIFY_S6) { char *ptr = strstr(arg, "%n"); if (ptr) { @@ -907,6 +908,19 @@ static void service_kill(svc_t *svc) 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 */ @@ -922,12 +936,7 @@ static void service_cleanup(svc_t *svc) logit(LOG_CRIT, "Failed removing service %s pidfile %s", svc_ident(svc, NULL, 0), fn); - /* Ensure we don't have any notify socket lingering */ - if (svc->notify && svc->notify_watcher.fd > 0) { - uev_io_stop(&svc->notify_watcher); - close(svc->notify_watcher.fd); - svc->notify_watcher.fd = 0; - } + service_notify_stop(svc); /* No longer running, update books. */ 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")) - return 1; + return SVC_NOTIFY_SYSTEMD; if (!strcmp(arg, "s6")) - return 2; - return 0; /* unsupported/disabled */ + return SVC_NOTIFY_S6; + if (!strcmp(arg, "pid")) + return SVC_NOTIFY_PID; + return SVC_NOTIFY_NONE; /* unsupported/none */ } 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 svc->log.enabled = 0; } - if (notify) { - int type = parse_notify(notify); - if (type <= 0) - goto disable_notify; - svc->notify = type; - } else if (svc->notify) { - disable_notify: - /* XXX: close existing socket */ - svc->notify = 0; - } + if (notify) + svc->notify = parse_notify(notify); + else + svc->notify = readiness; + if (desc) strlcpy(svc->desc, desc, sizeof(svc->desc)); else if (type == SVC_TYPE_TTY) @@ -2373,11 +2379,7 @@ restart: char condstr[MAX_COND_LEN]; dbg("%s: stopped, cleaning up timers and conditions ...", svc_ident(svc, NULL, 0)); - if (svc->notify && svc->notify_watcher.fd > 0) { - uev_io_stop(&svc->notify_watcher); - close(svc->notify_watcher.fd); - svc->notify_watcher.fd = 0; - } + service_notify_stop(svc); service_timeout_cancel(svc); cond_clear(mkcond(svc, condstr, sizeof(condstr))); @@ -2686,7 +2688,7 @@ void service_notify_reconf(void) svc_t *svc, *iter = NULL; 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 */ if (svc->state != SVC_RUNNING_STATE) @@ -2739,7 +2741,7 @@ void service_notify_cb(uev_t *w, void *arg, int events) service_ready(svc); /* s6 applications close their socket after notification */ - if (svc->notify == 2) { + if (svc->notify == SVC_NOTIFY_S6) { uev_io_stop(w); close(w->fd); w->fd = 0; diff --git a/src/svc.h b/src/svc.h index 5aaaadaa..f94a30e5 100644 --- a/src/svc.h +++ b/src/svc.h @@ -83,6 +83,14 @@ typedef enum { SVC_ONCRASH_SCRIPT, } 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_ARG_LEN 64 #define MAX_CMD_LEN 256 @@ -202,7 +210,7 @@ typedef struct svc { /* * Readiness notification socket: systemd, s6 */ - int notify; /* 0: none, 1: systemd, 2: s6 */ + svc_notify_t notify; uev_t notify_watcher; /* i/o watcher */ /* time at svc_del(), used by gc timer */ diff --git a/system/10-hotplug.conf.in b/system/10-hotplug.conf.in index fc4981c5..603a490f 100644 --- a/system/10-hotplug.conf.in +++ b/system/10-hotplug.conf.in @@ -18,8 +18,8 @@ # 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. -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 pid:udevd log udevd -D -- 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 notify:none pid:udevd log udevd -D -- Device event daemon (udev) # Wait for udevd to start, then trigger coldplug events and module loading. # The last 'settle' call waits for it to finalize processing all uevents. @@ -34,7 +34,7 @@ run nowarn if:udevd cgroup.init :5 [S] log udevadm settle -t 30 # (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 # 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) run nowarn conflict:udevd,mdev cgroup.init name:coldplug \ [S] mdevd-coldplug -- Cold plugging system @@ -42,7 +42,7 @@ run nowarn conflict:udevd,mdev cgroup.init name:coldplug cgroup.system \ + cgroup.system notify:none \ /sbin/serv -n -p -i testserv -- Test serv daemon