Compare commits

..
16 Commits
Author SHA1 Message Date
Joachim Wiberg fca5ce57ea Bump version for v4.6 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 07:03:30 +01:00
Joachim Wiberg 53aa195ad5 test: update, pid/B is no longer assert by Finit if notify != pid
As of Finit v4.6 we no longer assert the PID condition for services
declaring themselves as notify != pid.  We replace D with a forking
service to catch any future regressions in the pidfile plugin.

No need to check reload PID of D, it is enough to check PID of C.

Also, reduce the number of retries at startup.  If we haven't gone
up within 10 sec with this tiny config something is really wrong.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 06:09:35 +01:00
Joachim Wiberg e60211168e Fix type:forking regression in 66020f4, caught by tests
Premise, a service declaring itself 'notify:none' should never assert a
pid condition.  However, forking services still need to be supported and
the only way to do that is if they create a pid file.  Hence, instead of
skipping pidfile_update_conds() completely we need to filter the type.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 06:06:58 +01:00
Joachim Wiberg dc3ee9e467 Handle rare warning when registering configuration changes
2023-11-13T04:02:23 [WRN]: conf_change_act():failed registering /etc/finit.conf/ event mask 00000004: Not a directory

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 05:05:07 +01:00
Joachim Wiberg 6918f3b711 Update ChangeLog for v4.6
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:39:08 +01:00
Joachim Wiberg 66020f48ea Do not create PID conditions for services that do not support it
Follow-up to issue #386

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:37:03 +01:00
Joachim Wiberg 47b0d4cccf Reassert both PID and ready conditions when leaving paused state
Follow-up to issue #386

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:35:31 +01:00
Joachim Wiberg 92c2efa655 Minor, use svc_ident() instead of incomplete svc->name in debug logs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-13 04:32:30 +01:00
Joachim Wiberg b69aa8a8bd Bump version for 4.6 bug fix release, rc1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 19:08:40 +01:00
Joachim Wiberg ca90bbfb04 Fix #387: sanity check environment variables before sourcing
This fixes an issue when finding a global environment variable with
spaces in the variable name:

    set COLORTERM=yes

Literally, 'set COLORTERM' was the name of the variable.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 19:07:54 +01:00
Joachim Wiberg 0b5c555c7f 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>
2023-11-12 19:07:12 +01:00
Joachim Wiberg 6443995fc9 Fix #385: internal conditions are type oneshot, always active
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 09:04:35 +01:00
Joachim Wiberg 180faf8a40 test: minor, disable debug as final step in function
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 09:04:15 +01:00
Joachim Wiberg 0ebdd32f6b Fix #384: reassemble value fragments after wordexp() before setenv()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 08:50:12 +01:00
Joachim Wiberg 60bf858302 test: extend svc-env.sh with more common use-case
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-12 08:37:05 +01:00
Joachim Wiberg 7c86b40190 Fix #383: configure must expand FINIT_RUNPATH_ before define
This fixes an annying buglet that creeped in just before the v4.5 release
causing none of the internal services to be registered properly:

    conf_save_service():Failed creating ${localstatedir}/run/finit/system/dbus.conf: No such file or directory
    plugin_run_hook():Calling modules-load hook n:o 5 (arg: 0x55c4c8133621) ...
    load():Scanning /etc/modules-load.d for config files ...
    cond_set_oneshot():hook/svc/plugin
    cond_set_oneshot_noupdate():hook/svc/plugin => /run/finit/cond/hook/svc/plugin
    cond_update():hook/svc/plugin
    parse_conf():*** Parsing /etc/finit.conf
    conf_save_service():Failed creating ${localstatedir}/run/finit/system/runparts.conf: No such file or directory

The fix, like most, is simple when you find it.  We must expand $runstatedir
before creating the #define in config.h

For convenience, here's the patch for the generated configure script:

&<---------------------------[cut here]---------------------------
--- a/configure	2023-11-05 23:39:51.907334321 +0100
+++ b/configure	2023-11-05 23:39:58.339299795 +0100
@@ -15352,7 +15352,15 @@
 printf "%s\n" "#define FINIT_EXECPATH_ \"$pkglibexecdir\"" >>confdefs.h

-printf "%s\n" "#define FINIT_RUNPATH_ \"$runstatedir/finit/system\"" >>confdefs.h
+	finit_runpath="$runstatedir/finit/system"
+	finit_runpath=`(
+	    test "x$prefix" = xNONE && prefix="$ac_default_prefix"
+	    test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
+	    eval echo \""$finit_runpath"\"
+	    )`
+
+
+printf "%s\n" "#define FINIT_RUNPATH_ \"$finit_runpath\"" >>confdefs.h

&<---------------------------[cut here]---------------------------
Ensure you include the three empty lines for context at the end!

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-11-05 23:46:26 +01:00
18 changed files with 215 additions and 92 deletions
+22 -1
View File
@@ -4,6 +4,26 @@ Change Log
All relevant changes are documented in this file. All relevant changes are documented in this file.
[4.6][] - 2023-11-13
--------------------
### Changes
- Add support for service `notify:pid` and `readiness none` global
option to change how Finit expects readiness notification, issue #386
### Fixes
- Fix #383: dbus and runparts regression in Finit v4.5. The configure
script must expand `FINIT_RUNPATH_` before defining it in `config.h`
- Fix #384: service environment variables drop everything but the first
argument, e.g., `VAR="foo bar qux"` drops everything but `foo`
- Fix #385: internal conditions, e.g., `<int/bootstrap>` turn into flux
when leaving bootstrap, causing depending services to stop
- Fix #387: global environment variables declared with `set VAR=NAME` do
not drop leading `set `, causing `'set VAR'='NAME'` in env.
- Sanity check environment variables, for services and globally. Ensure
the variable name (key) does not contain spaces, or a leading `set `
[4.5][] - 2023-10-30 [4.5][] - 2023-10-30
-------------------- --------------------
@@ -1613,7 +1633,8 @@ Major bug fix release.
* Initial release * Initial release
[UNRELEASED]: https://github.com/troglobit/finit/compare/4.5...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/4.6...HEAD
[4.6]: https://github.com/troglobit/finit/compare/4.5...4.6
[4.5]: https://github.com/troglobit/finit/compare/4.4...4.5 [4.5]: https://github.com/troglobit/finit/compare/4.4...4.5
[4.4]: https://github.com/troglobit/finit/compare/4.3...4.4 [4.4]: https://github.com/troglobit/finit/compare/4.3...4.4
[4.3]: https://github.com/troglobit/finit/compare/4.2...4.3 [4.3]: https://github.com/troglobit/finit/compare/4.2...4.3
+3 -2
View File
@@ -1,4 +1,4 @@
AC_INIT([Finit], [4.5], [https://github.com/troglobit/finit/issues], AC_INIT([Finit], [4.6], [https://github.com/troglobit/finit/issues],
[finit], [https://troglobit.com/projects/finit/]) [finit], [https://troglobit.com/projects/finit/])
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
@@ -314,7 +314,8 @@ pkglibdir=$libdir/finit
AC_SUBST(pkglibexecdir) AC_SUBST(pkglibexecdir)
AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit") AC_EXPAND_DIR(pkglibexecdir, "$libexecdir/finit")
AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path]) AC_DEFINE_UNQUOTED(FINIT_EXECPATH_, "$pkglibexecdir", [Finit libexec path])
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "$runstatedir/finit/system", [Finit runtime dynamic system services]) AC_EXPAND_DIR(finit_runpath, "$runstatedir/finit/system")
AC_DEFINE_UNQUOTED(FINIT_RUNPATH_, "$finit_runpath", [Finit runtime dynamic system services])
AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins") AC_EXPAND_DIR(plugin_path, "$pkglibdir/plugins")
AC_SUBST(plugin_path) AC_SUBST(plugin_path)
+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");
+7 -6
View File
@@ -79,7 +79,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
return; return;
} }
dbg("Found svc %s for %s with pid %d", svc->name, fn, svc->pid); dbg("Found svc %s for %s with pid %d", svc_ident(svc, NULL, 0), fn, svc->pid);
mkcond(svc, cond, sizeof(cond)); mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) { if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
@@ -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)) {
@@ -120,12 +120,13 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
} }
} }
cond_set(cond); if (svc->notify == SVC_NOTIFY_PID)
if (!svc->notify) cond_set(cond);
if (svc->notify == SVC_NOTIFY_PID || svc->notify == SVC_NOTIFY_NONE)
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 +241,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));
+34 -4
View File
@@ -545,6 +545,23 @@ static void parse_env(char *line)
*end-- = 0; *end-- = 0;
} }
/* strip any leading 'set ' */
end = key;
if (!strncmp(key, "set", 3))
end += 3;
/* check key, no spaces allowed */
while (*end && isspace(*end))
end++;
key = end;
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val);
return; /* invalid key */
}
dbg("Global env '%s'='%s'", key, val);
setenv(key, val, 1); setenv(key, val, 1);
node = malloc(sizeof(*node)); node = malloc(sizeof(*node));
@@ -958,6 +975,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;
@@ -1393,7 +1422,8 @@ static int conf_change_act(char *dir, char *name, uint32_t mask)
struct conf_change *node; struct conf_change *node;
char *rp = NULL; char *rp = NULL;
if (name[0]) /* Check for actual printable characters, sometimes STX */
if (name[0] && name[0] >= ' ')
paste(fn, sizeof(fn), dir, name); paste(fn, sizeof(fn), dir, name);
else else
strlcpy(fn, dir, sizeof(fn)); strlcpy(fn, dir, sizeof(fn));
@@ -1579,7 +1609,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 +1617,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 +1651,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");
+2 -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;
@@ -697,7 +698,7 @@ int main(int argc, char *argv[])
/* Some bootstrap tasks may need to know if we're in a container. */ /* Some bootstrap tasks may need to know if we're in a container. */
if (in_container()) if (in_container())
cond_set("int/container"); cond_set_oneshot("int/container");
/* /*
* Initialize .conf system and load static /etc/finit.conf then * Initialize .conf system and load static /etc/finit.conf then
+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;
+77 -43
View File
@@ -394,11 +394,8 @@ static int redirect(svc_t *svc)
*/ */
static void source_env(svc_t *svc) static void source_env(svc_t *svc)
{ {
char buf[LINE_SIZE]; char *buf, *val, *line, *fn;
char *line;
size_t len;
FILE *fp; FILE *fp;
char *fn;
fn = svc_getenv(svc); fn = svc_getenv(svc);
if (!fn) if (!fn)
@@ -409,12 +406,19 @@ static void source_env(svc_t *svc)
if (!fp) if (!fp)
return; return;
buf = alloca(LINE_SIZE);
val = alloca(LINE_SIZE);
if (!buf || !val) {
warn("Failed allocating temporary env buffer");
return;
}
line = buf; line = buf;
len = sizeof(buf); while (fgets(line, LINE_SIZE, fp)) {
while (fgets(line, len, fp)) {
char *key = chomp(line); char *key = chomp(line);
wordexp_t we = { 0 }; wordexp_t we = { 0 };
char *value, *end; char *value, *end;
size_t i;
/* skip any leading whitespace */ /* skip any leading whitespace */
while (isspace(*key)) while (isspace(*key))
@@ -467,9 +471,30 @@ static void source_env(svc_t *svc)
*end-- = 0; *end-- = 0;
} }
/* strip any leading 'set ' */
end = key;
if (!strncmp(key, "set", 3))
end += 3;
/* check key, no spaces allowed */
while (*end && isspace(*end))
end++;
key = end;
while (*end && !isspace(*end))
end++;
if (*end != 0) {
warnx("'%s=%s': not a valid identifier", key, val);
continue; /* invalid key */
}
switch (wordexp(value, &we, 0)) { switch (wordexp(value, &we, 0)) {
case 0: case 0:
setenv(key, we.we_wordv[0], 1); for (i = 0, *val = 0; i < we.we_wordc; i++) {
if (i > 0)
strlcat(val, " ", LINE_SIZE);
strlcat(val, we.we_wordv[i], LINE_SIZE);
}
setenv(key, val, 1);
wordfree(&we); wordfree(&we);
break; break;
@@ -692,11 +717,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];
@@ -721,7 +747,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) {
@@ -898,6 +924,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
*/ */
@@ -913,12 +952,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)
@@ -1179,13 +1213,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)
@@ -1750,17 +1786,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)
@@ -2364,11 +2395,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)));
@@ -2522,6 +2549,8 @@ restart:
svc_mark_clean(svc); svc_mark_clean(svc);
} }
if (svc->notify == SVC_NOTIFY_NONE)
service_ready(svc);
break; break;
} }
break; break;
@@ -2546,16 +2575,21 @@ restart:
svc_set_state(svc, SVC_RUNNING_STATE); svc_set_state(svc, SVC_RUNNING_STATE);
/* Reassert condition if we go from waiting and no change */ /* Reassert condition if we go from waiting and no change */
if (!svc_is_changed(svc)) { if (!svc_is_changed(svc)) {
char name[MAX_COND_LEN]; if (svc->notify == SVC_NOTIFY_PID) {
char name[MAX_COND_LEN];
mkcond(svc, name, sizeof(name)); mkcond(svc, name, sizeof(name));
dbg("Reassert condition %s", name); dbg("Reassert condition %s", name);
cond_set_path(cond_path(name), COND_ON); cond_set_path(cond_path(name), COND_ON);
}
dbg("Reassert %s ready condition", svc_ident(svc, NULL, 0));
service_ready(svc);
} }
break; break;
case COND_OFF: case COND_OFF:
dbg("Condition for %s is off, sending SIGCONT + SIGTERM", svc->name); dbg("Condition for %s is off, sending SIGCONT + SIGTERM", svc_ident(svc, NULL, 0));
kill(svc->pid, SIGCONT); kill(svc->pid, SIGCONT);
service_stop(svc); service_stop(svc);
break; break;
@@ -2677,13 +2711,13 @@ 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)
continue; continue;
if (svc_is_changed(svc) || svc_is_starting(svc)) if (svc->notify != SVC_NOTIFY_NONE && (svc_is_changed(svc) || svc_is_starting(svc)))
continue; continue;
service_ready(svc); service_ready(svc);
@@ -2730,7 +2764,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;
+1 -1
View File
@@ -216,7 +216,7 @@ restart:
service_step_all(SVC_TYPE_ANY); service_step_all(SVC_TYPE_ANY);
/* Allow runparts to start */ /* Allow runparts to start */
cond_set("int/bootstrap"); cond_set_oneshot("int/bootstrap");
if (sm->newlevel == -1) if (sm->newlevel == -1)
break; break;
+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 */
+9 -9
View File
@@ -18,23 +18,23 @@
# 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.
run nowarn if:udevd cgroup.init :1 [S] <pid/udevd> log udevadm settle -t 0 -- run nowarn if:udevd cgroup.init :1 [S] <service/udevd/ready> log udevadm settle -t 0 --
run nowarn if:udevd cgroup.init :2 [S] <pid/udevd> log udevadm control --reload -- run nowarn if:udevd cgroup.init :2 [S] <service/udevd/ready> log udevadm control --reload --
run nowarn if:udevd cgroup.init :3 [S] <pid/udevd> log udevadm trigger -c add -t devices -- Requesting device events run nowarn if:udevd cgroup.init :3 [S] <service/udevd/ready> log udevadm trigger -c add -t devices -- Requesting device events
run nowarn if:udevd cgroup.init :4 [S] <pid/udevd> log udevadm trigger -c add -t subsystems -- Requesting subsystem events run nowarn if:udevd cgroup.init :4 [S] <service/udevd/ready> log udevadm trigger -c add -t subsystems -- Requesting subsystem events
run nowarn if:udevd cgroup.init :5 [S] <pid/udevd> log udevadm settle -t 30 -- Waiting for udev to finish run nowarn if:udevd cgroup.init :5 [S] <service/udevd/ready> log udevadm settle -t 30 -- Waiting for udev to finish
# If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the # If udevd is not available, check for mdevd. Unlike plain 'mdev -df', the
# mdevd-coldplug process exits when mdevd has completed. So the condition # mdevd-coldplug process exits when mdevd has completed. So the condition
# <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
+3
View File
@@ -80,6 +80,9 @@ test_one()
say "Done, drop stanza from $TEST_CONF ..." say "Done, drop stanza from $TEST_CONF ..."
run "rm $TEST_CONF" run "rm $TEST_CONF"
run "initctl reload" run "initctl reload"
# Done, disable debug
run "initctl debug"
} }
+9 -3
View File
@@ -2,6 +2,11 @@
set -eu set -eu
TEST_DIR=$(dirname "$0") TEST_DIR=$(dirname "$0")
# shellcheck disable=SC2034
BOOTSTRAP="foo=bar\n\
set COLORTERM=yes\n\
b aq=oroszoe\n\
baz=qux"
test_setup() test_setup()
{ {
@@ -19,13 +24,14 @@ test_teardown()
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh" . "$TEST_DIR/lib/setup.sh"
say "Waiting for runlevel 2"
sleep 2
#run "initctl debug" #run "initctl debug"
say "Add custom envs to $FINIT_CONF ..."
run "echo 'foo=bar' > $FINIT_CONF"
run "echo 'baz=qux' >> $FINIT_CONF"
say "Add service stanza to $FINIT_CONF ..." say "Add service stanza to $FINIT_CONF ..."
run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF" run "echo 'service serv -np -e foo:bar -- Verify foo=bar' >> $FINIT_CONF"
#run "cat $FINIT_CONF"
say 'Reload Finit' say 'Reload Finit'
run "initctl reload" run "initctl reload"
+2
View File
@@ -5,3 +5,5 @@ foo=bar
baz=qux baz=qux
xyzzy=$foo xyzzy=$foo
FOO_ARGS="-i $foo -l $xyzzy endarg"
+1 -1
View File
@@ -22,7 +22,7 @@ test_teardown()
sep "/etc/env:" sep "/etc/env:"
run "cat /etc/env" run "cat /etc/env"
run "echo 'service env:/etc/env serv -np -e xyzzy:bar -- serv checks xyzzy=bar' > $FINIT_CONF" run "echo 'service env:/etc/env serv -np -e xyzzy:bar -e \"FOO_ARGS:-i bar -l bar endarg\" -- serv checks xyzzy=bar' > $FINIT_CONF"
say 'Reload Finit' say 'Reload Finit'
run "initctl reload" run "initctl reload"
+13 -12
View File
@@ -14,10 +14,10 @@ TEST_DIR=$(dirname "$0")
test_setup() test_setup()
{ {
run "cat >> $FINIT_CONF" <<EOF run "cat >> $FINIT_CONF" <<EOF
service log:stdout name:A serv -np -i A service log:stdout notify:pid name:A serv -np -i A -- A (pid)
service log:stdout notify:systemd <pid/A> name:B serv -np -i B -N 0 -- B needs A service log:stdout notify:systemd <pid/A> name:B serv -np -i B -N 0 -- B needs A (systemd)
service log:stdout notify:systemd <service/B/ready> name:C serv -np -i C -N 0 -- C needs B(service) service log:stdout notify:s6 <service/B/ready> name:C serv -np -i C -N 0 -- C needs B (s6)
service log:stdout notify:systemd <pid/B> name:D serv -np -i D -N 0 -- D needs B(pid) service log:stdout notify:none <pid/A> name:D type:forking serv -i D -- D needs A (forking)
task <service/C/ready,service/D/ready> name:allup initctl cond set allup -- Everything is up task <service/C/ready,service/D/ready> name:allup initctl cond set allup -- Everything is up
EOF EOF
say "Test start $(date)" say "Test start $(date)"
@@ -41,17 +41,18 @@ run "cat $FINIT_CONF"
sep sep
run "initctl reload" run "initctl reload"
run "initctl status" run "initctl status"
run "initctl cond dump"
sep sep
run "initctl debug"
say "waiting for primary startup to complete" say "waiting for primary startup to complete"
retry 'assert_status allup "done"' 100 1 retry 'assert_status allup "done"' 10 1
assert_status C "running" assert_status C "running"
oldpid=$(pidof C) oldpid=$(pidof C)
assert_status D "running"
doldpid=$(pidof D)
sep "pre-reload status" sep "pre-reload status"
run "initctl status" run "initctl status"
run "initctl cond dump"
sep sep
say "Reload Finit, who gets restarted?" say "Reload Finit, who gets restarted?"
@@ -59,15 +60,15 @@ say "Reload Finit, who gets restarted?"
run "initctl reload" run "initctl reload"
sleep 2 sleep 2
sep "post-reload status"
run "initctl status"
run "initctl cond dump"
sep
assert_status A "running" assert_status A "running"
assert_status B "running" assert_status B "running"
assert_status C "running" assert_status C "running"
assert_status D "running"
newpid=$(pidof C) newpid=$(pidof C)
# shellcheck disable=SC2086 # shellcheck disable=SC2086
assert "C was not restarted" $oldpid -eq $newpid assert "C was not restarted" $oldpid -eq $newpid
dnewpid=$(pidof D)
# shellcheck disable=SC2086
assert "D was not restarted" $doldpid -eq $dnewpid