diff --git a/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh new file mode 100755 index 0000000000..c6a291199d --- /dev/null +++ b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-harness.sh @@ -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 diff --git a/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-no-handler.service b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-no-handler.service new file mode 100644 index 0000000000..3e0152c332 --- /dev/null +++ b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-no-handler.service @@ -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 diff --git a/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-sigstop.service b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-sigstop.service new file mode 100644 index 0000000000..83c4314ae0 --- /dev/null +++ b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-sigstop.service @@ -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 diff --git a/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-toggle-handler.service b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-toggle-handler.service new file mode 100644 index 0000000000..3f609c99e1 --- /dev/null +++ b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-toggle-handler.service @@ -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 diff --git a/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-well-behaved.service b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-well-behaved.service new file mode 100644 index 0000000000..f81e2b8904 --- /dev/null +++ b/test/integration-tests/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify-reload-well-behaved.service @@ -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 diff --git a/test/units/TEST-80-NOTIFYACCESS.sh b/test/units/TEST-80-NOTIFYACCESS.sh index 4439e46362..41e65c37c6 100755 --- a/test/units/TEST-80-NOTIFYACCESS.sh +++ b/test/units/TEST-80-NOTIFYACCESS.sh @@ -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