]>
Commit | Line | Data |
---|---|---|
759ed0a2 YW |
1 | #!/usr/bin/env bash |
2 | # SPDX-License-Identifier: LGPL-2.1-or-later | |
3 | ||
cb153b4f | 4 | # Utility functions for shell tests |
759ed0a2 | 5 | |
c1b129f4 FS |
6 | # shellcheck disable=SC2034 |
7 | [[ -e /var/tmp/.systemd_reboot_count ]] && REBOOT_COUNT="$(</var/tmp/.systemd_reboot_count)" || REBOOT_COUNT=0 | |
8 | ||
d170b475 | 9 | assert_true() {( |
d170b475 YW |
10 | set +ex |
11 | ||
386427cf YW |
12 | local rc |
13 | ||
759ed0a2 YW |
14 | "$@" |
15 | rc=$? | |
d170b475 | 16 | if [[ $rc -ne 0 ]]; then |
759ed0a2 YW |
17 | echo "FAIL: command '$*' failed with exit code $rc" >&2 |
18 | exit 1 | |
19 | fi | |
d170b475 YW |
20 | )} |
21 | ||
d170b475 YW |
22 | assert_eq() {( |
23 | set +ex | |
759ed0a2 | 24 | |
d170b475 | 25 | if [[ "${1?}" != "${2?}" ]]; then |
759ed0a2 YW |
26 | echo "FAIL: expected: '$2' actual: '$1'" >&2 |
27 | exit 1 | |
28 | fi | |
d170b475 YW |
29 | )} |
30 | ||
ce35bb95 MK |
31 | assert_le() {( |
32 | set +ex | |
33 | ||
34 | if [[ "${1:?}" -gt "${2:?}" ]]; then | |
35 | echo "FAIL: '$1' > '$2'" >&2 | |
36 | exit 1 | |
37 | fi | |
38 | )} | |
39 | ||
d170b475 YW |
40 | assert_in() {( |
41 | set +ex | |
759ed0a2 | 42 | |
d170b475 | 43 | if ! [[ "${2?}" =~ ${1?} ]]; then |
759ed0a2 YW |
44 | echo "FAIL: '$1' not found in:" >&2 |
45 | echo "$2" >&2 | |
46 | exit 1 | |
47 | fi | |
d170b475 YW |
48 | )} |
49 | ||
50 | assert_not_in() {( | |
51 | set +ex | |
52 | ||
53 | if [[ "${2?}" =~ ${1?} ]]; then | |
54 | echo "FAIL: '$1' found in:" >&2 | |
55 | echo "$2" >&2 | |
56 | exit 1 | |
57 | fi | |
58 | )} | |
59 | ||
60 | assert_rc() {( | |
d170b475 | 61 | set +ex |
759ed0a2 | 62 | |
386427cf YW |
63 | local rc exp="${1?}" |
64 | ||
759ed0a2 | 65 | shift |
759ed0a2 YW |
66 | "$@" |
67 | rc=$? | |
759ed0a2 | 68 | assert_eq "$rc" "$exp" |
d170b475 | 69 | )} |
5ff1c6fc | 70 | |
c1b129f4 FS |
71 | assert_not_reached() { |
72 | echo >&2 "Code should not be reached at ${BASH_SOURCE[1]}:${BASH_LINENO[1]}, function ${FUNCNAME[1]}()" | |
73 | exit 1 | |
74 | } | |
75 | ||
1a36d267 FS |
76 | run_and_grep() {( |
77 | set +ex | |
78 | ||
79 | local expression | |
80 | local log ec | |
81 | local exp_ec=0 | |
82 | ||
83 | # Invert the grep condition - i.e. check if the expression is _not_ in command's output | |
84 | if [[ "${1:?}" == "-n" ]]; then | |
85 | exp_ec=1 | |
86 | shift | |
87 | fi | |
88 | ||
89 | expression="${1:?}" | |
90 | shift | |
91 | ||
92 | if [[ $# -eq 0 ]]; then | |
93 | echo >&2 "FAIL: Not enough arguments for ${FUNCNAME[0]}()" | |
94 | return 1 | |
95 | fi | |
96 | ||
97 | log="$(mktemp)" | |
98 | if ! "$@" |& tee "${log:?}"; then | |
99 | echo >&2 "FAIL: Command '$*' failed" | |
100 | return 1 | |
101 | fi | |
102 | ||
103 | grep -qE "$expression" "$log" && ec=0 || ec=$? | |
104 | if [[ "$exp_ec" -eq 0 && "$ec" -ne 0 ]]; then | |
105 | echo >&2 "FAIL: Expression '$expression' not found in the output of '$*'" | |
106 | return 1 | |
107 | elif [[ "$exp_ec" -ne 0 && "$ec" -eq 0 ]]; then | |
108 | echo >&2 "FAIL: Expression '$expression' found in the output of '$*'" | |
109 | return 1 | |
110 | fi | |
111 | ||
112 | rm -f "$log" | |
113 | )} | |
114 | ||
5ff1c6fc FS |
115 | get_cgroup_hierarchy() { |
116 | case "$(stat -c '%T' -f /sys/fs/cgroup)" in | |
117 | cgroup2fs) | |
118 | echo "unified" | |
119 | ;; | |
120 | tmpfs) | |
121 | if [[ -d /sys/fs/cgroup/unified && "$(stat -c '%T' -f /sys/fs/cgroup/unified)" == cgroup2fs ]]; then | |
122 | echo "hybrid" | |
123 | else | |
124 | echo "legacy" | |
125 | fi | |
126 | ;; | |
127 | *) | |
128 | echo >&2 "Failed to determine host's cgroup hierarchy" | |
129 | exit 1 | |
130 | esac | |
131 | } | |
7c6fa5bf FS |
132 | |
133 | runas() { | |
134 | local userid="${1:?}" | |
135 | shift | |
136 | XDG_RUNTIME_DIR=/run/user/"$(id -u "$userid")" setpriv --reuid="$userid" --init-groups "$@" | |
137 | } | |
5656759d | 138 | |
28ed2326 FS |
139 | coverage_create_nspawn_dropin() { |
140 | # If we're collecting coverage, bind mount the $BUILD_DIR into the nspawn | |
141 | # container so gcov can update the counters. This is mostly for standalone | |
142 | # containers, as machinectl stuff is handled by overriding the systemd-nspawn@.service | |
143 | # (see test/test-functions:install_systemd()) | |
144 | local root="${1:?}" | |
145 | local container | |
146 | ||
147 | if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then | |
148 | return 0 | |
149 | fi | |
150 | ||
151 | container="$(basename "$root")" | |
152 | mkdir -p "/run/systemd/nspawn" | |
153 | echo -ne "[Files]\nBind=$COVERAGE_BUILD_DIR\n" >"/run/systemd/nspawn/${container:?}.nspawn" | |
154 | } | |
155 | ||
5656759d FS |
156 | create_dummy_container() { |
157 | local root="${1:?}" | |
158 | ||
047d5b6a | 159 | if [[ ! -d /usr/share/testsuite-13-container-template ]]; then |
5656759d FS |
160 | echo >&2 "Missing container template, probably not running in TEST-13-NSPAWN?" |
161 | exit 1 | |
162 | fi | |
163 | ||
164 | mkdir -p "$root" | |
047d5b6a | 165 | cp -a /usr/share/testsuite-13-container-template/* "$root" |
28ed2326 | 166 | coverage_create_nspawn_dropin "$root" |
5656759d | 167 | } |
c1b129f4 FS |
168 | |
169 | # Bump the reboot counter and call systemctl with the given arguments | |
170 | systemctl_final() { | |
171 | local counter | |
172 | ||
173 | if [[ $# -eq 0 ]]; then | |
174 | echo >&2 "Missing arguments" | |
175 | exit 1 | |
176 | fi | |
177 | ||
178 | [[ -e /var/tmp/.systemd_reboot_count ]] && counter="$(</var/tmp/.systemd_reboot_count)" || counter=0 | |
179 | echo "$((counter + 1))" >/var/tmp/.systemd_reboot_count | |
180 | ||
181 | systemctl "$@" | |
182 | } | |
a2dd5920 FS |
183 | |
184 | cgroupfs_supports_user_xattrs() { | |
185 | local xattr | |
186 | ||
187 | xattr="user.supported_$RANDOM" | |
188 | # shellcheck disable=SC2064 | |
189 | trap "setfattr --remove=$xattr /sys/fs/cgroup || :" RETURN | |
190 | ||
191 | setfattr --name="$xattr" --value=254 /sys/fs/cgroup | |
192 | [[ "$(getfattr --name="$xattr" --absolute-names --only-values /sys/fs/cgroup)" -eq 254 ]] | |
193 | } | |
29be5955 FS |
194 | |
195 | tpm_has_pcr() { | |
196 | local algorithm="${1:?}" | |
197 | local pcr="${2:?}" | |
198 | ||
199 | [[ -f "/sys/class/tpm/tpm0/pcr-$algorithm/$pcr" ]] | |
200 | } | |
52a09e62 FS |
201 | |
202 | openssl_supports_kdf() { | |
203 | local kdf="${1:?}" | |
204 | ||
205 | # The arguments will need to be adjusted to make this work for other KDFs than SSKDF, | |
206 | # but let's do that when/if the need arises | |
207 | openssl kdf -keylen 16 -kdfopt digest:SHA2-256 -kdfopt key:foo -out /dev/null "$kdf" | |
208 | } | |
4a43c2b3 FS |
209 | |
210 | kernel_supports_lsm() { | |
211 | local lsm="${1:?}" | |
212 | local items item | |
213 | ||
214 | if [[ ! -e /sys/kernel/security/lsm ]]; then | |
215 | echo "/sys/kernel/security/lsm doesn't exist, assuming $lsm is not supported" | |
216 | return 1 | |
217 | fi | |
218 | ||
219 | mapfile -t -d, items </sys/kernel/security/lsm | |
220 | for item in "${items[@]}"; do | |
221 | if [[ "$item" == "$lsm" ]]; then | |
222 | return 0 | |
223 | fi | |
224 | done | |
225 | ||
226 | return 1 | |
227 | } | |
87993468 | 228 | |
c77dad70 DDM |
229 | install_extension_images() { |
230 | local os_release | |
231 | os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)" | |
232 | ||
233 | # Rolling distros like Arch do not set VERSION_ID | |
234 | local version_id="" | |
235 | if grep -q "^VERSION_ID=" "$os_release"; then | |
236 | version_id="$(grep "^VERSION_ID=" "$os_release")" | |
237 | fi | |
238 | ||
239 | local initdir="/var/tmp/app0" | |
240 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" | |
241 | grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0" | |
242 | echo "$version_id" >>"$initdir/usr/lib/extension-release.d/extension-release.app0" | |
243 | ( | |
244 | echo "$version_id" | |
245 | echo "SYSEXT_IMAGE_ID=app" | |
246 | ) >>"$initdir/usr/lib/extension-release.d/extension-release.app0" | |
247 | cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF | |
248 | [Service] | |
249 | Type=oneshot | |
250 | RemainAfterExit=yes | |
251 | ExecStart=/opt/script0.sh | |
252 | TemporaryFileSystem=/var/lib | |
253 | StateDirectory=app0 | |
254 | RuntimeDirectory=app0 | |
255 | EOF | |
256 | cat >"$initdir/opt/script0.sh" <<EOF | |
257 | #!/bin/bash | |
258 | set -e | |
259 | test -e /usr/lib/os-release | |
260 | echo bar >\${STATE_DIRECTORY}/foo | |
261 | cat /usr/lib/extension-release.d/extension-release.app0 | |
262 | EOF | |
263 | chmod +x "$initdir/opt/script0.sh" | |
264 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" | |
265 | mksquashfs "$initdir" /tmp/app0.raw -noappend | |
266 | ||
267 | initdir="/var/tmp/conf0" | |
268 | mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt" | |
269 | grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0" | |
270 | echo "$version_id" >>"$initdir/etc/extension-release.d/extension-release.conf0" | |
271 | ( | |
272 | echo "$version_id" | |
273 | echo "CONFEXT_IMAGE_ID=app" | |
274 | ) >>"$initdir/etc/extension-release.d/extension-release.conf0" | |
275 | echo MARKER_1 >"$initdir/etc/systemd/system/some_file" | |
276 | mksquashfs "$initdir" /tmp/conf0.raw -noappend | |
277 | ||
278 | initdir="/var/tmp/app1" | |
279 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" | |
280 | grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2" | |
281 | ( | |
282 | echo "$version_id" | |
283 | echo "SYSEXT_SCOPE=portable" | |
284 | echo "SYSEXT_IMAGE_ID=app" | |
285 | echo "SYSEXT_IMAGE_VERSION=1" | |
286 | echo "PORTABLE_PREFIXES=app1" | |
287 | ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2" | |
288 | setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2" | |
289 | cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF | |
290 | [Service] | |
291 | Type=oneshot | |
292 | RemainAfterExit=yes | |
293 | ExecStart=/opt/script1.sh | |
294 | StateDirectory=app1 | |
295 | RuntimeDirectory=app1 | |
296 | EOF | |
297 | cat >"$initdir/opt/script1.sh" <<EOF | |
298 | #!/bin/bash | |
299 | set -e | |
300 | test -e /usr/lib/os-release | |
301 | echo baz >\${STATE_DIRECTORY}/foo | |
302 | cat /usr/lib/extension-release.d/extension-release.app2 | |
303 | EOF | |
304 | chmod +x "$initdir/opt/script1.sh" | |
305 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file" | |
306 | mksquashfs "$initdir" /tmp/app1.raw -noappend | |
307 | ||
308 | initdir="/var/tmp/app-nodistro" | |
309 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" | |
310 | ( | |
311 | echo "ID=_any" | |
312 | echo "ARCHITECTURE=_any" | |
313 | ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" | |
314 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" | |
315 | mksquashfs "$initdir" /tmp/app-nodistro.raw -noappend | |
316 | ||
317 | initdir="/var/tmp/service-scoped-test" | |
318 | mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" | |
319 | ( | |
320 | echo "ID=_any" | |
321 | echo "ARCHITECTURE=_any" | |
322 | ) >"$initdir/etc/extension-release.d/extension-release.service-scoped-test" | |
323 | echo MARKER_CONFEXT_123 >"$initdir/etc/systemd/system/some_file" | |
324 | mksquashfs "$initdir" /etc/service-scoped-test.raw -noappend | |
325 | ||
326 | # We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the | |
327 | # 'foo.service' it will be loaded from another test run, which will impact the targeted test. | |
328 | initdir="/var/tmp/app-reload" | |
329 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" | |
330 | ( | |
331 | echo "ID=_any" | |
332 | echo "ARCHITECTURE=_any" | |
333 | echo "EXTENSION_RELOAD_MANAGER=1" | |
334 | ) >"$initdir/usr/lib/extension-release.d/extension-release.app-reload" | |
335 | mkdir -p "$initdir/usr/lib/systemd/system/multi-user.target.d" | |
336 | cat >"$initdir/usr/lib/systemd/system/foo.service" <<EOF | |
337 | [Service] | |
338 | Type=oneshot | |
339 | RemainAfterExit=yes | |
340 | ExecStart=echo foo | |
341 | ||
342 | [Install] | |
343 | WantedBy=multi-user.target | |
344 | EOF | |
345 | echo -e "[Unit]\nUpholds=foo.service" >"$initdir/usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf" | |
346 | mksquashfs "$initdir" /tmp/app-reload.raw -noappend | |
347 | } | |
a2190c22 DDM |
348 | |
349 | restore_locale() { | |
350 | if [[ -d /usr/lib/locale/xx_XX.UTF-8 ]]; then | |
351 | rmdir /usr/lib/locale/xx_XX.UTF-8 | |
352 | fi | |
353 | ||
354 | if [[ -f /tmp/locale.conf.bak ]]; then | |
355 | mv /tmp/locale.conf.bak /etc/locale.conf | |
356 | else | |
357 | rm -f /etc/locale.conf | |
358 | fi | |
359 | ||
360 | if [[ -f /tmp/default-locale.bak ]]; then | |
361 | mv /tmp/default-locale.bak /etc/default/locale | |
362 | else | |
363 | rm -rf /etc/default | |
364 | fi | |
365 | ||
366 | if [[ -f /tmp/locale.gen.bak ]]; then | |
367 | mv /tmp/locale.gen.bak /etc/locale.gen | |
368 | else | |
369 | rm -f /etc/locale.gen | |
370 | fi | |
371 | } | |
372 | ||
373 | generate_locale() { | |
374 | local locale="${1:?}" | |
375 | ||
376 | if command -v locale-gen >/dev/null && ! localectl list-locales | grep -F "$locale"; then | |
377 | echo "$locale UTF-8" >/etc/locale.gen | |
378 | locale-gen "$locale" | |
379 | fi | |
380 | } |