test: Add a test for Type=dbus services surviving a broker restart
This commit is contained in:
@@ -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>
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
Reference in New Issue
Block a user