package/finit: backport stale-pidfile and death-log fixes

Backport two fixes addressing a critical failure reported by a customer:
an unclean dbus-daemon exit leaves a lingering /run/messagebus.pid, the
daemon then refuse to start, and Finit's restart loop gives up.

0001 service: clean stale pidfile after unclean daemon exit
     Drop a daemon-owned (pid:!) pidfile when it still names the
     just-reaped PID and that PID is no longer alive.

0002 service: log signal name and core dumps in death message
     "by signal: 9" -> "killed by SIGKILL", with ", core dumped"
     when applicable.  Stronger breadcrumb for sudden deaths.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-05-12 13:38:49 +02:00
parent 3be640409a
commit e4203b0c9e
2 changed files with 136 additions and 0 deletions
@@ -0,0 +1,86 @@
From 4a53f610cd05c2aba3da770384460f7e66488ff5 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 11 May 2026 13:55:11 +0200
Subject: [PATCH 1/2] service: clean stale pidfile after unclean daemon exit
Organization: Wires
With `pid:!/path` Finit does not manage the file -- the daemon
creates it on start and removes it on graceful exit. If the daemon
dies before cleanup (SIGKILL, OOM, segfault, exit during startup)
the file lingers and can block the next instance from starting,
e.g. dbus-daemon refuses with EEXIST and the restart loop fails.
Remove the file when it still names the just-reaped PID and that
PID is no longer alive (the liveness check guards against reuse).
Called from service_cleanup(), and from service_monitor()'s
forking+starting branch where cleanup was previously skipped.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
src/service.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/src/service.c b/src/service.c
index 7ed4fceb..e930c4fd 100644
--- a/src/service.c
+++ b/src/service.c
@@ -1120,6 +1120,35 @@ static void service_notify_stop(svc_t *svc)
}
}
+/*
+ * Drop a daemon-owned (pid:!) pidfile if it still names the just-reaped
+ * PID and that PID is gone. The liveness check guards against reuse.
+ */
+static void service_clean_pidfile(svc_t *svc, pid_t reaped)
+{
+ pid_t pid;
+ char *fn;
+
+ if (reaped <= 1)
+ return;
+
+ fn = pid_file(svc);
+ if (!fn)
+ return;
+
+ pid = pid_file_read(fn);
+ if (pid != reaped || pid_alive(pid))
+ return;
+
+ if (remove(fn) && errno != ENOENT) {
+ logit(LOG_CRIT, "Failed removing stale service %s pidfile %s",
+ svc_ident(svc, NULL, 0), fn);
+ return;
+ }
+
+ dbg("Removed stale service %s pidfile %s", svc_ident(svc, NULL, 0), fn);
+}
+
/*
* Clean up any lingering state from dead/killed services
*/
@@ -1137,6 +1166,8 @@ static void service_cleanup(svc_t *svc)
if (remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
svc_ident(svc, NULL, 0), fn);
+ } else if (svc->pidfile[0] == '!') {
+ service_clean_pidfile(svc, svc->pid);
}
/*
@@ -2405,7 +2436,10 @@ void service_monitor(pid_t lost, int status)
if (svc_is_forking(svc)) {
/* Likely start script exiting */
if (svc_is_starting(svc)) {
- svc->pid = 0; /* Expect no more activity from this one */
+ /* Daemon died before clearing 'starting'; drop any stale pidfile. */
+ service_clean_pidfile(svc, lost);
+ svc->oldpid = lost; /* So service_retry() logs the real PID */
+ svc->pid = 0; /* Expect no more activity from this one */
goto cont;
}
--
2.43.0
@@ -0,0 +1,50 @@
From 30f2ca3b2e64bce7db1e2d9dcb37a06d53e0b6bf Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 11 May 2026 17:08:25 +0200
Subject: [PATCH 2/2] service: log signal name and core dumps in death message
Organization: Wires
Replace the bare signal number ("by signal: 9") with the symbolic
name ("killed by SIGKILL") and annotate when the kernel wrote a
core:("killed by SIGSEGV, core dumped"). Makes the restart line
self-explanatory and gives operators a strong breadcrumb when a
daemon dies unexpectedly.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
src/service.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/src/service.c b/src/service.c
index e930c4fd..127e0099 100644
--- a/src/service.c
+++ b/src/service.c
@@ -2828,13 +2828,18 @@ static void service_retry(svc_t *svc)
timeout = ((*restart_cnt) <= (svc->restart_max / 2)) ? 2000 : 5000;
/* If a longer timeout was specified in the conf, use that instead. */
svc->restart_tmo = max(svc->restart_tmo, timeout);
- logit(LOG_CONSOLE|LOG_WARNING, "Service %s[%d] died (%s%d), restarting (retry in %d msec) (attempt: %d/%d)",
- svc_ident(svc, NULL, 0), svc->oldpid,
- WIFEXITED(svc->status) ? "with exit status: " : "by signal: ",
- WIFEXITED(svc->status) ? WEXITSTATUS(svc->status) : WTERMSIG(svc->status),
- svc->restart_tmo,
- *restart_cnt,
- svc->restart_max);
+ if (WIFEXITED(svc->status))
+ logit(LOG_CONSOLE|LOG_WARNING,
+ "Service %s[%d] died (exit status: %d), restarting (retry in %d msec) (attempt: %d/%d)",
+ svc_ident(svc, NULL, 0), svc->oldpid, WEXITSTATUS(svc->status),
+ svc->restart_tmo, *restart_cnt, svc->restart_max);
+ else
+ logit(LOG_CONSOLE|LOG_WARNING,
+ "Service %s[%d] died (killed by %s%s), restarting (retry in %d msec) (attempt: %d/%d)",
+ svc_ident(svc, NULL, 0), svc->oldpid,
+ sig_name(WTERMSIG(svc->status)),
+ WCOREDUMP(svc->status) ? ", core dumped" : "",
+ svc->restart_tmo, *restart_cnt, svc->restart_max);
svc_unblock(svc);
service_step(svc);
--
2.43.0