TEST-80-NOTIFYACCESS: Add notify-reload signal handler validation test
Add a new integration test to verify the signal handler validation for Type=notify-reload services, as introduced in the previous commit. The test adds a shell harness and four service files to cover all scenarios: 1. `notify-reload-no-handler.service`: Verifies that the service fails to start with Result=protocol when the handler is missing at the first READY=1. 2. `notify-reload-sigstop.service`: Verifies that SIGSTOP is exempt from handler validation because it cannot be caught or blocked. 3. `notify-reload-toggle-handler.service`: Verifies that removing the handler at runtime results in a warning on reload, but the signal is still sent, causing the service to terminate from unhandled SIGHUP. 4. `notify-reload-well-behaved.service`: Verifies the happy path where a service with a handler starts and reloads successfully. Use bounded FIFO operations and journal cursors so failures are prompt and log assertions cannot race a relative time window.
This commit is contained in:
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
MODE="${1:-well-behaved}"
|
||||
FIFO_IN="${2:-/tmp/reload-test-fifo-in}"
|
||||
FIFO_OUT="${3:-/tmp/reload-test-fifo-out}"
|
||||
|
||||
handle_signal() {
|
||||
echo "reload" >"$FIFO_OUT"
|
||||
systemd-notify --reloading
|
||||
systemd-notify --ready --status="Reloaded"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
no-handler)
|
||||
;;
|
||||
toggle-handler|well-behaved)
|
||||
trap 'handle_signal' HUP
|
||||
;;
|
||||
*)
|
||||
echo "Unknown mode: $MODE" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
systemd-notify --ready --status="Started with mode=$MODE"
|
||||
|
||||
while read -r cmd < "$FIFO_IN"; do
|
||||
case "$cmd" in
|
||||
remove-handler)
|
||||
trap - HUP
|
||||
echo "handler-removed" >"$FIFO_OUT"
|
||||
;;
|
||||
exit)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $cmd" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Test notify-reload without signal handler
|
||||
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh no-handler /tmp/reload-nohandler-in /tmp/reload-nohandler-out
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=Test notify-reload with SIGSTOP
|
||||
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
ReloadSignal=SIGSTOP
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh no-handler /tmp/reload-sigstop-in /tmp/reload-sigstop-out
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Test notify-reload with handler that can be removed
|
||||
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh toggle-handler /tmp/reload-toggle-in /tmp/reload-toggle-out
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Test notify-reload with proper signal handler
|
||||
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh well-behaved /tmp/reload-wellbehaved-in /tmp/reload-wellbehaved-out
|
||||
@@ -172,4 +172,85 @@ assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
|
||||
assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
|
||||
assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
|
||||
|
||||
# Test notify-reload signal handler validation
|
||||
|
||||
cleanup_notify_reload() {
|
||||
systemctl stop notify-reload-no-handler.service \
|
||||
notify-reload-sigstop.service \
|
||||
notify-reload-toggle-handler.service \
|
||||
notify-reload-well-behaved.service || :
|
||||
systemctl reset-failed notify-reload-no-handler.service \
|
||||
notify-reload-sigstop.service \
|
||||
notify-reload-toggle-handler.service \
|
||||
notify-reload-well-behaved.service || :
|
||||
rm -f /tmp/reload-nohandler-in /tmp/reload-nohandler-out \
|
||||
/tmp/reload-nohandler-cursor \
|
||||
/tmp/reload-sigstop-in /tmp/reload-sigstop-out \
|
||||
/tmp/reload-toggle-in /tmp/reload-toggle-out \
|
||||
/tmp/reload-toggle-cursor \
|
||||
/tmp/reload-wellbehaved-in /tmp/reload-wellbehaved-out
|
||||
}
|
||||
|
||||
trap cleanup_notify_reload EXIT
|
||||
cleanup_notify_reload
|
||||
|
||||
# Test 1: Service without signal handler should fail to start
|
||||
mkfifo /tmp/reload-nohandler-in /tmp/reload-nohandler-out
|
||||
journalctl -q -n 0 --cursor-file=/tmp/reload-nohandler-cursor
|
||||
(! systemctl start notify-reload-no-handler.service)
|
||||
assert_eq "$(systemctl show notify-reload-no-handler.service -P SubState)" "failed"
|
||||
assert_eq "$(systemctl show notify-reload-no-handler.service -P Result)" "protocol"
|
||||
# Verify error was logged about missing signal handler
|
||||
journalctl --sync
|
||||
journalctl -u notify-reload-no-handler.service --cursor-file=/tmp/reload-nohandler-cursor \
|
||||
--grep "lacks handler for reload signal" >/dev/null
|
||||
systemctl reset-failed notify-reload-no-handler.service
|
||||
rm -f /tmp/reload-nohandler-in /tmp/reload-nohandler-out
|
||||
|
||||
# Test 2: SIGSTOP cannot be caught or blocked, so it is exempt from handler validation
|
||||
mkfifo /tmp/reload-sigstop-in /tmp/reload-sigstop-out
|
||||
systemctl start notify-reload-sigstop.service
|
||||
assert_eq "$(systemctl show notify-reload-sigstop.service -P SubState)" "running"
|
||||
systemctl stop notify-reload-sigstop.service
|
||||
rm -f /tmp/reload-sigstop-in /tmp/reload-sigstop-out
|
||||
|
||||
# Test 3: Service that removes handler should warn on reload but still reload
|
||||
mkfifo /tmp/reload-toggle-in /tmp/reload-toggle-out
|
||||
systemctl start notify-reload-toggle-handler.service
|
||||
assert_eq "$(systemctl show notify-reload-toggle-handler.service -P SubState)" "running"
|
||||
|
||||
# Command service to remove its signal handler
|
||||
timeout 10 bash -c 'echo "remove-handler" >/tmp/reload-toggle-in'
|
||||
response="$(timeout 10 cat /tmp/reload-toggle-out)"
|
||||
assert_eq "$response" "handler-removed"
|
||||
|
||||
# Reload should succeed (with warning) even though handler is gone - service will be killed by SIGHUP
|
||||
# but that's intentional after the first start succeeds
|
||||
journalctl -q -n 0 --cursor-file=/tmp/reload-toggle-cursor
|
||||
systemctl reload --no-block notify-reload-toggle-handler.service
|
||||
# The service will die from SIGHUP since the handler is removed
|
||||
timeout 10 bash -c 'while systemctl is-active --quiet notify-reload-toggle-handler.service; do sleep .5; done'
|
||||
# Verify warning was logged
|
||||
journalctl --sync
|
||||
journalctl -u notify-reload-toggle-handler.service --cursor-file=/tmp/reload-toggle-cursor \
|
||||
--grep "lacks handler for reload signal" >/dev/null
|
||||
rm -f /tmp/reload-toggle-in /tmp/reload-toggle-out
|
||||
|
||||
# Test 4: Well-behaved service with handler should work correctly
|
||||
mkfifo /tmp/reload-wellbehaved-in /tmp/reload-wellbehaved-out
|
||||
systemctl start notify-reload-well-behaved.service
|
||||
assert_eq "$(systemctl show notify-reload-well-behaved.service -P SubState)" "running"
|
||||
|
||||
# Reload should succeed
|
||||
systemctl reload --no-block notify-reload-well-behaved.service
|
||||
timeout 10 bash -c 'until [[ $(systemctl show notify-reload-well-behaved.service -P SubState) == "reload-signal" ]]; do sleep .5; done'
|
||||
response="$(timeout 10 cat /tmp/reload-wellbehaved-out)"
|
||||
assert_eq "$response" "reload"
|
||||
timeout 10 bash -c 'until [[ $(systemctl show notify-reload-well-behaved.service -P SubState) == "running" ]]; do sleep .5; done'
|
||||
assert_eq "$(systemctl show notify-reload-well-behaved.service -P ReloadResult)" "success"
|
||||
|
||||
timeout 10 bash -c 'echo "exit" >/tmp/reload-wellbehaved-in'
|
||||
systemctl stop notify-reload-well-behaved.service
|
||||
rm -f /tmp/reload-wellbehaved-in /tmp/reload-wellbehaved-out
|
||||
|
||||
touch /testok
|
||||
|
||||
Reference in New Issue
Block a user