Package: runit-run
Version: 2.3.1-4
Severity: wishlist
Tags: patch

Dear Maintainer,

I'm using runit under systemd via runit-run. Some of the
runit-supervised services depend on systemd-supervised ones, and it's
easy to add a file in /etc/systemd/system/runit.service.d/ specifying
e.g.
| [Unit]
| After=postgresql.service valkey-server.service
| Wants=postgresql.service valkey-server.service

This works properly for startup. However, there's a race condition on
shutdown:

The runit.service is defined with
| KillSignal=SIGHUP
| KillMode=process
which causes systemd to send SIGHUP to the runsvdir. Quoting
runsvdir(8):
| If runsvdir receives a HUP signal, it sends a TERM signal to each
| runsv(8) process it is monitoring and then exits with 111.
...and that is exactly what it does. In particular, runsvdir does NOT
wait for the runsv processes to exit first, hence the runit-supervised
services may not have terminated (or even gotten SIGTERM) yet.

But systemd (in KillMode=process) assumes that runsvdir exiting means
the service is down, and proceeds to stop the dependencies. Depending on
timing, this breaks my still-running (or still-shutting-down) services.

I wish for this race to be fixed. :)

I can see 2.5 ways to do that:
1) It should be possible to teach runsvdir to wait for its runsv
   children to exit before exiting itself. And since runsv in turn does
   wait for its children to exit, that would ensure the whole
   supervision tree has exited when runsvdir does, as systemd expects.
2) It is possible to use systemd's ExecStop= configuration to delay
   systemd from considering the service stopped until the process
   specified there has exited. Hence, specify a process that waits for
   the supervision tree to die.
2a) You could do so by interacting with
    /etc/runit/runsvdir/svmanaged/*/supervise/ to find the processes in
    question, or
2b) since systemd puts each service in its own cgroup, you can wait for
    that cgroup to become empty.

While I think 1) would be cleaner, I'm unsure about the compatibility
implications (Change SIGHUP behaviour? Use a new signal? Would Debian
want to patch runsvdir like that?) and the reason for the current
runsvdir behaviour (When/why is not waiting ever useful?).
If you think some version of this would be a good feature, please say
so, and I'll try to come up with a patch.

So meanwhile, I implemented 2b), the resulting ExecStop= script is
attached. It has been working well for me. I hope it's useful, or even
acceptable for inclusion in Debian.

Please tell me if I can do anything else to help.
Thank you for maintaining runit!
cheers,
    Jan

-- System Information:
Debian Release: forky/sid
  APT prefers testing
  APT policy: (990, 'testing'), (800, 'stable-updates'), (800, 
'stable-security-debug'), (800, 'stable-security'), (800, 'stable-debug'), 
(800, 'stable'), (650, 'testing-debug'), (550, 'unstable-debug'), (550, 
'unstable'), (10, 'experimental-debug'), (10, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 7.1.8+deb14-amd64 (SMP w/12 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, 
TAINT_UNSIGNED_MODULE
Locale: LANG=C.UTF-8, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages runit-run depends on:
ii  runit  2.3.1-4

runit-run recommends no packages.

runit-run suggests no packages.

-- no debconf information
#!/bin/sh
set -eu

# Tell runsvdir(8) to shutdown:
kill -HUP "${MAINPID:-}" 2>/dev/null || true


# FIXME: what do we want to do on other init systems? are they even calling 
this script?
[ -d /run/systemd/system ] || exit 0

# Systemd starts the ExecStop script (us) in the service's cgroup:
cgroup=`sed -E 's/^0:://;t;d' /proc/$$/cgroup`
[ -n "$cgroup" ] || exit 2
cgroup="/sys/fs/cgroup/$cgroup"
[ -d "$cgroup" ] || exit 3


# Now we leave the cgroup and wait for everything else in it do die.
#
# While it might seem appropriate to use "systemd-run --scope" to get our own
# cgroup, that doesn't actually work: systemd will notice that we're trying to
# start a unit during shutdown, log
#   Transaction for run-….scope/start is destructive (shutdown.target has 
'start' job queued, but 'stop' is included in transaction).
# and refuse to execute our code.
# Hence, just put us into the root cgroup by hand:
echo $$ >/sys/fs/cgroup/cgroup.procs

while grep -qFx "populated 1" "$cgroup"/cgroup.events ; do
    sleep 0.1
done

Attachment: signature.asc
Description: PGP signature

Reply via email to