1#!/usr/bin/env bash 2# SPDX-License-Identifier: LGPL-2.1-or-later 3 4set -eux 5set -o pipefail 6 7systemd-analyze log-level debug 8systemd-analyze log-target console 9 10unit=testsuite-38-sleep.service 11 12start_test_service() { 13 systemctl daemon-reload 14 systemctl start "${unit}" 15} 16 17dbus_freeze() { 18 local name object_path suffix 19 20 suffix="${1##*.}" 21 name="${1%.$suffix}" 22 object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" 23 24 busctl call \ 25 org.freedesktop.systemd1 \ 26 "${object_path}" \ 27 org.freedesktop.systemd1.Unit \ 28 Freeze 29} 30 31dbus_thaw() { 32 local name object_path suffix 33 34 suffix="${1##*.}" 35 name="${1%.$suffix}" 36 object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" 37 38 busctl call \ 39 org.freedesktop.systemd1 \ 40 "${object_path}" \ 41 org.freedesktop.systemd1.Unit \ 42 Thaw 43} 44 45dbus_freeze_unit() { 46 busctl call \ 47 org.freedesktop.systemd1 \ 48 /org/freedesktop/systemd1 \ 49 org.freedesktop.systemd1.Manager \ 50 FreezeUnit \ 51 s \ 52 "$1" 53} 54 55dbus_thaw_unit() { 56 busctl call \ 57 org.freedesktop.systemd1 \ 58 /org/freedesktop/systemd1 \ 59 org.freedesktop.systemd1.Manager \ 60 ThawUnit \ 61 s \ 62 "$1" 63} 64 65dbus_can_freeze() { 66 local name object_path suffix 67 68 suffix="${1##*.}" 69 name="${1%.$suffix}" 70 object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" 71 72 busctl get-property \ 73 org.freedesktop.systemd1 \ 74 "${object_path}" \ 75 org.freedesktop.systemd1.Unit \ 76 CanFreeze 77} 78 79check_freezer_state() { 80 local name object_path suffix 81 82 suffix="${1##*.}" 83 name="${1%.$suffix}" 84 object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" 85 86 for _ in {0..10}; do 87 state=$(busctl get-property \ 88 org.freedesktop.systemd1 \ 89 "${object_path}" \ 90 org.freedesktop.systemd1.Unit \ 91 FreezerState | cut -d " " -f2 | tr -d '"') 92 93 # Ignore the intermediate freezing & thawing states in case we check 94 # the unit state too quickly 95 [[ "$state" =~ ^(freezing|thawing)$ ]] || break 96 sleep .5 97 done 98 99 [ "$state" = "$2" ] || { 100 echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 101 exit 1 102 } 103} 104 105check_cgroup_state() { 106 grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events 107} 108 109test_dbus_api() { 110 echo "Test that DBus API works:" 111 echo -n " - Freeze(): " 112 dbus_freeze "${unit}" 113 check_freezer_state "${unit}" "frozen" 114 check_cgroup_state "$unit" 1 115 echo "[ OK ]" 116 117 echo -n " - Thaw(): " 118 dbus_thaw "${unit}" 119 check_freezer_state "${unit}" "running" 120 check_cgroup_state "$unit" 0 121 echo "[ OK ]" 122 123 echo -n " - FreezeUnit(): " 124 dbus_freeze_unit "${unit}" 125 check_freezer_state "${unit}" "frozen" 126 check_cgroup_state "$unit" 1 127 echo "[ OK ]" 128 129 echo -n " - ThawUnit(): " 130 dbus_thaw_unit "${unit}" 131 check_freezer_state "${unit}" "running" 132 check_cgroup_state "$unit" 0 133 echo "[ OK ]" 134 135 echo -n " - CanFreeze(): " 136 output=$(dbus_can_freeze "${unit}") 137 [ "$output" = "b true" ] 138 echo "[ OK ]" 139 140 echo 141} 142 143test_jobs() { 144 local pid_before= 145 local pid_after= 146 echo "Test that it is possible to apply jobs on frozen units:" 147 148 systemctl start "${unit}" 149 dbus_freeze "${unit}" 150 check_freezer_state "${unit}" "frozen" 151 152 echo -n " - restart: " 153 pid_before=$(systemctl show -p MainPID "${unit}" --value) 154 systemctl restart "${unit}" 155 pid_after=$(systemctl show -p MainPID "${unit}" --value) 156 [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" 157 158 dbus_freeze "${unit}" 159 check_freezer_state "${unit}" "frozen" 160 161 echo -n " - stop: " 162 timeout 5s systemctl stop "${unit}" 163 echo "[ OK ]" 164 165 echo 166} 167 168test_systemctl() { 169 echo "Test that systemctl freeze/thaw verbs:" 170 171 systemctl start "$unit" 172 173 echo -n " - freeze: " 174 systemctl freeze "$unit" 175 check_freezer_state "${unit}" "frozen" 176 check_cgroup_state "$unit" 1 177 # Freezing already frozen unit should be NOP and return quickly 178 timeout 3s systemctl freeze "$unit" 179 echo "[ OK ]" 180 181 echo -n " - thaw: " 182 systemctl thaw "$unit" 183 check_freezer_state "${unit}" "running" 184 check_cgroup_state "$unit" 0 185 # Likewise thawing already running unit shouldn't block 186 timeout 3s systemctl thaw "$unit" 187 echo "[ OK ]" 188 189 systemctl stop "$unit" 190 191 echo 192} 193 194test_systemctl_show() { 195 echo "Test systemctl show integration:" 196 197 systemctl start "$unit" 198 199 echo -n " - FreezerState property: " 200 state=$(systemctl show -p FreezerState --value "$unit") 201 [ "$state" = "running" ] 202 systemctl freeze "$unit" 203 state=$(systemctl show -p FreezerState --value "$unit") 204 [ "$state" = "frozen" ] 205 systemctl thaw "$unit" 206 echo "[ OK ]" 207 208 echo -n " - CanFreeze property: " 209 state=$(systemctl show -p CanFreeze --value "$unit") 210 [ "$state" = "yes" ] 211 echo "[ OK ]" 212 213 systemctl stop "$unit" 214 echo 215} 216 217test_recursive() { 218 local slice="bar.slice" 219 local unit="baz.service" 220 221 systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 222 223 echo "Test recursive freezing:" 224 225 echo -n " - freeze: " 226 systemctl freeze "$slice" 227 check_freezer_state "${slice}" "frozen" 228 check_freezer_state "${unit}" "frozen" 229 grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events 230 grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 231 echo "[ OK ]" 232 233 echo -n " - thaw: " 234 systemctl thaw "$slice" 235 check_freezer_state "${unit}" "running" 236 check_freezer_state "${slice}" "running" 237 grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events 238 grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 239 echo "[ OK ]" 240 241 systemctl stop "$unit" 242 systemctl stop "$slice" 243 244 echo 245} 246 247test_preserve_state() { 248 local slice="bar.slice" 249 local unit="baz.service" 250 251 systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 252 253 echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" 254 255 echo -n " - freeze from outside: " 256 echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze 257 # Give kernel some time to freeze the slice 258 sleep 1 259 260 # Our state should not be affected 261 check_freezer_state "${slice}" "running" 262 check_freezer_state "${unit}" "running" 263 264 # However actual kernel state should be frozen 265 grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events 266 grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 267 echo "[ OK ]" 268 269 echo -n " - thaw from outside: " 270 echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze 271 sleep 1 272 273 check_freezer_state "${unit}" "running" 274 check_freezer_state "${slice}" "running" 275 grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events 276 grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events 277 echo "[ OK ]" 278 279 echo -n " - thaw from outside while inner service is frozen: " 280 systemctl freeze "$unit" 281 check_freezer_state "${unit}" "frozen" 282 echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze 283 echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze 284 check_freezer_state "${slice}" "running" 285 check_freezer_state "${unit}" "frozen" 286 echo "[ OK ]" 287 288 systemctl stop "$unit" 289 systemctl stop "$slice" 290 291 echo 292} 293 294test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { 295 start_test_service 296 test_dbus_api 297 test_systemctl 298 test_systemctl_show 299 test_jobs 300 test_recursive 301 test_preserve_state 302} 303 304echo OK >/testok 305exit 0 306