#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.1-or-later set -eux set -o pipefail systemd-analyze log-level debug systemd-analyze log-target console 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() { grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events } test_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 } test_jobs() { local pid_before= local pid_after= echo "Test that it is possible to apply jobs on frozen units:" systemctl start "${unit}" dbus_freeze "${unit}" check_freezer_state "${unit}" "frozen" echo -n " - restart: " pid_before=$(systemctl show -p MainPID "${unit}" --value) systemctl restart "${unit}" pid_after=$(systemctl show -p MainPID "${unit}" --value) [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" dbus_freeze "${unit}" check_freezer_state "${unit}" "frozen" echo -n " - stop: " timeout 5s systemctl stop "${unit}" echo "[ OK ]" echo } test_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 } test_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 } test_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: " systemctl freeze "$slice" check_freezer_state "${slice}" "frozen" check_freezer_state "${unit}" "frozen" grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events echo "[ OK ]" echo -n " - thaw: " systemctl thaw "$slice" check_freezer_state "${unit}" "running" check_freezer_state "${slice}" "running" grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events echo "[ OK ]" systemctl stop "$unit" systemctl stop "$slice" echo } test_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 grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 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" grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 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 stop "$unit" systemctl stop "$slice" echo } test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { start_test_service test_dbus_api test_systemctl test_systemctl_show test_jobs test_recursive test_preserve_state } echo OK >/testok exit 0