]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-functions
test: Rename testsuite-XX units to match test name
[thirdparty/systemd.git] / test / test-functions
CommitLineData
ff12a795 1#!/usr/bin/env bash
898720b7 2# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
8f5bcd61
ZJS
3# SPDX-License-Identifier: LGPL-2.1-or-later
4#
5# shellcheck disable=SC2030,SC2031
96af59aa
FS
6# ex: ts=8 sw=4 sts=4 et filetype=sh tw=180
7# Note: the shellcheck line above disables warning for variables which were
8# modified in a subshell. In our case this behavior is expected, but
9# `shellcheck` can't distinguish this because of poor variable tracking,
10# which results in warning for every instance of such variable used
11# throughout this file.
12# See:
13# * comment in function install_verity_minimal()
14# * koalaman/shellcheck#280
15set -o pipefail
16
b54bc139
FS
17# Simple wrapper to unify boolean checks.
18# Note: this function needs to stay near the top of the file, so we can use it
19# in code in the outermost scope.
20get_bool() {
21 # Make the value lowercase to make the regex matching simpler
22 local _bool="${1,,}"
23
24 # Consider empty value as "false"
25 if [[ -z "$_bool" || "$_bool" =~ ^(0|no|false)$ ]]; then
26 return 1
27 elif [[ "$_bool" =~ ^(1|yes|true)$ ]]; then
28 return 0
29 else
30 echo >&2 "Value '$_bool' is not a valid boolean value"
31 exit 1
32 fi
33}
34
898720b7
HH
35PATH=/sbin:/bin:/usr/sbin:/usr/bin
36export PATH
37
ae6c5987 38os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)
1b8fcd9c
FS
39# shellcheck source=/dev/null
40source "$os_release"
b54bc139 41[[ "$ID" == "debian" || " $ID_LIKE " == *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=no
62564681
LP
42# shellcheck disable=SC2034
43[[ "$ID" == "ubuntu" ]] && LOOKS_LIKE_UBUNTU=yes || LOOKS_LIKE_UBUNTU=no
b54bc139 44[[ "$ID" == "arch" || " $ID_LIKE " == *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=no
8ddbd9e0 45[[ "$ID" == "fedora" ]] && LOOKS_LIKE_FEDORA=yes || LOOKS_LIKE_FEDORA=no
b54bc139
FS
46[[ " $ID_LIKE " == *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=no
47
1b8fcd9c 48KERNEL_VER="${KERNEL_VER-$(uname -r)}"
cd15f7f6
FS
49QEMU_TIMEOUT="${QEMU_TIMEOUT:-1800}"
50NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-1800}"
b2ecd099 51TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
18c769b0 52get_bool "$LOOKS_LIKE_SUSE" && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
22f1f8f2 53UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
5f28f3dd 54EFI_MOUNT="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}"
d9e606e8
LB
55# Note that defining a different IMAGE_NAME in a test setup script will only result
56# in default.img being copied and renamed. It can then be extended by defining
57# a test_append_files() function. The $1 parameter will be the root directory.
58# To force creating a new image from scratch (eg: to encrypt it), also define
59# TEST_FORCE_NEWIMAGE=1 in the test setup script.
8c3534b5 60IMAGE_NAME=${IMAGE_NAME:-default}
9d84eb20 61TEST_REQUIRE_INSTALL_TESTS="${TEST_REQUIRE_INSTALL_TESTS:-1}"
7a57256c 62TEST_PARALLELIZE="${TEST_PARALLELIZE:-0}"
508a7f04 63TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED="${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED:-1}"
1506edca 64LOOPDEV=
898720b7 65
b92c3df2
FS
66# Since in Bash we can have only one handler per signal, let's overcome this
67# limitation by having one global handler for the EXIT signal which executes
68# all registered handlers
69_AT_EXIT_HANDLERS=()
70_at_exit() {
71 set +e
72
73 # Run the EXIT handlers in reverse order
74 for ((i = ${#_AT_EXIT_HANDLERS[@]} - 1; i >= 0; i--)); do
75 ddebug "Running EXIT handler '${_AT_EXIT_HANDLERS[$i]}'"
18fa5c82 76 eval "${_AT_EXIT_HANDLERS[$i]}"
b92c3df2
FS
77 done
78}
79
80trap _at_exit EXIT
81
82add_at_exit_handler() {
18fa5c82 83 _AT_EXIT_HANDLERS+=("${1:?}")
b92c3df2
FS
84}
85
3e8caa34 86# Decide if we can (and want to) run qemu with KVM acceleration.
501deda1
FS
87# Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
88# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
89# running under KVM. If these conditions are met, enable KVM (and possibly
90# nested KVM), otherwise disable it.
12ee072d 91if get_bool "${TEST_NESTED_KVM:=}" || (! get_bool "${TEST_NO_KVM:=}" && ! systemd-detect-virt -qv); then
501deda1
FS
92 QEMU_KVM=yes
93else
94 QEMU_KVM=no
95fi
96
3486cb6c 97if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
b0d3095f 98 echo "WARNING! Cannot determine libdir from pkg-config, assuming /usr/lib/systemd" >&2
3486cb6c
MP
99 ROOTLIBDIR=/usr/lib/systemd
100fi
101
42f3b48c 102# The calling test.sh scripts have TEST_BASE_DIR set via their Makefile, but we don't need them to provide it
1b8fcd9c 103TEST_BASE_DIR=${TEST_BASE_DIR:-$(realpath "$(dirname "${BASH_SOURCE[0]}")")}
c4cd6205 104TEST_UNITS_DIR="$(realpath "$TEST_BASE_DIR/units")"
42f3b48c 105SOURCE_DIR=$(realpath "$TEST_BASE_DIR/..")
1b8fcd9c 106# These variables are used by test scripts
04ef5d1b 107export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR
42f3b48c 108
04ef5d1b 109TOOLS_DIR="$SOURCE_DIR/tools"
12d31e4e 110# note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
dfd73ccb
FB
111if get_bool "${NO_BUILD:=}"; then
112 BUILD_DIR="$SOURCE_DIR"
113elif ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
114 echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
115 exit 1
12d31e4e
DS
116fi
117
a33e2692
FS
118PATH_TO_INIT="$ROOTLIBDIR/systemd"
119SYSTEMD_JOURNALD="${SYSTEMD_JOURNALD:-$(command -v "$BUILD_DIR/systemd-journald" || command -v "$ROOTLIBDIR/systemd-journald")}"
cca3050b 120SYSTEMD_JOURNAL_REMOTE="${SYSTEMD_JOURNAL_REMOTE:-$(command -v "$BUILD_DIR/systemd-journal-remote" || command -v "$ROOTLIBDIR/systemd-journal-remote" || echo "")}"
a33e2692
FS
121SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR/systemd")}"
122SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
123JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
354b3364 124SYSTEMCTL="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}"
a0b50e4d 125SYSTEMD_ID128="${SYSTEMD_ID128:-$(command -v "$BUILD_DIR/systemd-id128" || command -v systemd-id128)}"
016fa3b9 126
1b8fcd9c 127TESTFILE="${BASH_SOURCE[1]}"
42f3b48c
DS
128if [ -z "$TESTFILE" ]; then
129 echo "ERROR: test-functions must be sourced from one of the TEST-*/test.sh scripts" >&2
130 exit 1
131fi
1b8fcd9c 132TESTNAME="$(basename "$(dirname "$(realpath "$TESTFILE")")")"
0c566150
FB
133
134WORKDIR="/var/tmp/systemd-tests"
135if get_bool "${NO_BUILD:=}"; then
136 STATEDIR="$WORKDIR/$TESTNAME"
137else
138 STATEDIR="$BUILD_DIR/test/$TESTNAME"
139fi
140
19184069
DS
141STATEFILE="$STATEDIR/.testdir"
142IMAGESTATEDIR="$STATEDIR/.."
143TESTLOG="$STATEDIR/test.log"
144
c4cd6205
FS
145if ! [[ "$TESTNAME" =~ ^TEST\-([0-9]+)\-.+$ ]]; then
146 echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2
147 exit 1
148fi
c4cd6205 149
7a321b5a
DDM
150if [[ ! -f "$TEST_UNITS_DIR/$TESTNAME.service" ]]; then
151 echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/$TESTNAME.service" >&2
c4cd6205
FS
152 exit 1
153fi
154
4110a6de 155BASICTOOLS=(
f4c40fd7 156 awk
e7848266 157 base64
f4c40fd7 158 basename
4110a6de 159 bash
f4c40fd7 160 capsh
4110a6de 161 cat
52c1fb68 162 chgrp
4110a6de
ZJS
163 chmod
164 chown
a7eed3ec 165 chroot
4110a6de 166 cmp
6162caa2 167 cp
4110a6de 168 cryptsetup
f4c40fd7 169 cut
4110a6de 170 date
f4c40fd7 171 dd
6162caa2 172 dhclient
f4c40fd7
ZJS
173 diff
174 dirname
4110a6de
ZJS
175 dmsetup
176 echo
177 env
178 false
6162caa2 179 find
af70e202 180 findmnt
41d1aaea 181 flock
e4ff8040 182 getconf
f4c40fd7
ZJS
183 getent
184 getfacl
a2dd5920 185 getfattr
e29e4d57 186 grep
6162caa2 187 grep
f4c40fd7 188 gunzip
4110a6de
ZJS
189 gzip
190 head
6162caa2
FS
191 hostname
192 id
f4c40fd7 193 ionice
e5badaf3 194 ip
7906b790 195 jq
5c08efee 196 killall
42867dfe 197 ldd
4110a6de 198 ln
6162caa2 199 ln
4110a6de
ZJS
200 loadkeys
201 login
92ecc875 202 losetup
6162caa2 203 ls
378db9e2 204 lsattr
af70e202 205 lsblk
f4c40fd7 206 lz4cat
6162caa2 207 mkdir
c0b97b0f 208 mkfifo
e6c281c1 209 mknod
c0b97b0f 210 mktemp
4110a6de
ZJS
211 modprobe
212 mount
e47add9e 213 mountpoint
d10029bb
ZJS
214 mv
215 nc
f4c40fd7 216 nproc
6162caa2 217 ping
9c94ab0f 218 pkill
6162caa2 219 ps
c0b97b0f 220 readlink
bf9b5691 221 realpath
f4c40fd7 222 rev
4110a6de 223 rm
e5badaf3 224 rmdir
9c94ab0f 225 rmmod
990b629e 226 script
4110a6de 227 sed
f4c40fd7 228 seq
d5a6ff8c 229 setfacl
59331b8e 230 setfattr
4110a6de 231 setfont
c7bf1959 232 setpriv
4110a6de 233 setsid
e29e4d57 234 sfdisk
4110a6de
ZJS
235 sh
236 sleep
6162caa2 237 sort
d10029bb 238 stat
61961e69 239 stty
d0ac89a1 240 su
4110a6de 241 sulogin
e5badaf3 242 sysctl
4110a6de 243 tail
6e796683 244 tar
4110a6de
ZJS
245 tee
246 test
d446ae89 247 timeout
3ac189d8 248 touch
f4c40fd7 249 tr
4110a6de 250 true
e29e4d57 251 truncate
6162caa2 252 tty
4110a6de 253 umount
f4c40fd7 254 uname
e5badaf3 255 unshare
9c94ab0f
YW
256 useradd
257 userdel
9fbb13ac 258 wc
dfdcc7c9 259 whoami
4110a6de 260 xargs
f4c40fd7 261 xzcat
4110a6de
ZJS
262)
263
264DEBUGTOOLS=(
4110a6de 265 df
4110a6de
ZJS
266 dmesg
267 du
4110a6de 268 free
4110a6de 269 less
4110a6de 270 strace
4110a6de 271 vi
8afe2f53 272 /usr/libexec/vi
4110a6de 273)
889a9042 274
ec9181d2 275is_built_with_asan() {
b727d7e0
FS
276 local _bin="${1:?}"
277
ec9181d2 278 if ! type -P objdump >/dev/null; then
f258a763 279 echo "Failed to find objdump, assuming systemd hasn't been built with ASAN."
ec9181d2
EV
280 return 1
281 fi
282
283 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
1b8fcd9c 284 local _asan_calls
b727d7e0 285 _asan_calls="$(objdump -dC "$_bin" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)"
1b8fcd9c 286 if ((_asan_calls < 1000)); then
ec9181d2
EV
287 return 1
288 else
289 return 0
290 fi
291}
292
7a6c5b6a
FS
293is_built_with_coverage() {
294 if get_bool "${NO_BUILD:=}" || ! command -v meson >/dev/null; then
295 return 1
296 fi
297
298 meson configure "${BUILD_DIR:?}" | grep 'b_coverage' | awk '{ print $2 }' | grep -q 'true'
299}
300
b727d7e0 301IS_BUILT_WITH_ASAN=$(is_built_with_asan "$SYSTEMD_JOURNALD" && echo yes || echo no)
7a6c5b6a 302IS_BUILT_WITH_COVERAGE=$(is_built_with_coverage && echo yes || echo no)
ec9181d2 303
23f8e019 304if get_bool "$IS_BUILT_WITH_ASAN"; then
ba79e8c2 305 PATH_TO_INIT="$ROOTLIBDIR/systemd-under-asan"
dc350e78 306 QEMU_MEM="${QEMU_MEM:-2G}"
648fd189 307 QEMU_SMP="${QEMU_SMP:-4}"
37ee8dc8
FS
308
309 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
d49b881e 310 if ASAN_RT_NAME="$(awk '/libasan.so/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
37ee8dc8 311 ASAN_COMPILER=gcc
648fd189 312 ASAN_RT_PATH="$(readlink -f "$(${CC:-gcc} --print-file-name "$ASAN_RT_NAME")")"
d49b881e 313 elif ASAN_RT_NAME="$(awk '/libclang_rt.asan/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
37ee8dc8 314 ASAN_COMPILER=clang
648fd189 315 ASAN_RT_PATH="$(readlink -f "$(${CC:-clang} --print-file-name "$ASAN_RT_NAME")")"
37ee8dc8
FS
316
317 # As clang's ASan DSO is usually in a non-standard path, let's check if
318 # the environment is set accordingly. If not, warn the user and exit.
319 # We're not setting the LD_LIBRARY_PATH automagically here, because
320 # user should encounter (and fix) the same issue when running the unit
321 # tests (meson test)
84c49ad1 322 if ldd "$SYSTEMD" | grep -q "libclang_rt.asan.*not found"; then
648fd189
FS
323 echo >&2 "clang's ASan DSO ($ASAN_RT_NAME) is not present in the runtime library path"
324 echo >&2 "Consider setting LD_LIBRARY_PATH=${ASAN_RT_PATH%/*}"
37ee8dc8
FS
325 exit 1
326 fi
327 else
328 echo >&2 "systemd is not linked against the ASan DSO"
329 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
330 exit 1
331 fi
648fd189
FS
332
333 echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
ec9181d2
EV
334fi
335
70bdf6e6
FS
336test_require_bin() {
337 local bin
338
339 for bin in "$@"; do
340 if ! command -v "$bin" >/dev/null; then
341 echo "Required binary $bin not available, skipping the test"
342 exit 0
343 fi
344 done
345}
346
1b8fcd9c
FS
347find_qemu_bin() {
348 QEMU_BIN="${QEMU_BIN:-""}"
3e7aa2ed 349 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
23f8e019 350 if get_bool "$QEMU_KVM"; then
1b8fcd9c 351 [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)"
3e7aa2ed 352 fi
c6a77179 353
1b8fcd9c 354 [[ -n "$ARCH" ]] || ARCH="$(uname -m)"
c6a77179
RC
355 case $ARCH in
356 x86_64)
357 # QEMU's own build system calls it qemu-system-x86_64
1b8fcd9c 358 [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)"
c6a77179
RC
359 ;;
360 i*86)
361 # new i386 version of QEMU
1b8fcd9c 362 [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)"
c6a77179
RC
363
364 # i386 version of QEMU
1b8fcd9c 365 [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu 2>/dev/null | grep '^/' -m1)"
c6a77179 366 ;;
cf5f9bb8 367 ppc64*)
1b8fcd9c 368 [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)"
cf5f9bb8 369 ;;
c6a77179
RC
370 esac
371
1b8fcd9c 372 if [[ ! -e "$QEMU_BIN" ]]; then
3e8caa34 373 echo "Could not find a suitable qemu binary" >&2
c6a77179
RC
374 return 1
375 fi
376}
377
18fa5c82
FS
378qemu_setup_swtpm_socket() {
379 local pid state_dir tpm_device
380
381 if ! tpm_device="$(qemu_get_tpm_device)"; then
382 dinfo "Found QEMU version is too old for TPM2 on ppc64le"
383 exit 0
384 fi
385
386 state_dir="$(mktemp -d)"
387 swtpm socket --tpm2 --tpmstate dir="$state_dir" --ctrl type=unixio,path="$state_dir/sock" &
388 pid=$!
389 if ! kill -0 "$pid"; then
18c3ffbf
FS
390 derror "Failed to start swtpm"
391 return 1
392 fi
393
394 if ! timeout 5 bash -c "until [[ -S $state_dir/sock ]]; do sleep .5; done"; then
395 derror "Failed to setup swtpm socket"
18fa5c82
FS
396 return 1
397 fi
398
399 dinfo "Started swtpm as PID $pid with state dir $state_dir"
400
401 add_at_exit_handler "kill -TERM $pid 2>/dev/null; rm -rf '$state_dir'"
402
403 QEMU_OPTIONS+=" -chardev socket,id=chrtpm,path=$state_dir/sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device $tpm_device,tpmdev=tpm0"
404 dinfo "Configured emulated TPM2 device $tpm_device"
405
406 return 0
407}
408
409qemu_get_tpm_device() {
410 local tpm_device="tpm-tis"
411
412 if [[ "$(uname -m)" == "ppc64le" ]]; then
413 # tpm-spapr support was introduced in qemu 5.0.0
414 if ! qemu_min_version "5.0.0"; then
415 return 1
416 fi
417
418 tpm_device="tpm-spapr"
419 fi
420
421 echo "$tpm_device"
422 return 0
423}
424
43b49470
CE
425# Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu
426# returns 0 if newer or equal
427# returns 1 if older
428# returns 2 if failing
1b8fcd9c 429qemu_min_version() {
43b49470
CE
430 find_qemu_bin || return 2
431
432 # get version from binary
1b8fcd9c
FS
433 local qemu_ver
434 qemu_ver="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')"
43b49470
CE
435
436 # Check version string format
437 echo "$qemu_ver" | grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' || return 2
438 echo "$1" | grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' || return 2
439
440 # compare as last command to return that value
441 printf "%s\n%s\n" "$1" "$qemu_ver" | sort -V -C
442}
443
4a262d56
LP
444# Pads a file to multiple of 4 bytes
445pad4_file() {
446 local size
447 size=$(stat -c "%s" "$1")
448 local padded
449 padded=$((((size + 3) / 4) * 4))
450 truncate -s "$padded" "$1"
451}
452
3e8caa34
ZJS
453# Return 0 if qemu did run (then you must check the result state/logs for actual
454# success), or 1 if qemu is not available.
889a9042 455run_qemu() {
4a262d56
LP
456 if declare -F run_qemu_hook >/dev/null; then
457 if ! run_qemu_hook "${workspace}"; then
458 derror "check_qemu_hook() returned with EC > 0"
459 ret=4
460 fi
461 fi
462
b22d90e5
FS
463 # If the test provided its own initrd, use it (e.g. TEST-24)
464 if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then
465 INITRD="$TESTDIR/initrd.img"
466 fi
467
b6f0c419 468 if [ -f /etc/machine-id ]; then
1b8fcd9c 469 read -r MACHINE_ID </etc/machine-id
906bbac4
ZJS
470 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
471 && INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
472 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
473 && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
b6f0c419
HH
474 fi
475
1b8fcd9c 476 local CONSOLE=ttyS0
9a2e265b 477
3630f5aa 478 find "${initdir:?}/var/tmp" -mindepth 1 -delete
80b44b38 479 rm -f "$initdir"/{testok,failed,skipped}
ec43f686
ZJS
480 # make sure the initdir is not mounted to avoid concurrent access
481 cleanup_initdir
482 umount_loopback
483
e1a27318 484 if [[ ! "$KERNEL_BIN" ]]; then
23f8e019 485 if get_bool "$LOOKS_LIKE_ARCH"; then
e1a27318
EV
486 KERNEL_BIN=/boot/vmlinuz-linux
487 else
eaa602cb
DJL
488 [ "$ARCH" ] || ARCH=$(uname -m)
489 case $ARCH in
490 ppc64*)
46db176f
FS
491 # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
492 # uses the "standard" vmlinuz- prefix
493 [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
494 CONSOLE=hvc0
495 ;;
eaa602cb 496 *)
46db176f
FS
497 KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
498 ;;
eaa602cb 499 esac
e1a27318
EV
500 fi
501 fi
502
1b8fcd9c
FS
503 local default_fedora_initrd="/boot/initramfs-${KERNEL_VER}.img"
504 local default_debian_initrd="/boot/initrd.img-${KERNEL_VER}"
505 local default_arch_initrd="/boot/initramfs-linux-fallback.img"
506 local default_suse_initrd="/boot/initrd-${KERNEL_VER}"
e1a27318
EV
507 if [[ ! "$INITRD" ]]; then
508 if [[ -e "$default_fedora_initrd" ]]; then
509 INITRD="$default_fedora_initrd"
b54bc139 510 elif get_bool "$LOOKS_LIKE_DEBIAN" && [[ -e "$default_debian_initrd" ]]; then
e1a27318 511 INITRD="$default_debian_initrd"
b54bc139 512 elif get_bool "$LOOKS_LIKE_ARCH" && [[ -e "$default_arch_initrd" ]]; then
e1a27318 513 INITRD="$default_arch_initrd"
b54bc139 514 elif get_bool "$LOOKS_LIKE_SUSE" && [[ -e "$default_suse_initrd" ]]; then
85393d8f 515 INITRD="$default_suse_initrd"
e1a27318
EV
516 fi
517 fi
518
5bfb2a93
FS
519 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
520 # i.e. use the number of online CPUs on the host machine. If the nproc utility
521 # is not installed or there's some other error when calling it, fall back
522 # to the original value (QEMU_SMP=1).
1b8fcd9c 523 if [[ -z "${QEMU_SMP:=}" ]]; then
5bfb2a93
FS
524 if ! QEMU_SMP=$(nproc); then
525 dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
526 QEMU_SMP=1
527 fi
528 fi
c6a77179
RC
529
530 find_qemu_bin || return 1
531
18fa5c82
FS
532 if get_bool "${TEST_SETUP_SWTPM:-}"; then
533 qemu_setup_swtpm_socket || return 1
534 fi
535
8c3534b5 536 # Umount initdir to avoid concurrent access to the filesystem
0f194705
FS
537 _umount_dir "$initdir"
538
539 local kernel_params=()
540 local qemu_options=()
541 local qemu_cmd=("$QEMU_BIN")
8c3534b5 542
22f1f8f2 543 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
0f194705 544 kernel_params+=("systemd.unified_cgroup_hierarchy=yes")
22f1f8f2 545 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
0f194705 546 kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes")
22f1f8f2 547 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
0f194705 548 kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no")
22f1f8f2
EV
549 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
550 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
551 exit 1
552 fi
553
23f8e019 554 if get_bool "$LOOKS_LIKE_SUSE"; then
0f194705 555 kernel_params+=("rd.hostonly=0")
cc5549ca 556 fi
85393d8f 557
8fb26ccd
LB
558 # Debian/Ubuntu's initramfs tries to check if it can resume from hibernation
559 # and wastes a minute or so probing disks, skip that as it's not useful here
0f194705 560 kernel_params+=(
bac05644 561 "root=LABEL=systemd_boot"
0f194705
FS
562 "rw"
563 "raid=noautodetect"
564 "rd.luks=0"
565 "loglevel=2"
566 "init=$PATH_TO_INIT"
567 "console=$CONSOLE"
7a321b5a 568 "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$1.units:/usr/lib/systemd/tests/testdata/units:"
0f194705 569 "systemd.unit=testsuite.target"
7a321b5a 570 "systemd.wants=$1.service"
8fb26ccd 571 "noresume"
5ab2f737 572 "oops=panic"
0efa27bd
FS
573 ${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
574 ${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"}
0f194705
FS
575 )
576
5ab2f737 577 if ! get_bool "$INTERACTIVE_DEBUG" && ! get_bool "$TEST_SKIP_SHUTDOWN"; then
842a9d5f 578 kernel_params+=(
842a9d5f 579 "panic=1"
4e086c38 580 "softlockup_panic=1"
842a9d5f
FS
581 "systemd.wants=end.service"
582 )
fe85f2bb
ZJS
583 fi
584
e8945092 585 [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
0f194705
FS
586 qemu_options+=(
587 -smp "$QEMU_SMP"
588 -net none
6a9c4977 589 -m "${QEMU_MEM:-768M}"
0f194705
FS
590 -nographic
591 -kernel "$KERNEL_BIN"
592 -drive "format=raw,cache=unsafe,file=$image"
4d5d28e9 593 -device "virtio-rng-pci,max-bytes=1024,period=1000"
0f194705
FS
594 )
595
596 if [[ -n "${QEMU_OPTIONS:=}" ]]; then
597 local user_qemu_options
598 read -ra user_qemu_options <<< "$QEMU_OPTIONS"
599 qemu_options+=("${user_qemu_options[@]}")
600 fi
18a6cd4b 601 qemu_options+=(${QEMU_OPTIONS_ARRAY:+"${QEMU_OPTIONS_ARRAY[@]}"})
0f194705 602
93b896e9 603 if [[ -n "$INITRD" ]]; then
4a262d56
LP
604 if [[ -n "$INITRD_EXTRA" ]]; then
605 # An addition initrd has been specified, let's combine it with the main one.
606 local t="$WORKDIR"/initrd.combined."$RANDOM"
607
608 # First, show contents of additional initrd
609 echo "Additional initrd contents:"
610 cpio -tv < "$INITRD_EXTRA"
611
612 # Copy the main initrd
613 zstd -d -c -f "$INITRD" > "$t"
614 add_at_exit_handler "rm $t"
615 # Kernel requires this to be padded to multiple of 4 bytes with zeroes
616 pad4_file "$t"
617
618 # Copy the additional initrd
619 cat "$INITRD_EXTRA" >> "$t"
620 pad4_file "$t"
621
622 qemu_options+=(-initrd "$t")
623 else
624 qemu_options+=(-initrd "$INITRD")
625 fi
c6a77179
RC
626 fi
627
501deda1 628 # Let's use KVM if possible
23f8e019 629 if [[ -c /dev/kvm ]] && get_bool $QEMU_KVM; then
0f194705 630 qemu_options+=(-machine "accel=kvm" -enable-kvm -cpu host)
dbf43a42
DM
631 fi
632
91f9f8f1 633 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
0f194705 634 qemu_cmd=(timeout --foreground "$QEMU_TIMEOUT" "$QEMU_BIN")
91f9f8f1 635 fi
0f194705 636
4f3d8def 637 (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]} ${KERNEL_APPEND:-}" |& tee "${TESTDIR:?}/console.log")
b2ecd099 638 rc=$?
23f8e019
FS
639 if [ "$rc" -eq 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
640 derror "Test timed out after ${QEMU_TIMEOUT}s"
b2ecd099
MP
641 TIMED_OUT=1
642 else
3e8caa34 643 [ "$rc" != 0 ] && derror "qemu failed with exit code $rc"
b2ecd099
MP
644 fi
645 return 0
889a9042
RC
646}
647
7cad32bb
MP
648# Return 0 if nspawn did run (then you must check the result state/logs for actual
649# success), or 1 if nspawn is not available.
889a9042 650run_nspawn() {
7cad32bb 651 [[ -d /run/systemd/system ]] || return 1
3630f5aa 652 find "${initdir:?}/var/tmp" -mindepth 1 -delete
0f194705
FS
653 rm -f "${initdir:?}"/{testok,failed,skipped}
654
655 local nspawn_cmd=()
656 local nspawn_options=(
657 "--register=no"
658 "--kill-signal=SIGKILL"
659 "--directory=${1:?}"
7a321b5a
DDM
660 "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$2.units:/usr/lib/systemd/tests/testdata/units:"
661 "--machine=$2"
0f194705
FS
662 )
663 local kernel_params=(
664 "$PATH_TO_INIT"
665 "systemd.unit=testsuite.target"
7a321b5a 666 "systemd.wants=$2.service"
0efa27bd
FS
667 ${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
668 ${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"}
fe85f2bb
ZJS
669 )
670
5599c84b
FS
671 if get_bool "$INTERACTIVE_DEBUG"; then
672 nspawn_options+=("--console=interactive")
5ab2f737 673 elif ! get_bool "$TEST_SKIP_SHUTDOWN"; then
0f194705 674 kernel_params+=("systemd.wants=end.service")
fe85f2bb
ZJS
675 fi
676
0f194705
FS
677 if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then
678 local user_nspawn_arguments
679 read -ra user_nspawn_arguments <<< "$NSPAWN_ARGUMENTS"
680 nspawn_options+=("${user_nspawn_arguments[@]}")
681 fi
682
22f1f8f2 683 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
c78c095b 684 dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
22f1f8f2
EV
685 exit
686 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
0f194705 687 nspawn_cmd+=(env "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY")
22f1f8f2 688 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
0f194705 689 nspawn_cmd+=(env "--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY")
22f1f8f2
EV
690 else
691 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
692 exit 1
693 fi
856ca72b 694
0f194705
FS
695 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
696 nspawn_cmd+=(timeout --foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN")
697 else
698 nspawn_cmd+=("$SYSTEMD_NSPAWN")
699 fi
700
4f3d8def
FS
701 # Word splitting here is intentional
702 # shellcheck disable=SC2086
703 (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}" ${KERNEL_APPEND:-} |& tee "${TESTDIR:?}/console.log")
b2ecd099 704 rc=$?
23f8e019
FS
705 if [ "$rc" -eq 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
706 derror "Test timed out after ${NSPAWN_TIMEOUT}s"
b2ecd099
MP
707 TIMED_OUT=1
708 else
709 [ "$rc" != 0 ] && derror "nspawn failed with exit code $rc"
710 fi
711 return 0
889a9042
RC
712}
713
9785c44d
LB
714# Build two very minimal root images, with two units, one is the same and one is different across them
715install_verity_minimal() {
1b8fcd9c
FS
716 dinfo "Set up a set of minimal images for verity verification"
717 if [ -e "$initdir/usr/share/minimal.raw" ]; then
9785c44d
LB
718 return
719 fi
720 if ! command -v mksquashfs >/dev/null 2>&1; then
721 dfatal "mksquashfs not found"
722 exit 1
723 fi
724 if ! command -v veritysetup >/dev/null 2>&1; then
725 dfatal "veritysetup not found"
726 exit 1
727 fi
1b8fcd9c
FS
728 # Local modifications of some global variables is intentional in this
729 # subshell (SC2030)
730 # shellcheck disable=SC2030
9785c44d
LB
731 (
732 BASICTOOLS=(
733 bash
734 cat
62564681 735 echo
93f59701 736 grep
9785c44d
LB
737 mount
738 sleep
d110169b 739 touch
9785c44d 740 )
1b8fcd9c
FS
741 oldinitdir="$initdir"
742 rm -rfv "$TESTDIR/minimal"
743 export initdir="$TESTDIR/minimal"
3fa80e5e
LB
744 # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
745 mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
9785c44d
LB
746 setup_basic_dirs
747 install_basic_tools
4163c877 748 install_ld_so_conf
1b8fcd9c
FS
749 # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
750 # issue, thus falsely triggering SC2030 in this case
751 # See: koalaman/shellcheck#1409
9f6235e1
FS
752 if [[ -v ASAN_RT_PATH ]]; then
753 # If we're compiled with ASan, install the ASan RT (and its dependencies)
754 # into the verity images to get rid of the annoying errors about
755 # missing $LD_PRELOAD libraries.
756 inst_libs "$ASAN_RT_PATH"
757 inst_library "$ASAN_RT_PATH"
758 fi
1b8fcd9c
FS
759 cp "$os_release" "$initdir/usr/lib/os-release"
760 ln -s ../usr/lib/os-release "$initdir/etc/os-release"
761 touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
762 touch "$initdir/opt/some_file"
763 echo MARKER=1 >>"$initdir/usr/lib/os-release"
d76f0de7
LB
764 echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
765 cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
766[Service]
767ExecStartPre=cat /usr/lib/os-release
768ExecStart=sleep 120
769EOF
770 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
1b8fcd9c 771
4f2dba98 772 mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
1b8fcd9c
FS
773 veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
774 grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
775
776 sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
d76f0de7
LB
777 rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
778 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
1b8fcd9c 779
4f2dba98 780 mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
1b8fcd9c
FS
781 veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
782 grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
9785c44d
LB
783 )
784}
785
889a9042
RC
786setup_basic_environment() {
787 # create the basic filesystem layout
788 setup_basic_dirs
789
790 install_systemd
791 install_missing_libraries
792 install_config_files
f4c40fd7 793 install_zoneinfo
889a9042
RC
794 create_rc_local
795 install_basic_tools
796 install_libnss
797 install_pam
798 install_dbus
799 install_fonts
4ce68ea9 800 install_locales
889a9042 801 install_keymaps
1136175c 802 install_x11_keymaps
889a9042
RC
803 install_terminfo
804 install_execs
0dd77c15
ZJS
805 install_fs_tools
806 install_modules
889a9042 807 install_plymouth
d93857ae 808 install_haveged
889a9042
RC
809 install_debug_tools
810 install_ld_so_conf
d0ac89a1
ZJS
811 install_testuser
812 has_user_dbus_socket && install_user_dbus
e3ce42e7 813 setup_selinux
889a9042
RC
814 install_depmod_files
815 generate_module_dependencies
23f8e019 816 if get_bool "$IS_BUILT_WITH_ASAN"; then
37ee8dc8 817 create_asan_wrapper
ec9181d2 818 fi
23f8e019 819 if get_bool "$TEST_INSTALL_VERITY_MINIMAL"; then
9785c44d
LB
820 install_verity_minimal
821 fi
889a9042
RC
822}
823
e3ce42e7 824setup_selinux() {
1b8fcd9c 825 dinfo "Setup SELinux"
e3ce42e7 826 # don't forget KERNEL_APPEND='... selinux=1 ...'
23f8e019 827 if ! get_bool "$SETUP_SELINUX"; then
1b8fcd9c 828 dinfo "SETUP_SELINUX != yes, skipping SELinux configuration"
e3ce42e7
EV
829 return 0
830 fi
e3ce42e7 831
5ef964f8
FS
832 for dir in /etc/selinux /usr/share/selinux; do
833 rm -rf "${initdir:?}/$dir"
834 if ! cp -ar "$dir" "$initdir/$dir"; then
835 dfatal "Failed to copy $dir"
836 exit 1
837 fi
838 done
e3ce42e7 839
8c0ace57
FS
840 # We use a custom autorelabel service instead of the SELinux provided set
841 # of units & a generator, since the generator overrides the default target
842 # to the SELinux one when it detects /.autorelabel. However, we use
843 # systemd.unit= on the kernel command cmdline which always takes precedence,
844 # rendering all SELinux efforts useless. Also, pulling in selinux-autorelabel.service
845 # explicitly doesn't work either, as it doesn't check for the presence of /.autorelabel
846 # and does the relabeling unconditionally which always ends with a reboot, so
847 # we end up in a reboot loop (and it also spews quite a lot of errors as it
848 # wants /etc/fstab and dracut-initramfs-restore).
1b8fcd9c
FS
849 touch "$initdir/.autorelabel"
850 mkdir -p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
851 ln -sf ../autorelabel.service "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
e3ce42e7 852
5ef964f8
FS
853 # Tools requires by fixfiles
854 image_install awk bash cat chcon expr egrep find grep head secon setfiles rm sort uname uniq
855 image_install fixfiles getenforce load_policy selinuxenabled sestatus
e3ce42e7
EV
856}
857
a2fbff31
EV
858install_valgrind() {
859 if ! type -p valgrind; then
860 dfatal "Failed to install valgrind"
861 exit 1
862 fi
863
71116990 864 local valgrind_bins valgrind_libs valgrind_supp
1b8fcd9c 865
71116990
ZJS
866 readarray -t valgrind_bins < <(strace -e execve valgrind /bin/true 2>&1 >/dev/null |
867 sed -r -n 's/execve\("([^"]*)".*/\1/p')
c66e2f6c 868 image_install "${valgrind_bins[@]}"
a2fbff31 869
71116990
ZJS
870 readarray -t valgrind_libs < <(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null |
871 sed -r -n 's|.*calling init: (/.*vgpreload_.*)|\1|p')
c66e2f6c 872 image_install "${valgrind_libs[@]}"
a2fbff31 873
71116990
ZJS
874 readarray -t valgrind_supp < <(strace -e open valgrind /bin/true 2>&1 >/dev/null |
875 sed -r -n 's,open\("([^"]*(/debug[^"]*|\.supp))".*= [0-9].*,\1,p')
876 image_install "${valgrind_supp[@]}"
a2fbff31
EV
877}
878
cb2f9d3f 879create_valgrind_wrapper() {
1b8fcd9c
FS
880 local valgrind_wrapper="$initdir/$ROOTLIBDIR/systemd-under-valgrind"
881 ddebug "Create $valgrind_wrapper"
882 cat >"$valgrind_wrapper" <<EOF
ff12a795 883#!/usr/bin/env bash
cb2f9d3f 884
23cabb68 885mount -t proc proc /proc
8e78dca9 886exec valgrind --leak-check=full --track-fds=yes --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
cb2f9d3f 887EOF
1b8fcd9c 888 chmod 0755 "$valgrind_wrapper"
cb2f9d3f
EV
889}
890
1786fae3 891create_asan_wrapper() {
88c98cb2 892 local asan_wrapper default_asan_options default_ubsan_options default_environment manager_environment
37ee8dc8 893
648fd189
FS
894 [[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be"
895
91da9458 896 default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1}"
ba79e8c2 897 default_ubsan_options="${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}"
37ee8dc8 898
ba79e8c2
FS
899 if [[ "$ASAN_COMPILER" == "clang" ]]; then
900 # clang: install llvm-symbolizer to generate useful reports
901 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
902 image_install "llvm-symbolizer"
1786fae3 903
ba79e8c2
FS
904 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
905 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
906 # runtime ASan DSO is in a non-standard (library) path.
907 mkdir -p "${initdir:?}/etc/ld.so.conf.d/"
908 echo "${ASAN_RT_PATH%/*}" >"${initdir:?}/etc/ld.so.conf.d/asan-path-override.conf"
25bc4697 909 ldconfig -r "$initdir"
ba79e8c2 910 fi
25213e16 911
ba79e8c2
FS
912 # Create a simple environment file which can be included by systemd services
913 # that need it (i.e. services that utilize DynamicUser=true and bash, etc.)
914 cat >"${initdir:?}/usr/lib/systemd/systemd-asan-env" <<EOF
25213e16 915LD_PRELOAD=$ASAN_RT_PATH
ba79e8c2 916ASAN_OPTIONS=$default_asan_options
b8dd2766 917LSAN_OPTIONS=detect_leaks=0
ba79e8c2
FS
918UBSAN_OPTIONS=$default_ubsan_options
919EOF
1786fae3 920
ba79e8c2
FS
921 default_environment=(
922 "ASAN_OPTIONS='$default_asan_options'"
923 "UBSAN_OPTIONS='$default_ubsan_options'"
924 "ASAN_RT_PATH='$ASAN_RT_PATH'"
925 )
88c98cb2
FS
926 manager_environment=(
927 "ASAN_OPTIONS='$default_asan_options:log_path=/systemd-pid1.asan.log:log_to_syslog=1'"
928 "UBSAN_OPTIONS='$default_ubsan_options:log_path=/systemd-pid1.ubsan.log:log_to_syslog=1'"
929 "ASAN_RT_PATH='$ASAN_RT_PATH'"
930 )
648fd189 931
ba79e8c2
FS
932 mkdir -p "${initdir:?}/etc/systemd/system.conf.d/"
933 cat >"${initdir:?}/etc/systemd/system.conf.d/asan.conf" <<EOF
934[Manager]
935DefaultEnvironment=${default_environment[*]}
88c98cb2 936ManagerEnvironment=${manager_environment[*]}
ba79e8c2 937DefaultTimeoutStartSec=180s
ba79e8c2 938EOF
1786fae3 939
ba79e8c2
FS
940 # ASAN and syscall filters aren't compatible with each other.
941 find "${initdir:?}" -name '*.service' -type f -print0 | xargs -0 sed -i 's/^\(MemoryDeny\|SystemCall\)/#\1/'
1786fae3 942
ba79e8c2
FS
943 mkdir -p "${initdir:?}/etc/systemd/system/systemd-journald.service.d/"
944 cat >"${initdir:?}/etc/systemd/system/systemd-journald.service.d/asan-env.conf" <<EOF
945[Service]
88ed0f26
EV
946# The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
947# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
ba79e8c2 948Environment=ASAN_OPTIONS=$default_asan_options:log_path=/systemd-journald.asan.log UBSAN_OPTIONS=$default_ubsan_options:log_path=/systemd-journald.ubsan.log
88ed0f26 949
6141c6c9
EV
950# Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
951# Let's try to catch them by redirecting stderr (and stdout just in case) to a file
952# See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
ba79e8c2
FS
953StandardOutput=file:/systemd-journald.out
954EOF
6141c6c9 955
ba79e8c2
FS
956 # 90s isn't enough for some services to finish when literally everything is run
957 # under ASan+UBSan in containers, which, in turn, are run in VMs.
958 # Let's limit which environments such services should be executed in.
959 mkdir -p "${initdir:?}/etc/systemd/system/systemd-hwdb-update.service.d/"
960 cat >"${initdir:?}/etc/systemd/system/systemd-hwdb-update.service.d/asan.conf" <<EOF
961[Unit]
962ConditionVirtualization=container
082bcdca 963
ba79e8c2
FS
964[Service]
965TimeoutSec=240s
966EOF
a5372344 967
ba79e8c2
FS
968 # Let's override another hard-coded timeout that kicks in too early
969 mkdir -p "${initdir:?}/etc/systemd/system/systemd-journal-flush.service.d/"
970 cat >"${initdir:?}/etc/systemd/system/systemd-journal-flush.service.d/asan.conf" <<EOF
971[Service]
972TimeoutSec=180s
1786fae3
EV
973EOF
974
ba79e8c2
FS
975 asan_wrapper="${initdir:?}/${PATH_TO_INIT:?}"
976 # Sanity check to make sure we don't overwrite something we shouldn't.
977 [[ "$asan_wrapper" =~ systemd-under-asan$ ]]
978
979 cat >"$asan_wrapper" <<EOF
980#!/usr/bin/env bash
981set -eux
982
3bba91ef 983export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
88c98cb2 984export ${manager_environment[@]}
ba79e8c2 985[[ -n "\$ASAN_OPTIONS" && -n "\$UBSAN_OPTIONS" ]]
3bba91ef 986
ba79e8c2
FS
987exec "$ROOTLIBDIR/systemd" "\$@"
988EOF
1b8fcd9c 989 chmod 0755 "$asan_wrapper"
1786fae3
EV
990}
991
45dbd7b6 992create_strace_wrapper() {
1b8fcd9c
FS
993 local strace_wrapper="$initdir/$ROOTLIBDIR/systemd-under-strace"
994 ddebug "Create $strace_wrapper"
995 cat >"$strace_wrapper" <<EOF
ff12a795 996#!/usr/bin/env bash
45dbd7b6 997
1b8fcd9c 998exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd" "\$@"
45dbd7b6 999EOF
1b8fcd9c 1000 chmod 0755 "$strace_wrapper"
45dbd7b6
EV
1001}
1002
0dd77c15 1003install_fs_tools() {
1b8fcd9c 1004 dinfo "Install fsck"
39e17536
FS
1005 image_install /sbin/fsck*
1006 image_install -o /bin/fsck*
331fb4ca
EV
1007
1008 # fskc.reiserfs calls reiserfsck. so, install it
39e17536 1009 image_install -o reiserfsck
0dd77c15
ZJS
1010
1011 # we use mkfs in system-repart tests
39e17536
FS
1012 image_install /sbin/mkfs.ext4
1013 image_install /sbin/mkfs.vfat
8d82e390
SL
1014
1015 # we use mkswap in udev-storage tests
1016 image_install /sbin/mkswap
0dd77c15
ZJS
1017}
1018
1019install_modules() {
1020 dinfo "Install modules"
1021
adafa3b2 1022 instmods bridge dummy ext4 ipvlan macvlan vfat veth
807626d1 1023 instmods loop =block
35cde9e9 1024 instmods nls_ascii =nls
807626d1 1025 instmods overlay =overlayfs
23ff8a77 1026 instmods scsi_debug
0dd77c15 1027
23f8e019 1028 if get_bool "$LOOKS_LIKE_SUSE"; then
adafa3b2 1029 instmods af_packet
0dd77c15 1030 fi
9974ff63
EV
1031}
1032
889a9042
RC
1033install_dmevent() {
1034 instmods dm_crypt =crypto
59279e96 1035 inst_binary dmeventd
e3d9a2e7 1036 image_install "${ROOTLIBDIR:?}"/system/dm-event.{service,socket}
23f8e019 1037 if get_bool "$LOOKS_LIKE_DEBIAN"; then
ac289ce3 1038 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
9c869ff6
DJL
1039 # and since buster/bionic 95-dm-notify.rules
1040 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
1041 inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
ac289ce3
EV
1042 else
1043 inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
1044 fi
23f8e019 1045 if get_bool "$LOOKS_LIKE_SUSE"; then
2aa5a13a
ER
1046 inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules
1047 fi
889a9042
RC
1048}
1049
9e7c3bd4
FS
1050install_multipath() {
1051 instmods "=md" multipath
1052 image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
1053 image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
1054 if get_bool "$LOOKS_LIKE_DEBIAN"; then
7eb234fe 1055 # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 95-kpartx.rules (as seen on
519f0074 1056 # Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4)
7eb234fe 1057 inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
9e7c3bd4
FS
1058 else
1059 inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
1060 fi
1061 mkdir -p "${initdir:?}/etc/multipath"
1062
1063 local file
1064 while read -r file; do
1065 # Install libraries required by the given library
1066 inst_libs "$file"
1067 # Install the library itself and create necessary symlinks
1068 inst_library "$file"
1069 done < <(find /lib*/multipath -type f)
9e7c3bd4
FS
1070}
1071
4999f368 1072install_lvm() {
f9ba9d3e
FS
1073 local lvm_rules rule_prefix
1074
4999f368
FS
1075 image_install lvm
1076 image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket}
e50d743f 1077 image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service
4999f368 1078 image_install -o "/lib/tmpfiles.d/lvm2.conf"
f9ba9d3e 1079
4999f368 1080 if get_bool "$LOOKS_LIKE_DEBIAN"; then
f9ba9d3e
FS
1081 lvm_rules="56-lvm.rules"
1082 rule_prefix=""
4999f368 1083 else
f9ba9d3e
FS
1084 lvm_rules="11-dm-lvm.rules"
1085 rule_prefix="dm-"
4999f368 1086 fi
f9ba9d3e
FS
1087
1088 # Support the new udev autoactivation introduced in lvm 2.03.14
1089 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
1090 # Static autoactivation (via lvm2-activation-generator) was dropped
1091 # in lvm 2.03.15
1092 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
1093 if [[ -f "/lib/udev/rules.d/69-${rule_prefix}lvm.rules" ]]; then
1094 inst_rules "$lvm_rules" "69-${rule_prefix}lvm.rules"
1095 else
1096 image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
1097 image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
1098 inst_rules "$lvm_rules" "69-${rule_prefix}lvm-metad.rules"
1099 fi
1100
4999f368
FS
1101 mkdir -p "${initdir:?}/etc/lvm"
1102}
1103
5b4fa6f1
YW
1104host_has_btrfs() (
1105 set -e
1106 modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
1107)
1108
babe9355
FS
1109install_btrfs() {
1110 instmods btrfs
1111 # Not all utilities provided by btrfs-progs are listed here; extend the list
1112 # if necessary
1113 image_install btrfs btrfstune mkfs.btrfs
1114 inst_rules 64-btrfs-dm.rules
1115}
1116
f4e64b6e
FS
1117install_iscsi() {
1118 # Install both client and server side stuff by default
1119 local inst="${1:-}"
1120 local file
1121
1122 # Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case
1123 # (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
1124 if [[ -z "$inst" || "$inst" =~ (client|initiator) ]]; then
1125 image_install iscsi-iname iscsiadm iscsid iscsistart
1126 image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service
1127 image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket}
1128 image_install "${ROOTLIBDIR:?}"/system/iscsi.service
1129 mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
1130 mkdir -p "${initdir:?}/etc/iscsi"
1131 echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
d9e1cb28
FS
1132 # Since open-iscsi 2.1.2 [0] the initiator name should be generated via
1133 # a one-time service instead of distro package's post-install scripts.
1134 # However, some distros still use this approach even after this patch,
1135 # so prefer the already existing initiatorname.iscsi file if it exists.
1136 #
1137 # [0] https://github.com/open-iscsi/open-iscsi/commit/f37d5b653f9f251845db3f29b1a3dcb90ec89731
1138 if [[ ! -e /etc/iscsi/initiatorname.iscsi ]]; then
1139 image_install "${ROOTLIBDIR:?}"/system/iscsi-init.service
326425fb
FS
1140 if get_bool "$IS_BUILT_WITH_ASAN"; then
1141 # The iscsi-init.service calls `sh` which might, in certain circumstances,
1142 # pull in instrumented systemd NSS modules causing `sh` to fail. Let's mitigate
1143 # this by pulling in an env file crafted by `create_asan_wrapper()` that
1144 # (among others) pre-loads ASan's DSO.
1145 mkdir -p "${initdir:?}/etc/systemd/system/iscsi-init.service.d/"
1146 printf "[Service]\nEnvironmentFile=/usr/lib/systemd/systemd-asan-env" >"${initdir:?}/etc/systemd/system/iscsi-init.service.d/asan-env.conf"
1147 fi
d9e1cb28
FS
1148 else
1149 inst_simple "/etc/iscsi/initiatorname.iscsi"
1150 fi
f4e64b6e
FS
1151 fi
1152
1153 # Install server-side stuff ("target" in iSCSI jargon) - TGT in this case
1154 # (tgt on Debian, scsi-target-utils on Fedora, etc.)
1155 if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then
1156 image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
1157 image_install -o /etc/sysconfig/tgtd
1158 image_install "${ROOTLIBDIR:?}"/system/tgtd.service
1159 mkdir -p "${initdir:?}/etc/tgt"
1160 touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf
1161 # Install perl modules required by tgt-admin
1162 #
1163 # Forgive me father for I have sinned. The monstrosity below appends
1164 # a perl snippet to the `tgt-admin` perl script on the fly, which
1165 # dumps a list of files (perl modules) required by `tgt-admin` at
1166 # the runtime plus any DSOs loaded via DynaLoader. This list is then
1167 # passed to `inst_simple` which installs the necessary files into the image
1c3f490f
FS
1168 #
1169 # shellcheck disable=SC2016
f4e64b6e
FS
1170 while read -r file; do
1171 inst_simple "$file"
1c3f490f 1172 done < <(perl -- <(cat "$(command -v tgt-admin)" <(echo -e 'use DynaLoader; print map { "$_\n" } values %INC; print join("\n", @DynaLoader::dl_shared_objects)')) -p | awk '/^\// { print $1 }')
f4e64b6e
FS
1173 fi
1174}
1175
5b4fa6f1
YW
1176host_has_mdadm() (
1177 set -e
1178 command -v mdadm || return $?
1179)
1180
3c9af05c
FS
1181install_mdadm() {
1182 local unit
1183 local mdadm_units=(
1184 system/mdadm-grow-continue@.service
1185 system/mdadm-last-resort@.service
1186 system/mdadm-last-resort@.timer
1187 system/mdmon@.service
1188 system/mdmonitor-oneshot.service
1189 system/mdmonitor-oneshot.timer
1190 system/mdmonitor.service
1191 system-shutdown/mdadm.shutdown
1192 )
1193
4ed943e9 1194 instmods "=md"
3c9af05c
FS
1195 image_install mdadm mdmon
1196 inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules
1197 # Fedora/CentOS/RHEL ships this rule file
1198 [[ -f /lib/udev/rules.d/65-md-incremental.rules ]] && inst_rules 65-md-incremental.rules
1199
1200 for unit in "${mdadm_units[@]}"; do
1201 image_install "${ROOTLIBDIR:?}/$unit"
1202 done
0f236e8c
YW
1203
1204 # Disable the mdmonitor service, since it fails if there's no valid email address
1205 # configured in /etc/mdadm.conf, which just unnecessarily pollutes the logs
1206 "${SYSTEMCTL:?}" mask --root "${initdir:?}" mdmonitor.service || :
3c9af05c
FS
1207}
1208
8fa03808 1209install_compiled_systemd() {
1b8fcd9c 1210 dinfo "Install compiled systemd"
9f927e46 1211
1b8fcd9c
FS
1212 local ninja_bin
1213 ninja_bin="$(type -P ninja || type -P ninja-build)"
1214 if [[ -z "$ninja_bin" ]]; then
ca992ecf
EV
1215 dfatal "ninja was not found"
1216 exit 1
1217 fi
1b8fcd9c 1218 (set -x; DESTDIR="$initdir" "$ninja_bin" -C "$BUILD_DIR" install)
c82dc15b
LB
1219
1220 # If we are doing coverage runs, copy over the binary notes files, as lcov expects to
1221 # find them in the same directory as the runtime data counts
02d7e730 1222 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
c82dc15b
LB
1223 mkdir -p "${initdir}/${BUILD_DIR:?}/"
1224 rsync -am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/"
e4c822e9
FS
1225 # Set effective & default ACLs for the build dir so unprivileged
1226 # processes can write gcda files with coverage stats
1227 setfacl -R -m 'd:o:rwX' -m 'o:rwX' "${initdir}/${BUILD_DIR:?}/"
c82dc15b 1228 fi
8fa03808
DS
1229}
1230
fdd380dd
FS
1231install_package_file() {
1232 local file="${1:?}"
1233
1234 # Skip missing files (like /etc/machine-info)
1235 [[ ! -e "$file" ]] && return 0
1236 # Skip python unit tests, since the image_install machinery will try to pull
1237 # in the whole python stack in a very questionable state, making the tests fail.
1238 # And given we're trying to transition to mkosi-based images anyway I'm not even
1239 # going to bother
1240 [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && return 0
1241 # If the current file is a directory, create it with the original
1242 # mode; if it's a symlink to a directory, copy it as-is
1243 if [[ -d "$file" ]]; then
1244 inst_dir "$file"
1245 else
1246 inst "$file"
1247 fi
1248}
1249
8fa03808 1250install_debian_systemd() {
1b8fcd9c 1251 dinfo "Install debian systemd"
8fa03808 1252
fdd380dd 1253 local deb file
96af59aa
FS
1254
1255 while read -r deb; do
8fa03808 1256 ddebug "Install debian files from package $deb"
fdd380dd
FS
1257 while read -r file; do
1258 install_package_file "$file"
1259 done < <(dpkg-query -L "$deb" 2>/dev/null)
96af59aa 1260 done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
8fa03808
DS
1261}
1262
8ddbd9e0
FS
1263install_rpm() {
1264 local rpm="${1:?}"
1265 local file
1266
1267 if ! rpm -q "$rpm" >/dev/null; then
1268 derror "RPM $rpm is not installed"
1269 return 1
1270 fi
1271
1272 dinfo "Installing contents of RPM $rpm"
1273 while read -r file; do
fdd380dd 1274 install_package_file "$file"
8ddbd9e0
FS
1275 done < <(rpm -ql "$rpm")
1276}
1277
abf06267 1278install_suse_systemd() {
abf06267
FB
1279 local pkgs
1280
08abfd0b
FB
1281 dinfo "Install basic filesystem structure"
1282 install_rpm filesystem
1283
abf06267
FB
1284 dinfo "Install SUSE systemd"
1285
1286 pkgs=(
1287 systemd
03b1e10f 1288 systemd-boot
abf06267
FB
1289 systemd-container
1290 systemd-coredump
1291 systemd-experimental
33ce0a89 1292 systemd-homed
abf06267 1293 systemd-journal-remote
3adac701
FB
1294 # Since commit fb6f25d7b979134a, systemd-resolved, which is shipped by
1295 # systemd-network sub-package on openSUSE, has its own testsuite.
1296 systemd-network
abf06267
FB
1297 systemd-portable
1298 udev
1299 )
1300
1301 for p in "${pkgs[@]}"; do
1302 rpm -q "$p" &>/dev/null || continue
1303
8ddbd9e0 1304 install_rpm "$p"
abf06267
FB
1305 done
1306
fa2745a3 1307 dinfo "Install the data needed by the tests at runtime"
4e8172c8 1308 inst_recursive "${SOURCE_DIR}/testdata"
372d40fb 1309 inst_recursive "${SOURCE_DIR}/unit-tests/manual"
4e8172c8
FB
1310
1311 # On openSUSE, this directory is not created at package install, at least
1312 # for now.
abf06267
FB
1313 mkdir -p "$initdir/var/log/journal/remote"
1314}
1315
8ddbd9e0
FS
1316install_fedora_systemd() {
1317 local required_packages=(
1318 systemd
1319 systemd-container
1320 systemd-libs
1321 systemd-pam
1322 systemd-tests
1323 systemd-udev
1324 )
1325 local optional_packages=(
1326 systemd-boot-unsigned
1327 systemd-bootchart
1328 systemd-journal-remote
1329 systemd-networkd
1330 systemd-oomd-defaults
1331 systemd-resolved
1332 )
1333 local package
1334
1335 for package in "${required_packages[@]}"; do
1336 install_rpm "$package"
1337 done
1338
1339 for package in "${optional_packages[@]}"; do
1340 rpm -q "$package" >/dev/null || continue
1341 install_rpm "$package"
1342 done
1343}
1344
8fa03808 1345install_distro_systemd() {
1b8fcd9c 1346 dinfo "Install distro systemd"
8fa03808 1347
23f8e019 1348 if get_bool "$LOOKS_LIKE_DEBIAN"; then
8fa03808 1349 install_debian_systemd
abf06267
FB
1350 elif get_bool "$LOOKS_LIKE_SUSE"; then
1351 install_suse_systemd
8ddbd9e0
FS
1352 elif get_bool "$LOOKS_LIKE_FEDORA"; then
1353 install_fedora_systemd
8fa03808
DS
1354 else
1355 dfatal "NO_BUILD not supported for this distro"
1356 exit 1
1357 fi
1358}
1359
1360install_systemd() {
1b8fcd9c 1361 dinfo "Install systemd"
23f8e019 1362 if get_bool "$NO_BUILD"; then
8fa03808
DS
1363 install_distro_systemd
1364 else
1365 install_compiled_systemd
1366 fi
1367
ceea144e 1368 # Remove unneeded documentation
02d7e730 1369 rm -fr "${initdir:?}"/usr/share/{man,doc}
61b480b6 1370
ceea144e
FS
1371 # Enable debug logging in PID1
1372 mkdir -p "$initdir/etc/systemd/system.conf.d/"
1373 echo -ne "[Manager]\nLogLevel=debug\n" >"$initdir/etc/systemd/system.conf.d/10-log-level.conf"
bf6ef6b6 1374 if [[ -n "$TEST_SYSTEMD_LOG_LEVEL" ]]; then
ceea144e 1375 echo DefaultEnvironment=SYSTEMD_LOG_LEVEL="$TEST_SYSTEMD_LOG_LEVEL" >>"$initdir/etc/systemd/system.conf.d/99-log-level.conf"
bf6ef6b6 1376 fi
7f048f0e
FS
1377 # Enable debug logging for user instances as well
1378 mkdir -p "$initdir/etc/systemd/user.conf.d/"
1379 echo -ne "[Manager]\nLogLevel=debug\n" >"$initdir/etc/systemd/user.conf.d/10-log-level.conf"
ceea144e
FS
1380 # Store coredumps in journal
1381 mkdir -p "$initdir/etc/systemd/coredump.conf.d/"
1382 echo -ne "[Coredump]\nStorage=journal\n" >"$initdir/etc/systemd/coredump.conf.d/10-storage-journal.conf"
064a5c14 1383 # Propagate SYSTEMD_UNIT_PATH to user systemd managers
ceea144e
FS
1384 mkdir -p "$initdir/etc/systemd/system/user@.service.d/"
1385 echo -ne "[Service]\nPassEnvironment=SYSTEMD_UNIT_PATH\n" >"$initdir/etc/systemd/system/user@.service.d/99-SYSTEMD_UNIT_PATH.conf"
02d7e730 1386
b6fc5240
FS
1387 # When built with gcov, disable ProtectSystem= and ProtectHome= in the test
1388 # images, since it prevents gcov to write the coverage reports (*.gcda
93c3b698 1389 # files)
02d7e730
FS
1390 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
1391 mkdir -p "$initdir/etc/systemd/system/service.d/"
ceea144e 1392 echo -ne "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/99-gcov-override.conf"
52db3601
FS
1393 # Similarly, set ReadWritePaths= to the $BUILD_DIR in the test image to make the coverage work with
1394 # units using DynamicUser=yes. Do this only for services with test- prefix and a couple of
1395 # known-to-use DynamicUser=yes services, as setting this system-wide has many undesirable
1396 # side-effects, as it creates its own namespace.
607be850 1397 for service in capsule@ test- systemd-journal-{gatewayd,upload}; do
52db3601
FS
1398 mkdir -p "$initdir/etc/systemd/system/$service.service.d/"
1399 echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/$service.service.d/99-gcov-rwpaths-override.conf"
1400 done
e660c590
FS
1401 # Ditto, but for the user daemon
1402 mkdir -p "$initdir/etc/systemd/user/test-.service.d/"
ceea144e 1403 echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/user/test-.service.d/99-gcov-rwpaths-override.conf"
3b2823a7
FS
1404 # Bind the $BUILD_DIR into nspawn containers that are executed using
1405 # machinectl. Unfortunately, the .nspawn files don't support drop-ins
1406 # so we have to inject the bind mount directly into
1407 # the systemd-nspawn@.service unit.
1408 cp "$initdir/usr/lib/systemd/system/systemd-nspawn@.service" "$initdir/etc/systemd/system/systemd-nspawn@.service"
1409 sed -ri "s/^ExecStart=.+$/& --bind=${BUILD_DIR//\//\\\/}/" "$initdir/etc/systemd/system/systemd-nspawn@.service"
786f6d81
FS
1410 # Pass the $BUILD_DIR as $COVERAGE_BUILD_DIR env variable to the system
1411 # manager, similarly to what we do with $ASAN_RT_PATH during sanitized
1412 # builds
1413 mkdir -p "$initdir/etc/systemd/system.conf.d/"
1414 echo -ne "[Manager]\nDefaultEnvironment=COVERAGE_BUILD_DIR=$BUILD_DIR\n" >"$initdir/etc/systemd/system.conf.d/99-COVERAGE_BUILD_DIR.conf"
02d7e730 1415 fi
6f73ef8b
FS
1416
1417 # If we're built with -Dportabled=false, tests with systemd-analyze
1418 # --profile will fail. Since we need just the profile (text) files, let's
1419 # copy them into the image if they don't exist there.
1420 local portable_dir="${initdir:?}${ROOTLIBDIR:?}/portable"
1421 if [[ ! -d "$portable_dir/profile/strict" ]]; then
1422 dinfo "Couldn't find portable profiles in the test image"
1423 dinfo "Copying them directly from the source tree"
1424 mkdir -p "$portable_dir"
1425 cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir"
1426 fi
889a9042
RC
1427}
1428
d7a4278d 1429get_ldpath() {
1b8fcd9c
FS
1430 local rpath
1431 rpath="$(objdump -p "${1:?}" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :)"
5bb4503d
LP
1432
1433 if [ -z "$rpath" ] ; then
1b8fcd9c 1434 echo "$BUILD_DIR"
5bb4503d 1435 else
1b8fcd9c 1436 echo "$rpath"
5bb4503d 1437 fi
d7a4278d
FS
1438}
1439
889a9042 1440install_missing_libraries() {
1b8fcd9c 1441 dinfo "Install missing libraries"
889a9042 1442 # install possible missing libraries
26c2b302 1443 for i in "${initdir:?}"{,/usr}/{sbin,bin}/* "$initdir"{,/usr}/lib/systemd/{,tests/unit-tests/{,manual/,unsafe/}}*; do
1b8fcd9c 1444 LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs "$i"
889a9042 1445 done
b7fca1b0 1446
5f347d31
FS
1447 # Install libgcc_s.so if available, since it's dlopen()ed by libpthread
1448 # and might cause unexpected failures during pthread_exit()/pthread_cancel()
1449 # if not present
1450 # See: https://github.com/systemd/systemd/pull/23858
1451 while read -r libgcc_s; do
1452 [[ -e "$libgcc_s" ]] && inst_library "$libgcc_s"
1453 done < <(ldconfig -p | awk '/\/libgcc_s.so.1$/ { print $4 }')
1454
1b8fcd9c 1455 local lib path
b7fca1b0
LB
1456 # A number of dependencies is now optional via dlopen, so the install
1457 # script will not pick them up, since it looks at linkage.
1d98716e 1458 for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
a9d34376 1459 ddebug "Searching for $lib via pkg-config"
1b8fcd9c
FS
1460 if pkg-config --exists "$lib"; then
1461 path="$(pkg-config --variable=libdir "$lib")"
a9d34376
LB
1462 if [ -z "${path}" ]; then
1463 ddebug "$lib.pc does not contain a libdir variable, skipping"
1464 continue
1465 fi
1466
1467 if ! [[ ${lib} =~ ^lib ]]; then
1468 lib="lib${lib}"
1469 fi
da035a3a
LB
1470 # p11-kit-1's .so doesn't have the API level in the name
1471 if [[ ${lib} =~ p11-kit-1$ ]]; then
1472 lib="libp11-kit"
1473 fi
a9d34376
LB
1474 # Some pkg-config files are broken and give out the wrong paths
1475 # (eg: libcryptsetup), so just ignore them
1476 inst_libs "${path}/${lib}.so" || true
1477 inst_library "${path}/${lib}.so" || true
1136175c
YW
1478
1479 if [[ "$lib" == "libxkbcommon" ]]; then
1480 install_x11_keymaps full
1481 fi
a9d34376
LB
1482 else
1483 ddebug "$lib.pc not found, skipping"
1484 continue
1485 fi
b7fca1b0 1486 done
3c5f7ec4
DDM
1487
1488 # Install extra openssl 3 stuff
1489 path="$(pkg-config --variable=libdir libcrypto)"
1490 inst_simple "${path}/ossl-modules/legacy.so" || true
1491 inst_simple "${path}/ossl-modules/fips.so" || true
1492 inst_simple "${path}/engines-3/afalg.so" || true
1493 inst_simple "${path}/engines-3/capi.so" || true
1494 inst_simple "${path}/engines-3/loader_attic.so" || true
1495 inst_simple "${path}/engines-3/padlock.so" || true
bf3598be
DDM
1496
1497 # Binaries from mtools depend on the gconv modules to translate between codepages. Because there's no
1498 # pkg-config file for these, we copy every gconv/ directory we can find in /usr/lib and /usr/lib64.
1499 # shellcheck disable=SC2046
1500 inst_recursive $(find /usr/lib* -name gconv 2>/dev/null)
889a9042
RC
1501}
1502
1506edca 1503cleanup_loopdev() {
1b8fcd9c 1504 if [ -n "${LOOPDEV:=}" ]; then
1506edca
ZJS
1505 ddebug "losetup -d $LOOPDEV"
1506 losetup -d "${LOOPDEV}"
2991fa41 1507 unset LOOPDEV
1506edca
ZJS
1508 fi
1509}
1510
b92c3df2 1511add_at_exit_handler cleanup_loopdev
1506edca 1512
889a9042 1513create_empty_image() {
00c26769 1514 if [[ -z "${IMAGE_NAME:=}" ]]; then
8c3534b5
ZJS
1515 echo "create_empty_image: \$IMAGE_NAME not set"
1516 exit 1
1517 fi
1518
98b27937 1519 # Partition sizes are in MiBs
82929336
FS
1520 local root_size=768
1521 local data_size=100
97bbb9cf
YW
1522 local esp_size=128
1523 local boot_size=128
1524 local total=
23f8e019 1525 if ! get_bool "$NO_BUILD"; then
63878c52 1526 if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
00c26769 1527 root_size=$((root_size + 200))
63878c52
LB
1528 fi
1529 if meson configure "${BUILD_DIR:?}" | grep 'link-.*-shared' | awk '{ print $2 }' | grep -q 'false'; then
00c26769 1530 root_size=$((root_size + 200))
63878c52 1531 fi
02d7e730 1532 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
00c26769 1533 root_size=$((root_size + 250))
c82dc15b 1534 fi
82929336
FS
1535 if get_bool "$IS_BUILT_WITH_ASAN"; then
1536 root_size=$((root_size * 2))
1537 fi
853401a6 1538 fi
82929336 1539
0334afe4
FS
1540 if [[ "${IMAGE_ADDITIONAL_ROOT_SIZE:-0}" -gt 0 ]]; then
1541 root_size=$((root_size + IMAGE_ADDITIONAL_ROOT_SIZE))
1542 fi
1543 if [[ "${IMAGE_ADDITIONAL_DATA_SIZE:-0}" -gt 0 ]]; then
1544 data_size=$((data_size + IMAGE_ADDITIONAL_DATA_SIZE))
14697c41 1545 fi
80c53fe7 1546
97bbb9cf
YW
1547 total=$((root_size + data_size + esp_size + boot_size))
1548
1549 echo "Setting up ${IMAGE_PUBLIC:?} (${total} MB)"
1b8fcd9c 1550 rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
ec43f686 1551
889a9042 1552 # Create the blank file to use as a root filesystem
97bbb9cf 1553 truncate -s "${total}M" "$IMAGE_PUBLIC"
ec43f686 1554
00c26769
FS
1555 LOOPDEV="$(losetup --show -P -f "$IMAGE_PUBLIC")"
1556 [[ -b "$LOOPDEV" ]] || return 1
98b27937 1557 # Create two partitions - a root one and a data one (utilized by some tests)
edbced8a 1558 sfdisk "$LOOPDEV" <<EOF
98b27937 1559label: gpt
97bbb9cf 1560type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=${esp_size}M
a0b50e4d 1561type=$("${SYSTEMD_ID128:?}" show root -Pu) name=root size=${root_size}M bootable
97bbb9cf 1562type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M
98b27937 1563type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
889a9042
RC
1564EOF
1565
053edc5b
EV
1566 udevadm settle
1567
97bbb9cf
YW
1568 if ! mkfs -t vfat "${LOOPDEV}p1"; then
1569 dfatal "Failed to mkfs -t vfat ${LOOPDEV}p1"
1570 exit 1
1571 fi
1572
bac05644 1573 local label=(-L systemd_boot)
331fb4ca 1574 # mkfs.reiserfs doesn't know -L. so, use --label instead
bac05644 1575 [[ "$FSTYPE" == "reiserfs" ]] && label=(--label systemd_boot)
97bbb9cf
YW
1576 if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p2" -q; then
1577 dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p2 -q"
1578 exit 1
1579 fi
1580
1581 local label=(-L xbootldr)
1582 [[ "$FSTYPE" == "reiserfs" ]] && label=(--label xbootldr)
1583 if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p3" -q; then
1584 dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p3 -q"
331fb4ca
EV
1585 exit 1
1586 fi
889a9042
RC
1587}
1588
8c3534b5 1589mount_initdir() {
1b8fcd9c
FS
1590 if [ -z "${LOOPDEV:=}" ]; then
1591 [ -e "${IMAGE_PRIVATE:?}" ] && image="$IMAGE_PRIVATE" || image="${IMAGE_PUBLIC:?}"
1592 LOOPDEV="$(losetup --show -P -f "$image")"
8c3534b5 1593 [ -b "$LOOPDEV" ] || return 1
8c3534b5 1594
1506edca
ZJS
1595 udevadm settle
1596 fi
ec4cab49 1597
1b8fcd9c
FS
1598 if ! mountpoint -q "${initdir:?}"; then
1599 mkdir -p "$initdir"
97bbb9cf 1600 mount "${LOOPDEV}p2" "$initdir"
1506edca 1601 TEST_SETUP_CLEANUP_ROOTDIR=1
8c3534b5 1602 fi
8c3534b5
ZJS
1603}
1604
ec43f686
ZJS
1605cleanup_initdir() {
1606 # only umount if create_empty_image_rootdir() was called to mount it
9caab7b5
FS
1607 if get_bool "$TEST_SETUP_CLEANUP_ROOTDIR"; then
1608 _umount_dir "${initdir:?}"
1609 fi
ec43f686
ZJS
1610}
1611
1612umount_loopback() {
1613 # unmount the loopback device from all places. Otherwise we risk file
1614 # system corruption.
1b8fcd9c 1615 for device in $(losetup -l | awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do
ec43f686
ZJS
1616 ddebug "Unmounting all uses of $device"
1617 mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
1618 done
1619}
1620
8c3534b5
ZJS
1621create_empty_image_rootdir() {
1622 create_empty_image
1623 mount_initdir
1624}
1625
0d6e61d6
EV
1626check_asan_reports() {
1627 local ret=0
1b8fcd9c 1628 local root="${1:?}"
88c98cb2 1629 local log report
0d6e61d6 1630
23f8e019 1631 if get_bool "$IS_BUILT_WITH_ASAN"; then
0d6e61d6
EV
1632 ls -l "$root"
1633 if [[ -e "$root/systemd.asan.log.1" ]]; then
1634 cat "$root/systemd.asan.log.1"
1b8fcd9c 1635 ret=$((ret+1))
7e11a95e 1636 fi
998445fd 1637
88c98cb2
FS
1638 for log in pid1 journald; do
1639 report="$(find "$root" -name "systemd-$log.*san.log*" -exec cat {} \;)"
1640 if [[ -n "$report" ]]; then
1641 printf "%s\n" "$report"
1642 # shellcheck disable=SC2015
1643 [[ "$log" == journald ]] && cat "$root/systemd-journald.out" || :
1644 ret=$((ret+1))
1645 fi
1646 done
ed4f303f 1647
71116990 1648 # May 08 13:23:31 H testleak[2907148]: SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
1b8fcd9c 1649 pids="$(
71116990
ZJS
1650 "$JOURNALCTL" -D "$root/var/log/journal" --grep 'SUMMARY: .*Sanitizer:' |
1651 grep -v -E 'dbus-daemon|dbus-broker-launch' |
1652 sed -r -n 's/.* .+\[([0-9]+)\]: SUMMARY:.*/\1/p'
1b8fcd9c 1653 )"
71116990 1654
1b8fcd9c
FS
1655 if [[ -n "$pids" ]]; then
1656 ret=$((ret+1))
ed4f303f 1657 for pid in $pids; do
1b8fcd9c 1658 "$JOURNALCTL" -D "$root/var/log/journal" _PID="$pid" --no-pager
ed4f303f 1659 done
d56db495 1660 fi
7e11a95e
EV
1661 fi
1662
889a9042
RC
1663 return $ret
1664}
1665
c82dc15b
LB
1666check_coverage_reports() {
1667 local root="${1:?}"
1668
1669 if get_bool "$NO_BUILD"; then
1670 return 0
1671 fi
02d7e730 1672 if ! get_bool "$IS_BUILT_WITH_COVERAGE"; then
c82dc15b
LB
1673 return 0
1674 fi
1675
1676 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1677 dest="${ARTIFACT_DIRECTORY}/${testname:?}.coverage-info"
1678 else
1679 dest="${TESTDIR:?}/coverage-info"
1680 fi
1681
7fdd6e15
FS
1682 if [[ ! -e "${TESTDIR:?}/coverage-base" ]]; then
1683 # This shouldn't happen, as the report is generated during the setup
1684 # phase (test_setup()).
1685 derror "Missing base coverage report"
1686 return 1
1687 fi
1688
daeb95a1
FS
1689 # Create a coverage report that will later be uploaded. Remove info about system
1690 # libraries/headers and generated files, as we don't really care about them.
9fe18055 1691 lcov --directory "${root}/${BUILD_DIR:?}" --capture --exclude "*.gperf" --output-file "${dest}.new"
35382a9d
FS
1692 if [[ -f "$dest" ]]; then
1693 # If the destination report file already exists, don't overwrite it, but
7fdd6e15
FS
1694 # merge it with the already present one - this usually happens when
1695 # running both "parts" of a test in one run (the qemu and the nspawn part).
35382a9d 1696 lcov --add-tracefile "${dest}" --add-tracefile "${dest}.new" -o "${dest}"
35382a9d 1697 else
7fdd6e15
FS
1698 # If there's no prior coverage report, merge the new one with the base
1699 # report we did during the setup phase (see test_setup()).
1700 lcov --add-tracefile "${TESTDIR:?}/coverage-base" --add-tracefile "${dest}.new" -o "${dest}"
35382a9d 1701 fi
daeb95a1 1702 lcov --remove "$dest" -o "$dest" '/usr/include/*' '/usr/lib/*' "${BUILD_DIR:?}/*"
7fdd6e15 1703 rm -f "${dest}.new"
c82dc15b 1704
d282e57e
FS
1705 # If the test logs contain lines like:
1706 #
1707 # ...systemd-resolved[735885]: profiling:/systemd-meson-build/src/shared/libsystemd-shared-250.a.p/base-filesystem.c.gcda:Cannot open
1708 #
1709 # it means we're possibly missing some coverage since gcov can't write the stats,
1710 # usually due to the sandbox being too restrictive (e.g. ProtectSystem=yes,
1711 # ProtectHome=yes) or the $BUILD_DIR being inaccessible to non-root users - see
1712 # `setfacl` stuff in install_compiled_systemd().
7fdd6e15
FS
1713 #
1714 # Also, a note: some tests, like TEST-46, overmount /home with tmpfs, which
1715 # means if your build dir is under /home/your-user (which is usually the
94d82b59 1716 # case) you might get bogus errors and missing coverage.
1b2e3b8b
FS
1717 if ! get_bool "${IGNORE_MISSING_COVERAGE:=}" && \
1718 "${JOURNALCTL:?}" -q --no-pager -D "${root:?}/var/log/journal" --grep "profiling:.+?gcda:[Cc]annot open"; then
d282e57e
FS
1719 derror "Detected possibly missing coverage, check the journal"
1720 return 1
1721 fi
1722
c82dc15b
LB
1723 return 0
1724}
1725
8943daf8 1726save_journal() {
09bdb9f1
FS
1727 local source_dir="${1:?}"
1728 local state="${2:?}"
d3b8e384
DS
1729 # Default to always saving journal
1730 local save="yes"
09bdb9f1 1731 local dest_dir dest_name dest
d3b8e384 1732
09bdb9f1 1733 if [[ "${TEST_SAVE_JOURNAL:-}" == "no" ]]; then
d3b8e384 1734 save="no"
09bdb9f1 1735 elif [[ "${TEST_SAVE_JOURNAL:-}" == "fail" && "$state" -eq 0 ]]; then
d3b8e384
DS
1736 save="no"
1737 fi
1738
09bdb9f1
FS
1739 if [[ -n "${ARTIFACT_DIRECTORY:-}" ]]; then
1740 dest_dir="$ARTIFACT_DIRECTORY"
1741 dest_name="${testname:?}.journal"
f9eb2d51 1742 else
09bdb9f1
FS
1743 dest_dir="${TESTDIR:?}"
1744 dest_name="system.journal"
f9eb2d51 1745 fi
8943daf8 1746
7a321b5a 1747 # Show messages from the TEST-XX-XXX.service or messages with priority "warning" and higher
fa6f37c0 1748 echo " --- $source_dir ---"
bce0fa7d 1749 "$JOURNALCTL" --all --no-pager --no-hostname -o short-monotonic -D "$source_dir" \
7a321b5a 1750 _SYSTEMD_UNIT="${TESTNAME:?}.service" + SYSLOG_IDENTIFIER="$TESTNAME.sh" + \
77baca26 1751 PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
f1416431 1752
09bdb9f1
FS
1753 if get_bool "$save"; then
1754 # If we don't have systemd-journal-remote copy all journals from /var/log/journal/
1755 # to $dest_dir/journals/ as is, otherwise merge all journals into a single .journal
1756 # file
1757 if [[ -z "${SYSTEMD_JOURNAL_REMOTE:-}" ]]; then
1758 dest="$dest_dir/journals"
1759 mkdir -p "$dest"
1760 cp -a "$source_dir/*" "$dest/"
1761 else
1762 dest="$dest_dir/$dest_name"
1763 "$SYSTEMD_JOURNAL_REMOTE" -o "$dest" --getter="$JOURNALCTL -o export -D $source_dir"
f1416431
ZJS
1764 fi
1765
09bdb9f1
FS
1766 if [[ -n "${SUDO_USER:-}" ]]; then
1767 setfacl -R -m "user:$SUDO_USER:r-X" "$dest"
1768 fi
d3b8e384 1769
09bdb9f1
FS
1770 # we want to print this sometime later, so save this in a variable
1771 JOURNAL_LIST="$(ls -lR "$dest")"
a83a7d1e
YW
1772 fi
1773
09bdb9f1 1774 rm -rf "${source_dir:?}"/*
8943daf8
ZJS
1775}
1776
7bf20e48 1777check_result_common() {
1b8fcd9c 1778 local workspace="${1:?}"
7bf20e48
ZJS
1779 local ret
1780
1781 if [ -s "$workspace/failed" ]; then
c0d44092 1782 # Non-empty …/failed has highest priority
7bf20e48 1783 cp -a "$workspace/failed" "${TESTDIR:?}/"
a83a7d1e
YW
1784 if [ -n "${SUDO_USER}" ]; then
1785 setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed
1786 fi
7bf20e48 1787 ret=1
faca95e1
FS
1788 elif get_bool "$TIMED_OUT"; then
1789 echo "(timeout)" >"${TESTDIR:?}/failed"
1790 ret=2
7bf20e48
ZJS
1791 elif [ -e "$workspace/testok" ]; then
1792 # …/testok always counts (but with lower priority than …/failed)
1793 ret=0
1794 elif [ -e "$workspace/skipped" ]; then
1795 # …/skipped always counts (a message is expected)
1796 echo "${TESTNAME:?} was skipped:"
1797 cat "$workspace/skipped"
1798 ret=0
7bf20e48 1799 else
c0d44092
ZJS
1800 echo "(failed; see logs)" >"${TESTDIR:?}/failed"
1801 ret=3
7bf20e48
ZJS
1802 fi
1803
c0d44092 1804 check_asan_reports "$workspace" || ret=4
7bf20e48 1805
c82dc15b
LB
1806 check_coverage_reports "$workspace" || ret=5
1807
d3b8e384
DS
1808 save_journal "$workspace/var/log/journal" $ret
1809
1b8fcd9c
FS
1810 if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out" ]; then
1811 cp "$workspace/strace.out" "${ARTIFACT_DIRECTORY}/"
1812 fi
7bf20e48 1813
c0d44092
ZJS
1814 if [ ${ret:?} != 0 ] && [ -f "$TESTDIR/failed" ]; then
1815 echo -n "${TESTNAME:?}: "
1816 cat "$TESTDIR/failed"
1817 fi
7bf20e48
ZJS
1818 echo "${JOURNAL_LIST:-"No journals were saved"}"
1819
c0d44092 1820 return ${ret:?}
7bf20e48
ZJS
1821}
1822
1823check_result_nspawn() {
1824 local workspace="${1:?}"
cb16b72e 1825 local ret=0
7bf20e48 1826
35d2d2e6 1827 # Run a test-specific checks if defined by check_result_nspawn_hook()
6695c41c 1828 if declare -F check_result_nspawn_hook >/dev/null; then
4b9a0c3a 1829 if ! check_result_nspawn_hook "${workspace}"; then
6695c41c
FS
1830 derror "check_result_nspawn_hook() returned with EC > 0"
1831 ret=4
1832 fi
1833 fi
1834
cb16b72e 1835 check_result_common "${workspace}" || ret=$?
35d2d2e6 1836
1b8fcd9c 1837 _umount_dir "${initdir:?}"
7bf20e48 1838
0d6e61d6
EV
1839 return $ret
1840}
1841
054ee249
MP
1842# can be overridden in specific test
1843check_result_qemu() {
cb16b72e 1844 local ret=0
8c3534b5 1845 mount_initdir
7bf20e48 1846
35d2d2e6 1847 # Run a test-specific checks if defined by check_result_qemu_hook()
6695c41c 1848 if declare -F check_result_qemu_hook >/dev/null; then
4b9a0c3a 1849 if ! check_result_qemu_hook "${initdir:?}"; then
6695c41c
FS
1850 derror "check_result_qemu_hook() returned with EC > 0"
1851 ret=4
1852 fi
1853 fi
1854
cb16b72e 1855 check_result_common "${initdir:?}" || ret=$?
35d2d2e6 1856
4b9a0c3a
FS
1857 _umount_dir "${initdir:?}"
1858
054ee249
MP
1859 return $ret
1860}
1861
fa1fdd30
LB
1862check_result_nspawn_unittests() {
1863 local workspace="${1:?}"
1864 local ret=1
1865
1866 [[ -e "$workspace/testok" ]] && ret=0
1867
1868 if [[ -s "$workspace/failed" ]]; then
1869 ret=$((ret + 1))
1870 echo "=== Failed test log ==="
1871 cat "$workspace/failed"
1872 else
1873 if [[ -s "$workspace/skipped" ]]; then
1874 echo "=== Skipped test log =="
1875 cat "$workspace/skipped"
1876 # We might have only skipped tests - that should not fail the job
1877 ret=0
1878 fi
1879 if [[ -s "$workspace/testok" ]]; then
1880 echo "=== Passed tests ==="
1881 cat "$workspace/testok"
1882 fi
1883 fi
1884
23f8e019 1885 get_bool "${TIMED_OUT:=}" && ret=1
0b5fe54f 1886 check_coverage_reports "$workspace" || ret=5
d3b8e384
DS
1887
1888 save_journal "$workspace/var/log/journal" $ret
fa316d55 1889 echo "${JOURNAL_LIST:-"No journals were saved"}"
d3b8e384 1890
fa1fdd30
LB
1891 _umount_dir "${initdir:?}"
1892
fa1fdd30
LB
1893 return $ret
1894}
1895
1896check_result_qemu_unittests() {
1897 local ret=1
1898
1899 mount_initdir
1900 [[ -e "${initdir:?}/testok" ]] && ret=0
1901
1902 if [[ -s "$initdir/failed" ]]; then
1903 ret=$((ret + 1))
1904 echo "=== Failed test log ==="
1905 cat "$initdir/failed"
1906 else
1907 if [[ -s "$initdir/skipped" ]]; then
1908 echo "=== Skipped test log =="
1909 cat "$initdir/skipped"
1910 # We might have only skipped tests - that should not fail the job
1911 ret=0
1912 fi
1913 if [[ -s "$initdir/testok" ]]; then
1914 echo "=== Passed tests ==="
1915 cat "$initdir/testok"
1916 fi
1917 fi
1918
23f8e019 1919 get_bool "${TIMED_OUT:=}" && ret=1
0b5fe54f 1920 check_coverage_reports "$initdir" || ret=5
d3b8e384
DS
1921
1922 save_journal "$initdir/var/log/journal" $ret
fa316d55 1923 echo "${JOURNAL_LIST:-"No journals were saved"}"
d3b8e384 1924
fa1fdd30
LB
1925 _umount_dir "$initdir"
1926
fa1fdd30
LB
1927 return $ret
1928}
1929
889a9042 1930create_rc_local() {
1b8fcd9c
FS
1931 dinfo "Create rc.local"
1932 mkdir -p "${initdir:?}/etc/rc.d"
1933 cat >"$initdir/etc/rc.d/rc.local" <<EOF
ff12a795 1934#!/usr/bin/env bash
889a9042
RC
1935exit 0
1936EOF
1b8fcd9c 1937 chmod 0755 "$initdir/etc/rc.d/rc.local"
889a9042
RC
1938}
1939
1940install_execs() {
1b8fcd9c
FS
1941 ddebug "Install executables from the service files"
1942
1943 local pkg_config_path="${BUILD_DIR:?}/src/core/"
1944 local systemunitdir userunitdir exe
1945 systemunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemdsystemunitdir systemd)"
1946 userunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemduserunitdir systemd)"
1947 while read -r exe; do
1948 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
1949 # also, plymouth is pulled in by rescue.service, but even there the exit code
1950 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
1951 dinfo "Attempting to install $exe (based on unit file reference)"
1952 inst "$exe" || [ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth" ]
1953 done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service | sort -u)
889a9042
RC
1954}
1955
1956generate_module_dependencies() {
1b8fcd9c
FS
1957 dinfo "Generate modules dependencies"
1958 if [[ -d "${initdir:?}/lib/modules/${KERNEL_VER:?}" ]] && \
1959 ! depmod -a -b "$initdir" "$KERNEL_VER"; then
889a9042
RC
1960 dfatal "\"depmod -a $KERNEL_VER\" failed."
1961 exit 1
1962 fi
1963}
1964
1965install_depmod_files() {
1b8fcd9c
FS
1966 dinfo "Install depmod files"
1967 inst "/lib/modules/${KERNEL_VER:?}/modules.order"
1968 inst "/lib/modules/$KERNEL_VER/modules.builtin"
889a9042
RC
1969}
1970
1971install_plymouth() {
1b8fcd9c 1972 dinfo "Install plymouth"
889a9042
RC
1973 # install plymouth, if found... else remove plymouth service files
1974 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
1975 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
1976 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
39e17536 1977 # image_install plymouth plymouthd
889a9042 1978 # else
1b8fcd9c 1979 rm -f "${initdir:?}"/{usr/lib,lib,etc}/systemd/system/plymouth* "$initdir"/{usr/lib,lib,etc}/systemd/system/*/plymouth*
889a9042
RC
1980 # fi
1981}
1982
d93857ae 1983install_haveged() {
ce380c2f 1984 # If haveged is installed, it's probably included in initrd and needs to be
d93857ae
FB
1985 # installed in the image too.
1986 if [ -x /usr/sbin/haveged ]; then
1987 dinfo "Install haveged files"
1988 inst /usr/sbin/haveged
ce380c2f 1989 for u in /usr/lib/systemd/system/haveged*; do
1c3f490f 1990 inst "$u"
ce380c2f 1991 done
d93857ae
FB
1992 fi
1993}
1994
889a9042 1995install_ld_so_conf() {
1b8fcd9c
FS
1996 dinfo "Install /etc/ld.so.conf*"
1997 cp -a /etc/ld.so.conf* "${initdir:?}/etc"
889a9042
RC
1998 ldconfig -r "$initdir"
1999}
2000
d0ac89a1 2001install_testuser() {
1b8fcd9c 2002 dinfo "Set up a test user"
d0ac89a1 2003 # create unprivileged user for user manager tests
1b8fcd9c
FS
2004 mkdir -p "${initdir:?}/etc/sysusers.d"
2005 cat >"$initdir/etc/sysusers.d/testuser.conf" <<EOF
8cc42169 2006u testuser 4711 "Test User" /home/testuser /bin/bash
d0ac89a1
ZJS
2007EOF
2008
1b8fcd9c
FS
2009 mkdir -p "$initdir/home/testuser"
2010 chmod 0700 "$initdir/home/testuser"
2011 chown 4711:4711 "$initdir/home/testuser"
d0ac89a1
ZJS
2012}
2013
889a9042 2014install_config_files() {
1b8fcd9c 2015 dinfo "Install config files"
65dd488f 2016 inst /etc/sysconfig/init || :
889a9042
RC
2017 inst /etc/passwd
2018 inst /etc/shadow
2aa5a13a 2019 inst_any /etc/login.defs /usr/etc/login.defs
889a9042
RC
2020 inst /etc/group
2021 inst /etc/shells
9e173292 2022 inst_any /etc/nsswitch.conf /usr/etc/nsswitch.conf
65dd488f 2023 inst /etc/pam.conf || :
ae6c5987 2024 inst_any /etc/os-release /usr/lib/os-release
889a9042
RC
2025 inst /etc/localtime
2026 # we want an empty environment
1b8fcd9c
FS
2027 : >"${initdir:?}/etc/environment"
2028 : >"$initdir/etc/machine-id"
2029 : >"$initdir/etc/resolv.conf"
7eeeab20 2030
889a9042 2031 # set the hostname
ea0d33e2 2032 echo 'H' >"$initdir/etc/hostname"
a455e75a
ZJS
2033
2034 # let's set up just one image with the traditional verbose output
1b8fcd9c
FS
2035 if [ "${IMAGE_NAME:?}" != "basic" ]; then
2036 mkdir -p "$initdir/etc/systemd/system.conf.d"
2037 echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc/systemd/system.conf.d/status.conf"
a455e75a 2038 fi
889a9042
RC
2039}
2040
2041install_basic_tools() {
1b8fcd9c 2042 dinfo "Install basic tools"
39e17536
FS
2043 image_install "${BASICTOOLS[@]}"
2044 image_install -o sushell
7d023341 2045 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
39e17536 2046 image_install -o ldconfig.real
889a9042
RC
2047}
2048
2049install_debug_tools() {
1b8fcd9c 2050 dinfo "Install debug tools"
f7d47cc8 2051 image_install -o "${DEBUGTOOLS[@]}"
c81a46b9 2052
23f8e019 2053 if get_bool "$INTERACTIVE_DEBUG"; then
c81a46b9 2054 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
1b8fcd9c
FS
2055 local getty_override="${initdir:?}/etc/systemd/system/serial-getty@.service.d"
2056 mkdir -p "$getty_override"
2057 echo -e "[Service]\nEnvironment=TERM=linux" >"$getty_override/default-TERM.conf"
17082e8a 2058 echo 'export TERM=linux' >>"$initdir/etc/profile"
c81a46b9 2059
17082e8a
FS
2060 if command -v resize >/dev/null; then
2061 image_install resize
2062 echo "resize" >>"$initdir/etc/profile"
2063 fi
5c08efee
FS
2064
2065 # Sometimes we might end up with plymouthd still running (especially
2066 # with the initrd -> asan_wrapper -> systemd transition), which will eat
2067 # our inputs and make debugging via tty impossible. Let's fix this by
2068 # killing plymouthd explicitly for the interactive sessions.
2069 # Note: we can't use pkill/pidof/etc. here due to a bug in libasan, see:
2070 # - https://github.com/llvm/llvm-project/issues/49223
2071 # - https://bugzilla.redhat.com/show_bug.cgi?id=2098125
2072 local plymouth_unit="${initdir:?}/etc/systemd/system/kill-plymouth.service"
2073 cat >"$plymouth_unit" <<EOF
2074[Unit]
2075After=multi-user.target
2076
2077[Service]
2078ExecStart=sh -c 'killall --verbose plymouthd || :'
2079
2080[Install]
2081WantedBy=multi-user.target
2082EOF
2083 "${SYSTEMCTL:?}" enable --root "${initdir:?}" kill-plymouth.service
c81a46b9 2084 fi
889a9042
RC
2085}
2086
2087install_libnss() {
1b8fcd9c 2088 dinfo "Install libnss"
889a9042 2089 # install libnss_files for login
0f194705
FS
2090 local NSS_LIBS
2091 mapfile -t NSS_LIBS < <(LD_DEBUG=files getent passwd 2>&1 >/dev/null | sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
90782fde
FS
2092 if [[ ${#NSS_LIBS[@]} -gt 0 ]]; then
2093 image_install "${NSS_LIBS[@]}"
2094 fi
889a9042
RC
2095}
2096
2097install_dbus() {
1b8fcd9c
FS
2098 dinfo "Install dbus"
2099 inst "${ROOTLIBDIR:?}/system/dbus.socket"
a978c9f2 2100
a49ad4c4 2101 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1b8fcd9c
FS
2102 if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then
2103 inst "$ROOTLIBDIR/system/dbus-broker.service"
908665f4
LP
2104 inst /usr/bin/dbus-broker
2105 inst /usr/bin/dbus-broker-launch
ec6c7bac 2106 image_install -o {/etc,/usr/lib}/systemd/system/dbus.service
1b8fcd9c 2107 elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then
908665f4 2108 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1b8fcd9c 2109 inst "$ROOTLIBDIR/system/dbus-daemon.service"
a978c9f2 2110 # Alias symlink
ec6c7bac 2111 image_install -o {/etc,/usr/lib}/systemd/system/dbus.service
a978c9f2 2112 else
1b8fcd9c 2113 inst "$ROOTLIBDIR/system/dbus.service"
a978c9f2 2114 fi
889a9042 2115
96af59aa
FS
2116 while read -r file; do
2117 inst "$file"
2118 done < <(find /etc/dbus-1 /usr/share/dbus-1 -xtype f 2>/dev/null)
bdfd515a
ZJS
2119
2120 # setup policy for Type=dbus test
1b8fcd9c
FS
2121 mkdir -p "${initdir:?}/etc/dbus-1/system.d"
2122 cat >"$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf" <<EOF
bdfd515a
ZJS
2123<?xml version="1.0"?>
2124<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
41d6f3bf 2125 "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
bdfd515a
ZJS
2126<busconfig>
2127 <policy user="root">
2128 <allow own="systemd.test.ExecStopPost"/>
2129 </policy>
2130</busconfig>
2131EOF
c78d1821
FS
2132
2133 # If we run without KVM, bump the service start timeout
2134 if ! get_bool "$QEMU_KVM"; then
2135 cat >"$initdir/etc/dbus-1/system.d/service.timeout.conf" <<EOF
2136<?xml version="1.0"?>
2137<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
2138 "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
2139<busconfig>
72f6d0e5 2140 <limit name="service_start_timeout">120000</limit>
c78d1821
FS
2141</busconfig>
2142EOF
e0cbb739
FS
2143 # Bump the client-side timeout in sd-bus as well
2144 mkdir -p "$initdir/etc/systemd/system.conf.d"
72f6d0e5 2145 echo -e '[Manager]\nDefaultEnvironment=SYSTEMD_BUS_TIMEOUT=120' >"$initdir/etc/systemd/system.conf.d/bus-timeout.conf"
c78d1821 2146 fi
889a9042
RC
2147}
2148
a49ad4c4 2149install_user_dbus() {
1b8fcd9c 2150 dinfo "Install user dbus"
53a1c944 2151 local userunitdir
1b8fcd9c
FS
2152 if ! userunitdir="$(pkg-config --variable=systemduserunitdir systemd)"; then
2153 dwarn "WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user"
2154 userunitdir=/usr/lib/systemd/user
53a1c944
LB
2155 fi
2156
1b8fcd9c
FS
2157 inst "$userunitdir/dbus.socket"
2158 inst_symlink "$userunitdir/sockets.target.wants/dbus.socket" || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket
a49ad4c4
FB
2159
2160 # Append the After= dependency on dbus in case it isn't already set up
1b8fcd9c
FS
2161 mkdir -p "${initdir:?}/etc/systemd/system/user@.service.d/"
2162 cat >"$initdir/etc/systemd/system/user@.service.d/dbus.conf" <<EOF
a49ad4c4
FB
2163[Unit]
2164After=dbus.service
2165EOF
2166
2167 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1b8fcd9c
FS
2168 if [ -f "$userunitdir/dbus-broker.service" ]; then
2169 inst "$userunitdir/dbus-broker.service"
ec6c7bac 2170 image_install -o {/etc,/usr/lib}/systemd/user/dbus.service
1b8fcd9c 2171 elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then
a49ad4c4 2172 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1b8fcd9c 2173 inst "$userunitdir/dbus-daemon.service"
a49ad4c4 2174 # Alias symlink
ec6c7bac 2175 image_install -o {/etc,/usr/lib}/systemd/user/dbus.service
a49ad4c4 2176 else
1b8fcd9c 2177 inst "$userunitdir/dbus.service"
a49ad4c4
FB
2178 fi
2179}
2180
889a9042 2181install_pam() {
96af59aa
FS
2182 dinfo "Install PAM"
2183 local paths=()
2184
23f8e019 2185 if get_bool "$LOOKS_LIKE_DEBIAN" && type -p dpkg-architecture &>/dev/null; then
96af59aa 2186 paths+=("/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security")
818567fc 2187 else
96af59aa 2188 paths+=(/lib*/security)
818567fc 2189 fi
96af59aa 2190
eef72224 2191 for d in /etc/pam.d /{usr/,}etc/security /usr/{etc,lib}/pam.d; do
96af59aa 2192 [ -d "$d" ] && paths+=("$d")
889a9042 2193 done
417491f1 2194
96af59aa
FS
2195 while read -r file; do
2196 inst "$file"
2197 done < <(find "${paths[@]}" -xtype f)
2198
d5172c79
EV
2199 # pam_unix depends on unix_chkpwd.
2200 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
39e17536 2201 image_install -o unix_chkpwd
d5172c79 2202
e14b866b 2203 # set empty root password for easy debugging
1b8fcd9c 2204 sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
138f7619
FB
2205
2206 # And make sure pam_unix will accept it by making sure that
2207 # the PAM module has the nullok option.
2208 for d in /etc/pam.d /usr/{etc,lib}/pam.d; do
2209 [ -d "$initdir/$d" ] || continue
2210 sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
2211 done
889a9042
RC
2212}
2213
4ce68ea9
YW
2214install_locales() {
2215 # install only C.UTF-8 and English locales
2216 dinfo "Install locales"
2217
2218 if command -v meson >/dev/null \
2219 && (meson configure "${BUILD_DIR:?}" | grep 'localegen-path */') \
2220 || get_bool "$LOOKS_LIKE_DEBIAN"; then
2221 # locale-gen support
2222 image_install -o locale-gen localedef
2223 inst /etc/locale.gen || :
2224 inst /usr/share/i18n/SUPPORTED || :
2225 inst_recursive /usr/share/i18n/charmaps
2226 inst_recursive /usr/share/i18n/locales
0c416ea0
FS
2227 inst_recursive /usr/share/locale/en*
2228 inst_recursive /usr/share/locale/de*
2229 image_install /usr/share/locale/locale.alias
2230 # locale-gen might either generate each locale separately or merge them
2231 # into a single archive
2232 if ! (inst_recursive /usr/lib/locale/C.*8 /usr/lib/locale/en_*8 ||
2233 image_install /usr/lib/locale/locale-archive); then
2234 dfatal "Failed to install required locales"
2235 exit 1
2236 fi
2237 else
2238 inst_recursive /usr/lib/locale/C.*8 /usr/lib/locale/en_*8
4ce68ea9 2239 fi
4ce68ea9
YW
2240}
2241
1c3f490f 2242# shellcheck disable=SC2120
889a9042 2243install_keymaps() {
569c6fd1
YW
2244 local i p
2245 local -a prefix=(
1edad893
FS
2246 "/usr/lib"
2247 "/usr/share"
569c6fd1 2248 )
ad931fee 2249
569c6fd1
YW
2250 dinfo "Install console keymaps"
2251
569c6fd1
YW
2252 if (( $# == 0 )); then
2253 for p in "${prefix[@]}"; do
2254 # The first three paths may be deprecated.
2255 # It seems now the last three paths are used by many distributions.
2256 for i in \
1edad893
FS
2257 "$p"/kbd/keymaps/include/* \
2258 "$p"/kbd/keymaps/i386/include/* \
2259 "$p"/kbd/keymaps/i386/qwerty/us.* \
2260 "$p"/kbd/keymaps/legacy/include/* \
2261 "$p"/kbd/keymaps/legacy/i386/qwerty/us.* \
2262 "$p"/kbd/keymaps/xkb/us*; do
569c6fd1
YW
2263 [[ -f "$i" ]] || continue
2264 inst "$i"
2265 done
2266 done
2267 else
2268 # When it takes any argument, then install more keymaps.
2269 for p in "${prefix[@]}"; do
2270 for i in \
1edad893
FS
2271 "$p"/kbd/keymaps/include/* \
2272 "$p"/kbd/keymaps/i386/*/* \
2273 "$p"/kbd/keymaps/legacy/i386/*/* \
2274 "$p"/kbd/keymaps/xkb/*; do
569c6fd1
YW
2275 [[ -f "$i" ]] || continue
2276 inst "$i"
2277 done
ad931fee
YW
2278 done
2279 fi
889a9042
RC
2280}
2281
1136175c
YW
2282install_x11_keymaps() {
2283 dinfo "Install x11 keymaps"
2284
2285 if (( $# == 0 )); then
2286 # Install only keymap list.
2287 inst /usr/share/X11/xkb/rules/base.lst
2288 else
2289 # When it takes any argument, then install all keymaps.
2290 inst_recursive /usr/share/X11/xkb
2291 fi
2292}
2293
7d10ec1c 2294install_zoneinfo() {
1b8fcd9c 2295 dinfo "Install time zones"
f4c40fd7
ZJS
2296 inst_any /usr/share/zoneinfo/Asia/Seoul
2297 inst_any /usr/share/zoneinfo/Asia/Vladivostok
2298 inst_any /usr/share/zoneinfo/Australia/Sydney
2299 inst_any /usr/share/zoneinfo/Europe/Berlin
129cb6e2 2300 inst_any /usr/share/zoneinfo/Europe/Dublin
568d9795 2301 inst_any /usr/share/zoneinfo/Europe/Kyiv
f4c40fd7
ZJS
2302 inst_any /usr/share/zoneinfo/Pacific/Auckland
2303 inst_any /usr/share/zoneinfo/Pacific/Honolulu
2304 inst_any /usr/share/zoneinfo/CET
2305 inst_any /usr/share/zoneinfo/EET
2306 inst_any /usr/share/zoneinfo/UTC
7d10ec1c
YW
2307}
2308
889a9042 2309install_fonts() {
1b8fcd9c 2310 dinfo "Install system fonts"
889a9042 2311 for i in \
ba0ff9fc
FB
2312 /usr/{lib,share}/kbd/consolefonts/eurlatgr* \
2313 /usr/{lib,share}/kbd/consolefonts/latarcyrheb-sun16*; do
1b8fcd9c
FS
2314 [[ -f "$i" ]] || continue
2315 inst "$i"
889a9042
RC
2316 done
2317}
2318
2319install_terminfo() {
1b8fcd9c
FS
2320 dinfo "Install terminfo files"
2321 local terminfodir
2322 for terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
2323 [ -f "${terminfodir}/l/linux" ] && break
889a9042 2324 done
39e17536 2325 image_install -o "${terminfodir}/l/linux"
889a9042
RC
2326}
2327
a49ad4c4
FB
2328has_user_dbus_socket() {
2329 if [ -f /usr/lib/systemd/user/dbus.socket ] || [ -f /etc/systemd/user/dbus.socket ]; then
2330 return 0
2331 else
2332 echo "Per-user instances are not supported. Skipping..."
2333 return 1
2334 fi
2335}
2336
48f3bc5c
LN
2337setup_nspawn_root_hook() { :;}
2338
889a9042 2339setup_nspawn_root() {
8c3534b5
ZJS
2340 if [ -z "${initdir}" ]; then
2341 dfatal "\$initdir not defined"
2342 exit 1
2343 fi
ec43f686 2344
1b8fcd9c 2345 rm -rf "${TESTDIR:?}/unprivileged-nspawn-root"
693ad298 2346
23f8e019 2347 if get_bool "$RUN_IN_UNPRIVILEGED_CONTAINER"; then
ec43f686 2348 ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
1b8fcd9c 2349 cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
746fbd9c 2350 fi
48f3bc5c
LN
2351
2352 setup_nspawn_root_hook
889a9042
RC
2353}
2354
0d6e798a 2355setup_basic_dirs() {
1b8fcd9c
FS
2356 mkdir -p "${initdir:?}/run"
2357 mkdir -p "$initdir/etc/systemd/system"
2358 mkdir -p "$initdir/var/log/journal"
2359
889a9042 2360
1b8fcd9c 2361 for d in usr/bin usr/sbin bin etc lib "${libdir:?}" sbin tmp usr var var/log var/tmp dev proc sys sysroot root run run/lock run/initramfs; do
898720b7
HH
2362 if [ -L "/$d" ]; then
2363 inst_symlink "/$d"
2364 else
0d6e798a 2365 inst_dir "/$d"
898720b7
HH
2366 fi
2367 done
2368
2369 ln -sfn /run "$initdir/var/run"
2370 ln -sfn /run/lock "$initdir/var/lock"
2371}
2372
51fa8591
ZJS
2373mask_supporting_services() {
2374 # mask some services that we do not want to run in these tests
7776b225
FS
2375 ln -fsv /dev/null "${initdir:?}/etc/systemd/system/systemd-hwdb-update.service"
2376 ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-journal-catalog-update.service"
2377 ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-networkd.service"
2378 ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-networkd.socket"
2379 ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-resolved.service"
51fa8591
ZJS
2380}
2381
898720b7 2382inst_libs() {
96af59aa
FS
2383 local bin="${1:?}"
2384 local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2385 local file line
898720b7 2386
96af59aa
FS
2387 while read -r line; do
2388 [[ "$line" = 'not a dynamic executable' ]] && break
ff254eea
ZJS
2389 # Ignore errors about our own stuff missing. This is most likely caused
2390 # by ldd attempting to use the unprefixed RPATH.
04bce24d 2391 [[ "$line" =~ (libsystemd|libudev).*\ not\ found ]] && continue
898720b7 2392
96af59aa
FS
2393 if [[ "$line" =~ not\ found ]]; then
2394 dfatal "Missing a shared library required by $bin."
2395 dfatal "Run \"ldd $bin\" to find out what it is."
2396 dfatal "$line"
39e17536 2397 dfatal "Cannot create a test image."
898720b7
HH
2398 exit 1
2399 fi
04bce24d
FS
2400
2401 if [[ "$line" =~ $so_regex ]]; then
2402 file="${BASH_REMATCH[1]}"
2403 [[ -e "${initdir:?}/$file" ]] && continue
2404 inst_library "$file"
2405 fi
96af59aa 2406 done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
898720b7
HH
2407}
2408
2409import_testdir() {
1506edca 2410 # make sure we don't get a stale LOOPDEV value from old times
1b8fcd9c
FS
2411 local _LOOPDEV="${LOOPDEV:=}"
2412 # We don't want shellcheck to follow & check the $STATEFILE
2413 # shellcheck source=/dev/null
2414 [[ -e "$STATEFILE" ]] && . "$STATEFILE"
2415 LOOPDEV="$_LOOPDEV"
3f50fff5
FS
2416 if [[ ! -d "$TESTDIR" ]]; then
2417 if [[ -z "$TESTDIR" ]]; then
0c566150 2418 TESTDIR="$(mktemp --tmpdir=$WORKDIR -d -t systemd-test.XXXXXX)"
3f50fff5
FS
2419 else
2420 mkdir -p "$TESTDIR"
2421 fi
2422
1b8fcd9c 2423 cat >"$STATEFILE" <<EOF
8c3534b5 2424TESTDIR="$TESTDIR"
8c3534b5 2425EOF
898720b7
HH
2426 export TESTDIR
2427 fi
e8945092 2428
1b8fcd9c
FS
2429 IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME:?}.img"
2430 IMAGE_PUBLIC="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img"
898720b7
HH
2431}
2432
889a9042 2433import_initdir() {
1b8fcd9c
FS
2434 initdir="${TESTDIR:?}/root"
2435 mkdir -p "$initdir"
889a9042
RC
2436 export initdir
2437}
2438
f7237408
FS
2439get_cgroup_hierarchy() {
2440 case "$(stat -c '%T' -f /sys/fs/cgroup)" in
2441 cgroup2fs)
2442 echo "unified"
2443 ;;
2444 tmpfs)
2445 if [[ -d /sys/fs/cgroup/unified && "$(stat -c '%T' -f /sys/fs/cgroup/unified)" == cgroup2fs ]]; then
2446 echo "hybrid"
2447 else
2448 echo "legacy"
2449 fi
2450 ;;
2451 *)
2452 dfatal "Failed to determine host's cgroup hierarchy"
2453 exit 1
2454 esac
2455}
2456
898720b7
HH
2457## @brief Converts numeric logging level to the first letter of level name.
2458#
2459# @param lvl Numeric logging level in range from 1 to 6.
2460# @retval 1 if @a lvl is out of range.
2461# @retval 0 if @a lvl is correct.
2462# @result Echoes first letter of level name.
2463_lvl2char() {
2464 case "$1" in
2465 1) echo F;;
2466 2) echo E;;
2467 3) echo W;;
2468 4) echo I;;
2469 5) echo D;;
2470 6) echo T;;
2471 *) return 1;;
2472 esac
2473}
2474
2475## @brief Internal helper function for _do_dlog()
2476#
2477# @param lvl Numeric logging level.
2478# @param msg Message.
2479# @retval 0 It's always returned, even if logging failed.
2480#
2481# @note This function is not supposed to be called manually. Please use
2482# dtrace(), ddebug(), or others instead which wrap this one.
2483#
2484# This function calls _do_dlog() either with parameter msg, or if
2485# none is given, it will read standard input and will use every line as
2486# a message.
2487#
2488# This enables:
2489# dwarn "This is a warning"
2490# echo "This is a warning" | dwarn
1b8fcd9c 2491LOG_LEVEL="${LOG_LEVEL:-4}"
898720b7
HH
2492
2493dlog() {
1b8fcd9c
FS
2494 local lvl lvlc
2495
898720b7 2496 [ -z "$LOG_LEVEL" ] && return 0
1b8fcd9c
FS
2497 lvl="${1:?}"; shift
2498 [ "$lvl" -le "$LOG_LEVEL" ] || return 0
2499 lvlc="$(_lvl2char "$lvl")" || return 0
898720b7
HH
2500
2501 if [ $# -ge 1 ]; then
2502 echo "$lvlc: $*"
2503 else
1b8fcd9c 2504 while read -r line; do
898720b7
HH
2505 echo "$lvlc: " "$line"
2506 done
2507 fi
2508}
2509
2510## @brief Logs message at TRACE level (6)
2511#
2512# @param msg Message.
2513# @retval 0 It's always returned, even if logging failed.
2514dtrace() {
2515 set +x
2516 dlog 6 "$@"
23f8e019 2517 if get_bool "${debug:=}"; then
1b8fcd9c
FS
2518 set -x
2519 fi
898720b7
HH
2520}
2521
2522## @brief Logs message at DEBUG level (5)
2523#
2524# @param msg Message.
2525# @retval 0 It's always returned, even if logging failed.
2526ddebug() {
898720b7 2527 dlog 5 "$@"
898720b7
HH
2528}
2529
2530## @brief Logs message at INFO level (4)
2531#
2532# @param msg Message.
2533# @retval 0 It's always returned, even if logging failed.
2534dinfo() {
2535 set +x
2536 dlog 4 "$@"
23f8e019 2537 if get_bool "${debug:=}"; then
1b8fcd9c
FS
2538 set -x
2539 fi
898720b7
HH
2540}
2541
2542## @brief Logs message at WARN level (3)
2543#
2544# @param msg Message.
2545# @retval 0 It's always returned, even if logging failed.
2546dwarn() {
2547 set +x
2548 dlog 3 "$@"
23f8e019 2549 if get_bool "${debug:=}"; then
1b8fcd9c
FS
2550 set -x
2551 fi
898720b7
HH
2552}
2553
2554## @brief Logs message at ERROR level (2)
2555#
2556# @param msg Message.
2557# @retval 0 It's always returned, even if logging failed.
2558derror() {
898720b7 2559 dlog 2 "$@"
898720b7
HH
2560}
2561
2562## @brief Logs message at FATAL level (1)
2563#
2564# @param msg Message.
2565# @retval 0 It's always returned, even if logging failed.
2566dfatal() {
2567 set +x
2568 dlog 1 "$@"
23f8e019 2569 if get_bool "${debug:=}"; then
1b8fcd9c
FS
2570 set -x
2571 fi
898720b7
HH
2572}
2573
2574
2575# Generic substring function. If $2 is in $1, return 0.
c049acb2 2576strstr() { [ "${1#*"$2"*}" != "$1" ]; }
898720b7
HH
2577
2578# normalize_path <path>
2579# Prints the normalized path, where it removes any duplicated
2580# and trailing slashes.
2581# Example:
2582# $ normalize_path ///test/test//
2583# /test/test
2584normalize_path() {
2585 shopt -q -s extglob
2586 set -- "${1//+(\/)//}"
2587 shopt -q -u extglob
2588 echo "${1%/}"
2589}
2590
2591# convert_abs_rel <from> <to>
2592# Prints the relative path, when creating a symlink to <to> from <from>.
2593# Example:
2594# $ convert_abs_rel /usr/bin/test /bin/test-2
2595# ../../bin/test-2
2596# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
2597convert_abs_rel() {
2598 local __current __absolute __abssize __cursize __newpath
2599 local -i __i __level
2600
1b8fcd9c 2601 set -- "$(normalize_path "${1:?}")" "$(normalize_path "${2:?}")"
898720b7
HH
2602
2603 # corner case #1 - self looping link
2604 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
2605
2606 # corner case #2 - own dir link
2607 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
2608
1b8fcd9c
FS
2609 IFS="/" read -ra __current <<< "$1"
2610 IFS="/" read -ra __absolute <<< "$2"
898720b7
HH
2611
2612 __abssize=${#__absolute[@]}
2613 __cursize=${#__current[@]}
2614
4627fb80 2615 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]; do
898720b7
HH
2616 (( __level++ ))
2617 if (( __level > __abssize || __level > __cursize ))
2618 then
2619 break
2620 fi
2621 done
2622
4627fb80 2623 for ((__i = __level; __i < __cursize-1; __i++)); do
898720b7
HH
2624 if ((__i > __level))
2625 then
2626 __newpath=$__newpath"/"
2627 fi
2628 __newpath=$__newpath".."
2629 done
2630
4627fb80 2631 for ((__i = __level; __i < __abssize; __i++)); do
898720b7
HH
2632 if [[ -n $__newpath ]]
2633 then
2634 __newpath=$__newpath"/"
2635 fi
2636 __newpath=$__newpath${__absolute[__i]}
2637 done
2638
2639 echo "$__newpath"
2640}
2641
2642
2643# Install a directory, keeping symlinks as on the original system.
2644# Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
2645# will create ${initdir}/lib64, ${initdir}/lib64/file,
2646# and a symlink ${initdir}/lib -> lib64.
2647inst_dir() {
1b8fcd9c
FS
2648 local dir="${1:?}"
2649 local part="${dir%/*}"
2650 local file
898720b7 2651
1b8fcd9c
FS
2652 [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there
2653
2654 while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do
2655 dir="$part $dir"
2656 part="${part%/*}"
898720b7
HH
2657 done
2658
2659 # iterate over parent directories
1b8fcd9c
FS
2660 for file in $dir; do
2661 [[ -e "${initdir}/$file" ]] && continue
2662 if [[ -L $file ]]; then
2663 inst_symlink "$file"
898720b7
HH
2664 else
2665 # create directory
1b8fcd9c
FS
2666 mkdir -m 0755 "${initdir}/$file" || return 1
2667 [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file"
2668 chmod u+w "${initdir}/$file"
898720b7
HH
2669 fi
2670 done
2671}
2672
2673# $1 = file to copy to ramdisk
2674# $2 (optional) Name for the file on the ramdisk
2675# Location of the image dir is assumed to be $initdir
2676# We never overwrite the target if it exists.
2677inst_simple() {
1b8fcd9c 2678 [[ -f "${1:?}" ]] || return 1
898720b7
HH
2679 strstr "$1" "/" || return 1
2680
1b8fcd9c
FS
2681 local src="$1"
2682 local target="${2:-$1}"
2683 if ! [[ -d ${initdir:?}/$target ]]; then
898720b7
HH
2684 [[ -e ${initdir}/$target ]] && return 0
2685 [[ -L ${initdir}/$target ]] && return 0
2686 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
2687 fi
2688 # install checksum files also
1b8fcd9c
FS
2689 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2690 inst "${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
898720b7 2691 fi
1b8fcd9c 2692 ddebug "Installing $src"
a6695a43 2693 cp --sparse=always --force --dereference --preserve=all "$src" "${initdir}/$target"
898720b7
HH
2694}
2695
2696# find symlinks linked to given library file
2697# $1 = library file
2698# Function searches for symlinks by stripping version numbers appended to
2699# library filename, checks if it points to the same target and finally
2700# prints the list of symlinks to stdout.
2701#
2702# Example:
2703# rev_lib_symlinks libfoo.so.8.1
2704# output: libfoo.so.8 libfoo.so
2705# (Only if libfoo.so.8 and libfoo.so exists on host system.)
2706rev_lib_symlinks() {
1b8fcd9c
FS
2707 local fn="${1:?}"
2708 local links=""
2709 local orig
2710 orig="$(readlink -f "$1")"
898720b7 2711
1b8fcd9c 2712 [[ "${fn}" =~ .*\.so\..* ]] || return 1
898720b7 2713
1b8fcd9c 2714 until [[ "${fn##*.}" == so ]]; do
898720b7 2715 fn="${fn%.*}"
1b8fcd9c 2716 [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}"
898720b7
HH
2717 done
2718
2719 echo "${links}"
2720}
2721
2722# Same as above, but specialized to handle dynamic libraries.
2723# It handles making symlinks according to how the original library
2724# is referenced.
2725inst_library() {
1b8fcd9c
FS
2726 local src="${1:?}"
2727 local dest="${2:-$1}"
2728 local reallib symlink
2729
898720b7 2730 strstr "$1" "/" || return 1
1b8fcd9c
FS
2731 [[ -e ${initdir:?}/$dest ]] && return 0
2732 if [[ -L $src ]]; then
898720b7 2733 # install checksum files also
1b8fcd9c
FS
2734 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2735 inst "${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac"
898720b7 2736 fi
1b8fcd9c
FS
2737 reallib="$(readlink -f "$src")"
2738 inst_simple "$reallib" "$reallib"
2739 inst_dir "${dest%/*}"
2740 [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
134d4f1b 2741 ddebug "Creating symlink $reallib -> $dest"
1b8fcd9c 2742 ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
898720b7 2743 else
1b8fcd9c 2744 inst_simple "$src" "$dest"
898720b7
HH
2745 fi
2746
2747 # Create additional symlinks. See rev_symlinks description.
1b8fcd9c
FS
2748 for symlink in $(rev_lib_symlinks "$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do
2749 if [[ ! -e "$initdir/$symlink" ]]; then
2750 ddebug "Creating extra symlink: $symlink"
2751 inst_symlink "$symlink"
2752 fi
898720b7
HH
2753 done
2754}
2755
2756# find a binary. If we were not passed the full path directly,
2757# search in the usual places to find the binary.
2758find_binary() {
1b8fcd9c
FS
2759 local bin="${1:?}"
2760 if [[ -z ${bin##/*} ]]; then
2761 if [[ -x "$bin" ]] || { strstr "$bin" ".so" && ldd "$bin" &>/dev/null; }; then
2762 echo "$bin"
898720b7
HH
2763 return 0
2764 fi
2765 fi
2766
1b8fcd9c 2767 type -P "$bin"
898720b7
HH
2768}
2769
2770# Same as above, but specialized to install binary executables.
2771# Install binary executable, and all shared library dependencies, if any.
2772inst_binary() {
1b8fcd9c
FS
2773 local bin="${1:?}"
2774 local path target
3cdb93d0
FS
2775
2776 # In certain cases we might attempt to install a binary which is already
2777 # present in the test image, yet it's missing from the host system.
2778 # In such cases, let's check if the binary indeed exists in the image
ff254eea 2779 # before doing any other checks. If it does, immediately return with
3cdb93d0 2780 # success.
1b8fcd9c
FS
2781 if [[ $# -eq 1 ]]; then
2782 for path in "" bin sbin usr/bin usr/sbin; do
2783 [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0
2784 done
2785 fi
3cdb93d0 2786
1b8fcd9c
FS
2787 bin="$(find_binary "$bin")" || return 1
2788 target="${2:-$bin}"
2789 [[ -e "${initdir:?}/$target" ]] && return 0
2790 [[ -L "$bin" ]] && inst_symlink "$bin" "$target" && return 0
2791
2792 local file line
2793 local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
3917534d
FS
2794 # DSOs provided by systemd
2795 local systemd_so_regex='/(libudev|libsystemd.*|.+[\-_]systemd([\-_].+)?|libnss_(mymachines|myhostname|resolve)).so'
2796 local wrap_binary=0
2b5e7860 2797 local enable_lsan=0
898720b7 2798 # I love bash!
1b8fcd9c
FS
2799 while read -r line; do
2800 [[ "$line" = 'not a dynamic executable' ]] && break
898720b7 2801
ff254eea
ZJS
2802 # Ignore errors about our own stuff missing. This is most likely caused
2803 # by ldd attempting to use the unprefixed RPATH.
2804 [[ "$line" =~ libsystemd.*\ not\ found ]] && continue
2805
3917534d
FS
2806 # We're built with ASan and the target binary loads one of the systemd's
2807 # DSOs, so we need to tweak the environment before executing the binary
2808 if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$line" =~ $systemd_so_regex ]]; then
2809 wrap_binary=1
2810 fi
2811
1b8fcd9c
FS
2812 if [[ "$line" =~ $so_regex ]]; then
2813 file="${BASH_REMATCH[1]}"
2814 [[ -e "${initdir}/$file" ]] && continue
2815 inst_library "$file"
898720b7
HH
2816 continue
2817 fi
2818
1b8fcd9c
FS
2819 if [[ "$line" =~ not\ found ]]; then
2820 dfatal "Missing a shared library required by $bin."
2821 dfatal "Run \"ldd $bin\" to find out what it is."
2822 dfatal "$line"
39e17536 2823 dfatal "Cannot create a test image."
898720b7
HH
2824 exit 1
2825 fi
1b8fcd9c 2826 done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
3917534d
FS
2827
2828 # Same as above, but we need to wrap certain libraries unconditionally
2829 #
52c1fb68 2830 # chgrp, chown, getent, login, setfacl, su, useradd, userdel
2de77dbf 2831 # - dlopen() (not only) systemd's PAM modules
c7bf1959 2832 # ls, mkfs.*, mksquashfs, mkswap, setpriv, stat
2de77dbf
FS
2833 # - pull in nss_systemd with certain options (like ls -l) when
2834 # nsswitch.conf uses [SUCCESS=merge] (like on Arch Linux)
e912bef8 2835 # delv, dig - pull in nss_resolve if `resolve` is in nsswitch.conf
3917534d 2836 # tar - called by machinectl in TEST-25
52c1fb68 2837 bin_rx='/(agetty|chgrp|chown|curl|delv|dig|getfacl|getent|id|login|ls|mkfs\.[a-z0-9]+|mksquashfs|mkswap|setfacl|setpriv|stat|su|tar|useradd|userdel)$'
e912bef8 2838 if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ $bin_rx ]]; then
3917534d 2839 wrap_binary=1
2b5e7860
FS
2840 # Ugh, so we want to disable LSan in most cases for the wrapped binaries, since
2841 # we don't care about memory leaks in such binaries. However, in certain cases
2842 # the external binary is the only interface for the systemd code, like for
2843 # the systemd NSS modules, where we want to detect memory leaks. So let's
2844 # do another check to decide if we want to enable LSan for given binary.
2845 if [[ "$bin" =~ /getent$ ]]; then
2846 enable_lsan=1
2847 fi
3917534d
FS
2848 fi
2849
b727d7e0
FS
2850 # If the target binary is built with ASan support, we don't need to wrap
2851 # it, as it should handle everything by itself
2852 if get_bool "$wrap_binary" && ! is_built_with_asan "$bin"; then
3917534d
FS
2853 dinfo "Creating ASan-compatible wrapper for binary '$target'"
2854 # Install the target binary with a ".orig" suffix
2855 inst_simple "$bin" "${target}.orig"
2856 # Create a simple shell wrapper in place of the target binary, which
2857 # sets necessary ASan-related env variables and then exec()s the
2858 # suffixed target binary
2859 cat >"$initdir/$target" <<EOF
2860#!/bin/bash
2861# Preload the ASan runtime DSO, otherwise ASAn will complain
2862export LD_PRELOAD="$ASAN_RT_PATH"
2863# Disable LSan to speed things up, since we don't care about leak reports
2864# from 'external' binaries
2b5e7860 2865export ASAN_OPTIONS=detect_leaks=$enable_lsan
3917534d
FS
2866# Set argv[0] to the original binary name without the ".orig" suffix
2867exec -a "\$0" -- "${target}.orig" "\$@"
2868EOF
2869 chmod +x "$initdir/$target"
2870 else
2871 inst_simple "$bin" "$target"
2872 fi
898720b7
HH
2873}
2874
2875# same as above, except for shell scripts.
2876# If your shell script does not start with shebang, it is not a shell script.
2877inst_script() {
1b8fcd9c
FS
2878 local bin line shebang_regex
2879 bin="$(find_binary "${1:?}")" || return 1
898720b7 2880 shift
1b8fcd9c
FS
2881
2882 read -r -n 80 line <"$bin"
898720b7 2883 # If debug is set, clean unprintable chars to prevent messing up the term
23f8e019 2884 get_bool "${debug:=}" && line="$(echo -n "$line" | tr -c -d '[:print:][:space:]')"
1b8fcd9c
FS
2885 shebang_regex='(#! *)(/[^ ]+).*'
2886 [[ "$line" =~ $shebang_regex ]] || return 1
2887 inst "${BASH_REMATCH[2]}" && inst_simple "$bin" "$@"
898720b7
HH
2888}
2889
2890# same as above, but specialized for symlinks
2891inst_symlink() {
1b8fcd9c
FS
2892 local src="${1:?}"
2893 local target="${2:-$src}"
2894 local realsrc
2895
2896 strstr "$src" "/" || return 1
2897 [[ -L "$src" ]] || return 1
2898 [[ -L "${initdir:?}/$target" ]] && return 0
2899 realsrc="$(readlink -f "$src")"
2900 if ! [[ -e "$initdir/$realsrc" ]]; then
2901 if [[ -d "$realsrc" ]]; then
2902 inst_dir "$realsrc"
898720b7 2903 else
1b8fcd9c 2904 inst "$realsrc"
898720b7
HH
2905 fi
2906 fi
1b8fcd9c
FS
2907 [[ ! -e "$initdir/${target%/*}" ]] && inst_dir "${target%/*}"
2908 [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}"
2909 ln -sfn -- "$(convert_abs_rel "${target}" "${realsrc}")" "$initdir/$target"
898720b7
HH
2910}
2911
2912# attempt to install any programs specified in a udev rule
2913inst_rule_programs() {
1b8fcd9c
FS
2914 local rule="${1:?}"
2915 local prog bin
898720b7 2916
29bff80b 2917 sed -rn 's/^.*?PROGRAM==?"([^ "]+).*$/\1/p' "$rule" | while read -r prog; do
1b8fcd9c
FS
2918 if [ -x "/lib/udev/$prog" ]; then
2919 bin="/lib/udev/$prog"
2920 else
2921 if ! bin="$(find_binary "$prog")"; then
2922 dinfo "Skipping program $prog used in udev rule $(basename "$rule") as it cannot be found"
2923 continue
898720b7 2924 fi
1b8fcd9c 2925 fi
898720b7 2926
1b8fcd9c 2927 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
39e17536 2928 image_install "$bin"
1b8fcd9c 2929 done
898720b7
HH
2930}
2931
2932# udev rules always get installed in the same place, so
2933# create a function to install them to make life simpler.
2934inst_rules() {
1b8fcd9c
FS
2935 local target=/etc/udev/rules.d
2936 local found rule
898720b7
HH
2937
2938 inst_dir "/lib/udev/rules.d"
1b8fcd9c
FS
2939 inst_dir "$target"
2940 for rule in "$@"; do
898720b7
HH
2941 if [ "${rule#/}" = "$rule" ]; then
2942 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1b8fcd9c
FS
2943 if [[ -f "$r/$rule" ]]; then
2944 found="$r/$rule"
2945 inst_simple "$found"
2946 inst_rule_programs "$found"
898720b7
HH
2947 fi
2948 done
2949 fi
1b8fcd9c
FS
2950 for r in '' ./; do
2951 if [[ -f "${r}${rule}" ]]; then
2952 found="${r}${rule}"
2953 inst_simple "$found" "$target/${found##*/}"
2954 inst_rule_programs "$found"
898720b7
HH
2955 fi
2956 done
1b8fcd9c
FS
2957 [[ $found ]] || dinfo "Skipping udev rule: $rule"
2958 found=
898720b7
HH
2959 done
2960}
2961
2962# general purpose installation function
2963# Same args as above.
2964inst() {
898720b7
HH
2965 case $# in
2966 1) ;;
1b8fcd9c
FS
2967 2)
2968 [[ ! "$initdir" && -d "$2" ]] && export initdir="$2"
2969 [[ "$initdir" = "$2" ]] && set "$1"
2970 ;;
2971 3)
2972 [[ -z "$initdir" ]] && export initdir="$2"
2973 set "$1" "$3"
2974 ;;
2975 *)
2976 dfatal "inst only takes 1 or 2 or 3 arguments"
2977 exit 1
2978 ;;
898720b7 2979 esac
1b8fcd9c
FS
2980
2981 local fun
2982 for fun in inst_symlink inst_script inst_binary inst_simple; do
2983 "$fun" "$@" && return 0
898720b7 2984 done
7074c047
FS
2985
2986 dwarn "Failed to install '$1'"
898720b7
HH
2987 return 1
2988}
2989
2990# install any of listed files
2991#
2992# If first argument is '-d' and second some destination path, first accessible
2993# source is installed into this path, otherwise it will installed in the same
2994# path as source. If none of listed files was installed, function return 1.
2995# On first successful installation it returns with 0 status.
2996#
2997# Example:
2998#
2999# inst_any -d /bin/foo /bin/bar /bin/baz
3000#
3001# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
32e27670 3002# initrd.
898720b7 3003inst_any() {
1b8fcd9c 3004 local dest file
898720b7 3005
1b8fcd9c 3006 [[ "${1:?}" = '-d' ]] && dest="${2:?}" && shift 2
898720b7 3007
1b8fcd9c
FS
3008 for file in "$@"; do
3009 if [[ -e "$file" ]]; then
3010 [[ -n "$dest" ]] && inst "$file" "$dest" && return 0
3011 inst "$file" && return 0
898720b7
HH
3012 fi
3013 done
3014
3015 return 1
3016}
3017
da0465dc
YW
3018inst_recursive() {
3019 local p item
3020
3021 for p in "$@"; do
eb5d7730
FS
3022 # Make sure the source exists, as the process substitution below
3023 # suppresses errors
3024 stat "$p" >/dev/null || return 1
3025
da0465dc
YW
3026 while read -r item; do
3027 if [[ -d "$item" ]]; then
3028 inst_dir "$item"
3029 elif [[ -f "$item" ]]; then
3030 inst_simple "$item"
3031 fi
3032 done < <(find "$p" 2>/dev/null)
3033 done
3034}
3035
39e17536
FS
3036# image_install [-o ] <file> [<file> ... ]
3037# Install <file> to the test image
898720b7 3038# -o optionally install the <file> and don't fail, if it is not there
39e17536 3039image_install() {
1b8fcd9c
FS
3040 local optional=no
3041 local prog="${1:?}"
3042
3043 if [[ "$prog" = '-o' ]]; then
3044 optional=yes
898720b7
HH
3045 shift
3046 fi
1b8fcd9c
FS
3047
3048 for prog in "$@"; do
3049 if ! inst "$prog" ; then
23f8e019 3050 if get_bool "$optional"; then
1b8fcd9c 3051 dinfo "Skipping program $prog as it cannot be found and is" \
898720b7
HH
3052 "flagged to be optional"
3053 else
1b8fcd9c 3054 dfatal "Failed to install $prog"
898720b7
HH
3055 exit 1
3056 fi
3057 fi
898720b7
HH
3058 done
3059}
3060
0d6e798a
HH
3061# Install a single kernel module along with any firmware it may require.
3062# $1 = full path to kernel module to install
3063install_kmod_with_fw() {
94009c27 3064 local module="${1:?}"
0d6e798a 3065 # no need to go further if the module is already installed
c049acb2 3066 [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*"/lib/modules/$KERNEL_VER/"}" ]] && return 0
94009c27 3067 [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0
0d6e798a 3068
94009c27 3069 [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}"
0d6e798a 3070
c049acb2 3071 inst_simple "$module" "/lib/modules/$KERNEL_VER/${module##*"/lib/modules/$KERNEL_VER/"}" || return $?
0d6e798a 3072
94009c27
FS
3073 local modname="${module##*/}"
3074 local fwdir found fw
3075 modname="${modname%.ko*}"
0d6e798a 3076
94009c27
FS
3077 while read -r fw; do
3078 found=
3079 for fwdir in /lib/firmware/updates /lib/firmware; do
3080 if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then
3081 inst_simple "$fwdir/$fw" "/lib/firmware/$fw"
3082 found=yes
0d6e798a
HH
3083 fi
3084 done
23f8e019 3085 if ! get_bool "$found"; then
94009c27
FS
3086 if ! grep -qe "\<${modname//-/_}\>" /proc/modules; then
3087 dinfo "Possible missing firmware \"${fw}\" for kernel module" \
3088 "\"${modname}.ko\""
0d6e798a 3089 else
94009c27
FS
3090 dwarn "Possible missing firmware \"${fw}\" for kernel module" \
3091 "\"${modname}.ko\""
0d6e798a
HH
3092 fi
3093 fi
94009c27 3094 done < <(modinfo -k "$KERNEL_VER" -F firmware "$module" 2>/dev/null)
0d6e798a
HH
3095 return 0
3096}
3097
3098# Do something with all the dependencies of a kernel module.
3099# Note that kernel modules depend on themselves using the technique we use
3100# $1 = function to call for each dependency we find
3101# It will be passed the full path to the found kernel module
3102# $2 = module to get dependencies for
3103# rest of args = arguments to modprobe
0d6e798a 3104for_each_kmod_dep() {
94009c27
FS
3105 local func="${1:?}"
3106 local kmod="${2:?}"
3107 local found=0
3108 local cmd modpath
0d6e798a 3109 shift 2
0d6e798a 3110
94009c27
FS
3111 while read -r cmd modpath _; do
3112 [[ "$cmd" = insmod ]] || continue
3113 "$func" "$modpath" || return $?
3114 found=1
3115 done < <(modprobe "$@" --ignore-install --show-depends "$kmod")
0d6e798a 3116
23f8e019 3117 ! get_bool "$found" && return 1
94009c27 3118 return 0
0d6e798a
HH
3119}
3120
3121# instmods [-c] <kernel module> [<kernel module> ... ]
3122# instmods [-c] <kernel subsystem>
3123# install kernel modules along with all their dependencies.
3124# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
94009c27 3125# FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5
0d6e798a 3126instmods() {
94009c27
FS
3127 local check=no
3128 if [[ $# -ge 0 && "$1" = '-c' ]]; then
3129 check=yes
0d6e798a
HH
3130 shift
3131 fi
3132
94009c27
FS
3133 inst1mod() {
3134 local mod="${1:?}"
3135 local ret=0
3136 local mod_dir="/lib/modules/${KERNEL_VER:?}/"
3137
3138 case "$mod" in
0d6e798a 3139 =*)
94009c27
FS
3140 if [ -f "${mod_dir}/modules.${mod#=}" ]; then
3141 (
3142 [[ "$mpargs" ]] && echo "$mpargs"
3143 cat "${mod_dir}/modules.${mod#=}"
3144 ) | instmods
0d6e798a 3145 else
94009c27
FS
3146 (
3147 [[ "$mpargs" ]] && echo "$mpargs"
84817bfd 3148 find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f -printf '%f\n'
94009c27 3149 ) | instmods
0d6e798a
HH
3150 fi
3151 ;;
94009c27
FS
3152 --*)
3153 mpargs+=" $mod"
3154 ;;
3155 i2o_scsi)
3156 # Do not load this diagnostic-only module
3157 return
3158 ;;
0d6e798a 3159 *)
94009c27 3160 mod=${mod##*/}
0d6e798a
HH
3161 # if we are already installed, skip this module and go on
3162 # to the next one.
94009c27 3163 [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return
0d6e798a
HH
3164
3165 # We use '-d' option in modprobe only if modules prefix path
3166 # differs from default '/'. This allows us to use Dracut with
3167 # old version of modprobe which doesn't have '-d' option.
94009c27
FS
3168 local mod_dirname=${mod_dir%%/lib/modules/*}
3169 [[ -n ${mod_dirname} ]] && mod_dirname="-d ${mod_dirname}/"
0d6e798a
HH
3170
3171 # ok, load the module, all its dependencies, and any firmware
3172 # it may require
94009c27
FS
3173 for_each_kmod_dep install_kmod_with_fw "$mod" \
3174 --set-version "$KERNEL_VER" \
3175 ${mod_dirname:+"$mod_dirname"} \
3176 ${mpargs:+"$mpargs"}
3177 ((ret+=$?))
0d6e798a
HH
3178 ;;
3179 esac
7bf20e48 3180 return "$ret"
0d6e798a
HH
3181 }
3182
94009c27
FS
3183 local mod mpargs
3184
3185 if [[ $# -eq 0 ]]; then # filenames from stdin
3186 while read -r mod; do
3187 if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then
3188 dfatal "Failed to install $mod"
3189 return 1
3190 fi
0d6e798a 3191 done
94009c27 3192 fi
0d6e798a 3193
94009c27
FS
3194 for mod in "$@"; do # filenames as arguments
3195 if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then
3196 dfatal "Failed to install $mod"
3197 return 1
3198 fi
3199 done
3200
3201 return 0
0d6e798a 3202}
898720b7 3203
9e19a8b0 3204_umount_dir() {
1b8fcd9c
FS
3205 local mountpoint="${1:?}"
3206 if mountpoint -q "$mountpoint"; then
3207 ddebug "umount $mountpoint"
3208 umount "$mountpoint"
9e19a8b0
DS
3209 fi
3210}
3211
ec4cab49
DS
3212# can be overridden in specific test
3213test_setup_cleanup() {
ec43f686 3214 cleanup_initdir
ec4cab49
DS
3215}
3216
3217_test_cleanup() {
f85bc044
DS
3218 # (post-test) cleanup should always ignore failure and cleanup as much as possible
3219 (
3220 set +e
1b8fcd9c
FS
3221 [[ -n "$initdir" ]] && _umount_dir "$initdir"
3222 [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC"
21be71ee 3223 # If multiple setups/cleans are ran in parallel, this can cause a race
1c3f490f 3224 if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then
21be71ee
LB
3225 rm -vf "${IMAGESTATEDIR}/default.img"
3226 fi
1b8fcd9c
FS
3227 [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR"
3228 [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE"
65dd488f 3229 ) || :
ec4cab49
DS
3230}
3231
054ee249
MP
3232# can be overridden in specific test
3233test_cleanup() {
ec4cab49 3234 _test_cleanup
054ee249
MP
3235}
3236
693ad298
ZJS
3237test_cleanup_again() {
3238 [ -n "$TESTDIR" ] || return
3239 rm -rf "$TESTDIR/unprivileged-nspawn-root"
1b8fcd9c 3240 [[ -n "$initdir" ]] && _umount_dir "$initdir"
a82cf490
LB
3241 # Test specific images are not reused, so delete them or we run out of disk space
3242 if [[ -n "$IMAGE_PUBLIC" ]] && [ "$(basename "$IMAGE_PUBLIC")" != "default.img" ]; then
3243 rm -vf "$IMAGE_PUBLIC"
3244 fi
3245 if [[ -n "$IMAGE_PRIVATE" ]] && [ "$(basename "$IMAGE_PRIVATE")" != "default.img" ]; then
3246 rm -vf "$IMAGE_PRIVATE"
3247 fi
693ad298
ZJS
3248}
3249
8c3534b5 3250test_create_image() {
70ce817c
ZJS
3251 create_empty_image_rootdir
3252
3253 # Create what will eventually be our root filesystem onto an overlay
3254 (
3255 LOG_LEVEL=5
70ce817c 3256 setup_basic_environment
70ce817c 3257 )
8c3534b5
ZJS
3258}
3259
3260test_setup() {
58bcbad8
FS
3261 if ! get_bool "$NO_BUILD" && \
3262 get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \
3263 command -v meson >/dev/null && \
3264 [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
20f938ff 3265 dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
8c3534b5
ZJS
3266 exit 1
3267 fi
3268
1b8fcd9c
FS
3269 if [ -e "${IMAGE_PRIVATE:?}" ]; then
3270 echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")"
8c3534b5 3271 mount_initdir
2991fa41 3272 else
1b8fcd9c 3273 if [ ! -e "${IMAGE_PUBLIC:?}" ]; then
d9e606e8 3274 # default.img is the base that every test uses and optionally appends to
1b8fcd9c 3275 if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] || [ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then
d9e606e8
LB
3276 # Create the backing public image, but then completely unmount
3277 # it and drop the loopback device responsible for it, since we're
3278 # going to symlink/copy the image and mount it again from
3279 # elsewhere.
1b8fcd9c 3280 local image_old="${IMAGE_PUBLIC}"
d9e606e8
LB
3281 if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
3282 IMAGE_PUBLIC="${IMAGESTATEDIR}/default.img"
3283 fi
3284 test_create_image
3285 test_setup_cleanup
3286 umount_loopback
3287 cleanup_loopdev
3288 IMAGE_PUBLIC="${image_old}"
3289 fi
23f8e019 3290 if [ "${IMAGE_NAME:?}" != "default" ] && ! get_bool "${TEST_FORCE_NEWIMAGE}"; then
d9e606e8
LB
3291 cp -v "$(realpath "${IMAGESTATEDIR}/default.img")" "$IMAGE_PUBLIC"
3292 fi
3293 fi
3294
23f8e019
FS
3295 local hook_defined
3296 declare -f -F test_append_files >/dev/null && hook_defined=yes || hook_defined=no
2991fa41 3297
1b8fcd9c 3298 echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")"
23f8e019 3299 if get_bool "$TEST_PARALLELIZE" || get_bool "$hook_defined"; then
1b8fcd9c 3300 cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
7a57256c 3301 else
1b8fcd9c 3302 ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
7a57256c 3303 fi
2991fa41 3304
8c3534b5 3305 mount_initdir
508a7f04
FS
3306
3307 if get_bool "${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED}"; then
eb70d945
FS
3308 dinfo "Masking supporting services"
3309 mask_supporting_services
3310 fi
3311
7fdd6e15
FS
3312 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
3313 # Do an initial coverage capture, to make sure the final report includes
3314 # files that the tests didn't touch at all
9fe18055 3315 lcov --initial --capture --directory "${initdir}/${BUILD_DIR:?}" --exclude "*.gperf" --output-file "${TESTDIR:?}/coverage-base"
7fdd6e15
FS
3316 fi
3317
23f8e019 3318 if get_bool "$hook_defined"; then
1b8fcd9c 3319 test_append_files "${initdir:?}"
d9e606e8 3320 fi
8c3534b5
ZJS
3321 fi
3322
70ce817c
ZJS
3323 setup_nspawn_root
3324}
3325
054ee249 3326test_run() {
7a321b5a 3327 local test_name="${1:?}"
4962ed9f 3328 mount_initdir
4962ed9f 3329
23f8e019 3330 if ! get_bool "${TEST_NO_QEMU:=}"; then
7a321b5a 3331 if run_qemu "$test_name"; then
3e8caa34 3332 check_result_qemu || { echo "qemu test failed"; return 1; }
054ee249 3333 else
3e8caa34 3334 dwarn "can't run qemu, skipping"
054ee249
MP
3335 fi
3336 fi
23f8e019 3337 if ! get_bool "${TEST_NO_NSPAWN:=}"; then
ec43f686 3338 mount_initdir
7a321b5a 3339 if run_nspawn "${initdir:?}" "$test_name"; then
ec43f686 3340 check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
054ee249
MP
3341 else
3342 dwarn "can't run systemd-nspawn, skipping"
3343 fi
746fbd9c 3344
23f8e019 3345 if get_bool "${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
ec43f686 3346 dir="$TESTDIR/unprivileged-nspawn-root"
7a321b5a 3347 if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_name"; then
ec43f686 3348 check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
746fbd9c
EV
3349 else
3350 dwarn "can't run systemd-nspawn, skipping"
3351 fi
d56db495 3352 fi
054ee249
MP
3353 fi
3354 return 0
3355}
3356
898720b7 3357do_test() {
33a5e20f
HH
3358 if [[ $UID != "0" ]]; then
3359 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
3360 exit 0
3361 fi
3362
23f8e019 3363 if get_bool "${TEST_NO_QEMU:=}" && get_bool "${TEST_NO_NSPAWN:=}"; then
3e8caa34 3364 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both qemu and nspawn disabled" >&2
aeac20fc
LB
3365 exit 0
3366 fi
3367
23f8e019 3368 if get_bool "${TEST_QEMU_ONLY:=}" && ! get_bool "$TEST_NO_NSPAWN"; then
3e8caa34 3369 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: qemu-only tests requested" >&2
51d56d3b
LB
3370 exit 0
3371 fi
3372
23f8e019 3373 if get_bool "${TEST_PREFER_NSPAWN:=}" && ! get_bool "$TEST_NO_NSPAWN"; then
eb3785f3
LB
3374 TEST_NO_QEMU=1
3375 fi
3376
cc5549ca 3377 # Detect lib paths
1b8fcd9c 3378 [[ "$libdir" ]] || for libdir in /lib64 /lib; do
898720b7
HH
3379 [[ -d $libdir ]] && libdirs+=" $libdir" && break
3380 done
3381
1b8fcd9c 3382 [[ "$usrlibdir" ]] || for usrlibdir in /usr/lib64 /usr/lib; do
898720b7
HH
3383 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
3384 done
3385
0c566150 3386 mkdir -p "$WORKDIR"
22077c9c
MP
3387 mkdir -p "$STATEDIR"
3388
898720b7 3389 import_testdir
889a9042 3390 import_initdir
898720b7 3391
954c77c2
ZJS
3392 if [ -n "${SUDO_USER}" ]; then
3393 ddebug "Making ${TESTDIR:?} readable for ${SUDO_USER} (acquired from sudo)"
3394 setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}"
954c77c2
ZJS
3395 fi
3396
1b8fcd9c 3397 testname="$(basename "$PWD")"
8af10ca3 3398
898720b7
HH
3399 while (($# > 0)); do
3400 case $1 in
3401 --run)
8af10ca3 3402 echo "${testname} RUN: $TEST_DESCRIPTION"
7a321b5a 3403 test_run "$TESTNAME"
0013fac2 3404 ret=$?
1b8fcd9c 3405 if [ $ret -eq 0 ]; then
8af10ca3 3406 echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
898720b7 3407 else
8af10ca3 3408 echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
898720b7 3409 fi
23f8e019
FS
3410 exit $ret
3411 ;;
898720b7 3412 --setup)
8af10ca3 3413 echo "${testname} SETUP: $TEST_DESCRIPTION"
898720b7 3414 test_setup
ec4cab49 3415 test_setup_cleanup
818567fc 3416 ;;
693ad298 3417 --clean)
8af10ca3 3418 echo "${testname} CLEANUP: $TEST_DESCRIPTION"
898720b7 3419 test_cleanup
818567fc 3420 ;;
693ad298 3421 --clean-again)
8af10ca3 3422 echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
693ad298
ZJS
3423 test_cleanup_again
3424 ;;
898720b7 3425 --all)
818567fc 3426 ret=0
8af10ca3 3427 echo -n "${testname}: $TEST_DESCRIPTION "
0761da38
LB
3428 # Do not use a subshell, otherwise cleanup variables (LOOPDEV) will be lost
3429 # and loop devices will leak
3430 test_setup </dev/null >"$TESTLOG" 2>&1 || ret=$?
3431 if [ $ret -eq 0 ]; then
3432 test_setup_cleanup </dev/null >>"$TESTLOG" 2>&1 || ret=$?
3433 fi
3434 if [ $ret -eq 0 ]; then
7a321b5a 3435 test_run "$TESTNAME" </dev/null >>"$TESTLOG" 2>&1 || ret=$?
0761da38 3436 fi
f85bc044 3437 test_cleanup
898720b7 3438 if [ $ret -eq 0 ]; then
49e83429
LB
3439 # $TESTLOG is in $STATEDIR, so clean it up only on success
3440 [[ -n "$STATEDIR" ]] && rm -vfr "$STATEDIR"
898720b7
HH
3441 echo "[OK]"
3442 else
3443 echo "[FAILED]"
22077c9c 3444 echo "see $TESTLOG"
898720b7 3445 fi
23f8e019
FS
3446 exit $ret
3447 ;;
3448 *)
3449 break
3450 ;;
898720b7
HH
3451 esac
3452 shift
3453 done
3454}