Files
finit/test/conf-dirs.sh
T
Joachim Wiberg f0d7257374 Fix #492: add per-service directories, systemd RuntimeDirectory style
A service that drops privileges cannot create its own PID file in
/run, root owns it.  Finit can create the file with pidfile-create,
but the daemon still cannot touch it to confirm a SIGHUP.

Five new settings, block format only: runtime-dir, state-dir,
cache-dir, logs-dir, and config-dir.  The value is a directory name,
resolved under /run, /var/lib, /var/cache, /var/log, and /etc,
respectively.  The directory is created before the service starts,
mode 0755 owned by user/group, and the full path is exported to the
process as RUNTIME_DIRECTORY, STATE_DIRECTORY, CACHE_DIRECTORY,
LOGS_DIRECTORY, and CONFIGURATION_DIRECTORY.  Mode and ownership are
asserted at creation only, a daemon may tighten them afterwards.

The runtime directory is removed when the unit stops, after any
exec-stop-post script, like systemd with RuntimeDirectoryPreserve=no.
A completed run/task counts as stopped unless remain-after-exit keeps
it up.  The other four persist across restarts.

These are the first settings with no legacy token: they are validated
by service_set_dir() and stored on the svc that service_register()
now returns.  systemd accepts a list of directories per setting; this
is a single name for now, widening later is compatible since
libconfuse accepts a bare value for a list option.

The test sysroot gains libnss_files.so.2, which ldd cannot see, glibc
dlopen()s it.  Without it getpwnam() fails inside the chroot, so
user/group settings never resolved and directory ownership could not
be tested.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:23:37 +02:00

81 lines
2.3 KiB
Bash
Executable File

#!/bin/sh
# Verify the per-service directory settings: runtime-dir, state-dir,
# cache-dir, logs-dir, and config-dir. The directory is created before
# the service starts, owned by the service user, and exported to the
# environment. The runtime directory is removed again when the service
# stops, the others persist.
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck disable=SC2034
BOOTSTRAP="service owned {
runlevel = \"S12345\"
user = \"daemon\"
group = \"daemon\"
runtime-dir = \"owned\"
state-dir = \"owned\"
pidfile = \"/run/owned/serv.pid\"
command = \"/sbin/serv -np -P /run/owned/serv.pid -i owned -e STATE_DIRECTORY:/var/lib/owned\"
}
task probe {
runlevel = \"S12345\"
runtime-dir = \"probe\"
cache-dir = \"probe\"
command = \"/sbin/serv -h -e RUNTIME_DIRECTORY:/run/probe -e CACHE_DIRECTORY:/var/cache/probe\"
}
service escape {
runlevel = \"S12345\"
runtime-dir = \"../escape\"
command = \"serv -np -i escape\"
}"
# ls output is empty for an empty directory, so test -d instead
assert_dir()
{
assert "Directory $1 exists" "$(texec test -d "$1" && echo yes)" = "yes"
}
assert_nodir()
{
assert "Directory $1 removed" "$(texec test -d "$1" || echo gone)" = "gone"
}
assert_owner()
{
assert "$1 owned by $2" "$(texec stat -c %U:%G "$1")" = "$2"
}
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
say 'Directory created before start, owned by the service user'
retry 'assert_status owned running'
assert_dir /run/owned
assert_owner /run/owned daemon:daemon
assert_dir /var/lib/owned
assert_owner /var/lib/owned daemon:daemon
say 'The paths are exported to the process environment; both commands'
say 'verify their own with serv -e, and refuse to run on a mismatch'
say 'A completed task no longer holds its runtime directory'
retry 'assert_status probe done'
assert_nodir /run/probe
assert_dir /var/cache/probe
say 'Stopping the service removes the runtime directory, state persists'
run "initctl stop owned"
retry 'assert_status owned stopped'
assert_nodir /run/owned
assert_dir /var/lib/owned
say 'Starting again recreates it'
run "initctl start owned"
retry 'assert_status owned running'
assert_dir /run/owned
say 'A path escaping the base directory is refused, service still runs'
retry 'assert_status escape running'
assert_nodir /escape