mirror of
https://github.com/systemd/systemd.git
synced 2024-12-26 03:22:00 +03:00
4cb2e6af8d
Previously, unit_{start,stop,reload} would call the low-level cgroup unfreeze function whenever a unit was started, stopped, or reloaded. It did so with no error checking. This call would ultimately recurse up the cgroup tree, and unfreeze all the parent cgroups of the unit, unless an error occurred (in which case I have no idea what would happen...) After the freeze/thaw rework in a previous commit, this can no longer work. If we recursively thaw the parent cgroups of the unit, there may be sibling units marked as PARENT_FROZEN which will no longer actually have frozen parents. Fixing this is a lot more complicated than simply disallowing start/stop/reload on a frozen unit Fixes https://github.com/systemd/systemd/issues/15849
364 lines
9.9 KiB
Bash
Executable File
364 lines
9.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
# shellcheck disable=SC2317
|
|
set -eux
|
|
set -o pipefail
|
|
|
|
# shellcheck source=test/units/test-control.sh
|
|
. "$(dirname "$0")"/test-control.sh
|
|
|
|
systemd-analyze log-level debug
|
|
|
|
unit=testsuite-38-sleep.service
|
|
|
|
start_test_service() {
|
|
systemctl daemon-reload
|
|
systemctl start "${unit}"
|
|
}
|
|
|
|
dbus_freeze() {
|
|
local name object_path suffix
|
|
|
|
suffix="${1##*.}"
|
|
name="${1%".$suffix"}"
|
|
object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
|
|
|
|
busctl call \
|
|
org.freedesktop.systemd1 \
|
|
"${object_path}" \
|
|
org.freedesktop.systemd1.Unit \
|
|
Freeze
|
|
}
|
|
|
|
dbus_thaw() {
|
|
local name object_path suffix
|
|
|
|
suffix="${1##*.}"
|
|
name="${1%".$suffix"}"
|
|
object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
|
|
|
|
busctl call \
|
|
org.freedesktop.systemd1 \
|
|
"${object_path}" \
|
|
org.freedesktop.systemd1.Unit \
|
|
Thaw
|
|
}
|
|
|
|
dbus_freeze_unit() {
|
|
busctl call \
|
|
org.freedesktop.systemd1 \
|
|
/org/freedesktop/systemd1 \
|
|
org.freedesktop.systemd1.Manager \
|
|
FreezeUnit \
|
|
s \
|
|
"$1"
|
|
}
|
|
|
|
dbus_thaw_unit() {
|
|
busctl call \
|
|
org.freedesktop.systemd1 \
|
|
/org/freedesktop/systemd1 \
|
|
org.freedesktop.systemd1.Manager \
|
|
ThawUnit \
|
|
s \
|
|
"$1"
|
|
}
|
|
|
|
dbus_can_freeze() {
|
|
local name object_path suffix
|
|
|
|
suffix="${1##*.}"
|
|
name="${1%".$suffix"}"
|
|
object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
|
|
|
|
busctl get-property \
|
|
org.freedesktop.systemd1 \
|
|
"${object_path}" \
|
|
org.freedesktop.systemd1.Unit \
|
|
CanFreeze
|
|
}
|
|
|
|
check_freezer_state() {
|
|
local name object_path suffix
|
|
|
|
suffix="${1##*.}"
|
|
name="${1%".$suffix"}"
|
|
object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
|
|
|
|
for _ in {0..10}; do
|
|
state=$(busctl get-property \
|
|
org.freedesktop.systemd1 \
|
|
"${object_path}" \
|
|
org.freedesktop.systemd1.Unit \
|
|
FreezerState | cut -d " " -f2 | tr -d '"')
|
|
|
|
# Ignore the intermediate freezing & thawing states in case we check
|
|
# the unit state too quickly
|
|
[[ "$state" =~ ^(freezing|thawing)$ ]] || break
|
|
sleep .5
|
|
done
|
|
|
|
[ "$state" = "$2" ] || {
|
|
echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
check_cgroup_state() {
|
|
# foo.unit -> /system.slice/foo.unit/
|
|
# foo.slice/ -> /foo.slice/./
|
|
# foo.slice/foo.unit -> /foo.slice/foo.unit/
|
|
local slice unit
|
|
unit="${1##*/}"
|
|
slice="${1%"$unit"}"
|
|
slice="${slice%/}"
|
|
grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events
|
|
}
|
|
|
|
testcase_dbus_api() {
|
|
echo "Test that DBus API works:"
|
|
echo -n " - Freeze(): "
|
|
dbus_freeze "${unit}"
|
|
check_freezer_state "${unit}" "frozen"
|
|
check_cgroup_state "$unit" 1
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - Thaw(): "
|
|
dbus_thaw "${unit}"
|
|
check_freezer_state "${unit}" "running"
|
|
check_cgroup_state "$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - FreezeUnit(): "
|
|
dbus_freeze_unit "${unit}"
|
|
check_freezer_state "${unit}" "frozen"
|
|
check_cgroup_state "$unit" 1
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - ThawUnit(): "
|
|
dbus_thaw_unit "${unit}"
|
|
check_freezer_state "${unit}" "running"
|
|
check_cgroup_state "$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - CanFreeze(): "
|
|
output=$(dbus_can_freeze "${unit}")
|
|
[ "$output" = "b true" ]
|
|
echo "[ OK ]"
|
|
|
|
echo
|
|
}
|
|
|
|
testcase_systemctl() {
|
|
echo "Test that systemctl freeze/thaw verbs:"
|
|
|
|
systemctl start "$unit"
|
|
|
|
echo -n " - freeze: "
|
|
systemctl freeze "$unit"
|
|
check_freezer_state "${unit}" "frozen"
|
|
check_cgroup_state "$unit" 1
|
|
# Freezing already frozen unit should be NOP and return quickly
|
|
timeout 3s systemctl freeze "$unit"
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - thaw: "
|
|
systemctl thaw "$unit"
|
|
check_freezer_state "${unit}" "running"
|
|
check_cgroup_state "$unit" 0
|
|
# Likewise thawing already running unit shouldn't block
|
|
timeout 3s systemctl thaw "$unit"
|
|
echo "[ OK ]"
|
|
|
|
systemctl stop "$unit"
|
|
|
|
echo
|
|
}
|
|
|
|
testcase_systemctl_show() {
|
|
echo "Test systemctl show integration:"
|
|
|
|
systemctl start "$unit"
|
|
|
|
echo -n " - FreezerState property: "
|
|
state=$(systemctl show -p FreezerState --value "$unit")
|
|
[ "$state" = "running" ]
|
|
systemctl freeze "$unit"
|
|
state=$(systemctl show -p FreezerState --value "$unit")
|
|
[ "$state" = "frozen" ]
|
|
systemctl thaw "$unit"
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - CanFreeze property: "
|
|
state=$(systemctl show -p CanFreeze --value "$unit")
|
|
[ "$state" = "yes" ]
|
|
echo "[ OK ]"
|
|
|
|
systemctl stop "$unit"
|
|
echo
|
|
}
|
|
|
|
testcase_recursive() {
|
|
local slice="bar.slice"
|
|
local unit="baz.service"
|
|
|
|
systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
|
|
|
|
echo "Test recursive freezing:"
|
|
|
|
echo -n " - freeze/thaw parent: "
|
|
systemctl freeze "$slice"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen-by-parent"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$slice"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "running"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - child freeze/thaw during frozen parent: "
|
|
systemctl freeze "$slice"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen-by-parent"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl freeze "$unit"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$unit"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen-by-parent"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$slice"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "running"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - pre-frozen child not thawed by parent: "
|
|
systemctl freeze "$unit"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl freeze "$slice"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$slice"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 1
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - pre-frozen child demoted and thawed by parent: "
|
|
systemctl freeze "$slice"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$unit"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen-by-parent"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$slice"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "running"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - child promoted and not thawed by parent: "
|
|
systemctl freeze "$slice"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen-by-parent"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl freeze "$unit"
|
|
check_freezer_state "$slice" "frozen"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
systemctl thaw "$slice"
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "frozen"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 1
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - can't stop a frozen unit: "
|
|
(! systemctl -q stop "$unit" )
|
|
echo "[ OK ]"
|
|
systemctl thaw "$unit"
|
|
|
|
systemctl stop "$unit"
|
|
systemctl stop "$slice"
|
|
|
|
echo
|
|
}
|
|
|
|
testcase_preserve_state() {
|
|
local slice="bar.slice"
|
|
local unit="baz.service"
|
|
|
|
systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
|
|
|
|
echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
|
|
|
|
echo -n " - freeze from outside: "
|
|
echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
|
|
# Give kernel some time to freeze the slice
|
|
sleep 1
|
|
|
|
# Our state should not be affected
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "running"
|
|
|
|
# However actual kernel state should be frozen
|
|
check_cgroup_state "$slice/" 1
|
|
check_cgroup_state "$slice/$unit" 1
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - thaw from outside: "
|
|
echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
|
|
sleep 1
|
|
|
|
check_freezer_state "$unit" "running"
|
|
check_freezer_state "$slice" "running"
|
|
check_cgroup_state "$slice/" 0
|
|
check_cgroup_state "$slice/$unit" 0
|
|
echo "[ OK ]"
|
|
|
|
echo -n " - thaw from outside while inner service is frozen: "
|
|
systemctl freeze "$unit"
|
|
check_freezer_state "$unit" "frozen"
|
|
echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
|
|
echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
|
|
check_freezer_state "$slice" "running"
|
|
check_freezer_state "$unit" "frozen"
|
|
echo "[ OK ]"
|
|
|
|
systemctl thaw "$unit"
|
|
systemctl stop "$unit"
|
|
systemctl stop "$slice"
|
|
|
|
echo
|
|
}
|
|
|
|
if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then
|
|
start_test_service
|
|
run_testcases
|
|
fi
|
|
|
|
touch /testok
|