test: Add a test for Type=dbus services surviving a broker restart

This commit is contained in:
Chris Down
2026-09-01 03:03:46 +09:00
committed by Yu Watanabe
parent fdf0876bc7
commit df3c9c9ef0
4 changed files with 255 additions and 0 deletions
@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1-or-later
-->
<busconfig>
<policy user="root">
<allow own="systemd.test.DBusReconnect"/>
</policy>
</busconfig>
+4
View File
@@ -315,6 +315,10 @@ executables += [
'dependencies' : libopenssl_cflags, 'dependencies' : libopenssl_cflags,
'conditions' : ['HAVE_OPENSSL'], 'conditions' : ['HAVE_OPENSSL'],
}, },
test_template + {
'sources' : files('test-dbus-name-holder.c'),
'type' : 'manual',
},
test_template + { test_template + {
'sources' : files('test-display-quota.c'), 'sources' : files('test-display-quota.c'),
'type' : 'manual', 'type' : 'manual',
+77
View File
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "sd-bus.h"
#include "sd-daemon.h"
#include "log.h"
#include "main-func.h"
#include "time-util.h"
static int run(int argc, char *argv[]) {
int r;
if (argc < 3 || argc > 5)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Usage: %s BUSNAME READY_MARKER [RELEASE_MARKER] [NOTIFY_MARKER]",
program_invocation_short_name);
const char
*name = argv[1],
*marker = argv[2],
*release_marker = argc > 3 ? argv[3] : NULL,
*notify_marker = argc > 4 ? argv[4] : NULL;
for (;;) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
r = sd_bus_open_system(&bus);
if (r < 0)
return log_error_errno(r, "Failed to open system bus: %m");
r = sd_bus_request_name(bus, name, /* flags= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to acquire dbus name '%s': %m", name);
log_info("Acquired dbus name '%s'.", name);
for (;;) {
r = sd_bus_process(bus, /* ret= */ NULL);
if (r < 0)
break;
if (r > 0)
continue;
if (release_marker && access(release_marker, F_OK) >= 0) {
r = sd_bus_release_name(bus, name);
if (r < 0)
return log_error_errno(r, "Failed to release dbus name '%s': %m", name);
log_info("Released dbus name '%s'.", name);
release_marker = NULL; /* go back to blocking until we're killed */
}
r = sd_bus_wait(bus, release_marker ? 100 * USEC_PER_MSEC : USEC_INFINITY);
if (r < 0)
break;
}
log_info("Disconnected from bus, reconnecting...");
while (access(marker, F_OK) < 0) {
if (notify_marker && access(notify_marker, F_OK) >= 0) {
r = sd_notify(/* unset_environment= */ false, "RELOADING=1\nREADY=1");
if (r < 0)
return log_error_errno(r, "Failed to send reload notification: %m");
log_info("Sent reload notification.");
notify_marker = NULL;
}
usleep_safe(50 * USEC_PER_MSEC);
}
}
}
DEFINE_MAIN_FUNCTION(run);
+161
View File
@@ -0,0 +1,161 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
holder=/usr/lib/systemd/tests/unit-tests/manual/test-dbus-name-holder
name=systemd.test.DBusReconnect
marker=/run/dbus-reconnect-go
release=/run/dbus-reconnect-release
notify=/run/dbus-reconnect-notify
reloaded=/run/dbus-reconnect-reloaded
at_exit() {
rm -f "$marker" "$release" "$notify" "$reloaded"
systemctl stop \
dbus-reconnect-recover.service \
dbus-reconnect-stop.service \
dbus-reconnect-exit.service \
dbus-reconnect-drop.service \
dbus-reconnect-notify.service \
dbus-reconnect-notify-target.service \
dbus-reconnect-reexec.service 2>/dev/null || :
}
trap at_exit EXIT
# Poll until property $2 of unit $1 reaches value $3
poll() {
local unit="${1:?}" prop="${2:?}" want="${3:?}" got i
for ((i = 0; i < 150; i++)); do
got="$(systemctl show -p "$prop" --value "$unit")"
if [[ "$got" == "$want" ]]; then
return 0
fi
sleep 0.2
done
echo "poll: timed out waiting for $unit $prop=$want (last: $got)" >&2
return 1
}
reset_failed() {
local unit="${1:?}"
systemctl reset-failed "$unit" 2>/dev/null || :
}
restart_broker() {
systemctl kill --signal=KILL dbus.service
timeout 90 bash -c '
until busctl --system status >/dev/null 2>&1; do
systemctl reset-failed dbus.service dbus.socket 2>/dev/null
systemctl start dbus.socket 2>/dev/null
sleep 0.2
done'
}
enter_revalidation() {
local unit="${1:?}"
shift
reset_failed "$unit"
rm -f "$marker"
systemd-run \
--unit="$unit" \
-p Type=dbus \
-p BusName="$name" \
-p Restart=no \
"$@" \
"$holder" "$name" "$marker"
restart_broker
poll "$unit" SubState running-revalidating
}
# the name comes back within the grace window
unit=dbus-reconnect-recover.service
enter_revalidation "$unit"
main_pid="$(systemctl show -p MainPID --value "$unit")"
assert_neq "$main_pid" "0"
touch "$marker"
poll "$unit" SubState running
assert_eq "$(systemctl show -p MainPID --value "$unit")" "$main_pid"
systemctl stop "$unit"
# the name never comes back
unit=dbus-reconnect-stop.service
enter_revalidation "$unit"
poll "$unit" ActiveState inactive
# ditto with RemainAfterExit=yes
unit=dbus-reconnect-exit.service
enter_revalidation "$unit" -p RemainAfterExit=yes
poll "$unit" SubState exited
systemctl stop "$unit"
# the name is dropped on a live connection and we action it at once
unit=dbus-reconnect-drop.service
reset_failed "$unit"
rm -f "$release"
systemd-run \
--unit="$unit" \
-p Type=dbus \
-p BusName="$name" \
-p Restart=no \
"$holder" "$name" "$marker" "$release"
poll "$unit" SubState running
touch "$release"
for ((i = 0; i < 150; i++)); do
sub_state="$(systemctl show -p SubState --value "$unit")"
if [[ "$sub_state" == running-revalidating ]]; then
echo "live drop entered the revalidation window" >&2
exit 1
fi
active_state="$(systemctl show -p ActiveState --value "$unit")"
if [[ "$active_state" == inactive ]]; then
break
fi
sleep 0.2
done
assert_eq "$(systemctl show -p ActiveState --value "$unit")" inactive
# a reload notification is propagated even while the name is absent (revalidating)
target=dbus-reconnect-notify-target.service
reset_failed "$target"
systemd-run \
--unit="$target" \
-p Type=oneshot \
-p RemainAfterExit=yes \
-p "ExecReload=/usr/bin/touch $reloaded" \
/usr/bin/true
unit=dbus-reconnect-notify.service
reset_failed "$unit"
rm -f "$marker" "$release" "$notify" "$reloaded"
systemd-run \
--unit="$unit" \
-p Type=dbus \
-p BusName="$name" \
-p Restart=no \
-p NotifyAccess=main \
-p PropagatesReloadTo="$target" \
"$holder" "$name" "$marker" "$release" "$notify"
restart_broker
poll "$unit" SubState running-revalidating
touch "$notify"
timeout 30 bash -c "until [[ -e '$reloaded' ]]; do sleep 0.2; done"
touch "$marker"
poll "$unit" SubState running
systemctl stop "$unit" "$target"
# the revalidation state survives a daemon-reexec and still recovers afterwards
unit=dbus-reconnect-reexec.service
enter_revalidation "$unit"
systemctl daemon-reexec
touch "$marker"
poll "$unit" SubState running
systemctl stop "$unit"