2 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
3 # SPDX-License-Identifier: LGPL-2.1-or-later
5 # shellcheck disable=SC2030,SC2031
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.
13 # * comment in function install_verity_minimal()
14 # * koalaman/shellcheck#280
17 PATH
=/sbin
:/bin
:/usr
/sbin
:/usr
/bin
20 os_release
=$
(test -e /etc
/os-release
&& echo /etc
/os-release ||
echo /usr
/lib
/os-release
)
21 # shellcheck source=/dev/null
23 [[ "$ID" = "debian" ||
" $ID_LIKE " = *" debian "* ]] && LOOKS_LIKE_DEBIAN
=yes || LOOKS_LIKE_DEBIAN
=""
24 [[ "$ID" = "arch" ||
" $ID_LIKE " = *" arch "* ]] && LOOKS_LIKE_ARCH
=yes || LOOKS_LIKE_ARCH
=""
25 [[ " $ID_LIKE " = *" suse "* ]] && LOOKS_LIKE_SUSE
=yes || LOOKS_LIKE_SUSE
=""
26 KERNEL_VER
="${KERNEL_VER-$(uname -r)}"
27 QEMU_TIMEOUT
="${QEMU_TIMEOUT:-infinity}"
28 NSPAWN_TIMEOUT
="${NSPAWN_TIMEOUT:-infinity}"
29 TIMED_OUT
= # will be 1 after run_* if *_TIMEOUT is set and test timed out
30 [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE
="${FSTYPE:-btrfs}" || FSTYPE
="${FSTYPE:-ext4}"
31 UNIFIED_CGROUP_HIERARCHY
="${UNIFIED_CGROUP_HIERARCHY:-default}"
32 EFI_MOUNT
="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}"
33 QEMU_MEM
="${QEMU_MEM:-512M}"
34 # Note that defining a different IMAGE_NAME in a test setup script will only result
35 # in default.img being copied and renamed. It can then be extended by defining
36 # a test_append_files() function. The $1 parameter will be the root directory.
37 # To force creating a new image from scratch (eg: to encrypt it), also define
38 # TEST_FORCE_NEWIMAGE=1 in the test setup script.
39 IMAGE_NAME
=${IMAGE_NAME:-default}
40 STRIP_BINARIES
="${STRIP_BINARIES:-yes}"
41 TEST_REQUIRE_INSTALL_TESTS
="${TEST_REQUIRE_INSTALL_TESTS:-1}"
42 TEST_PARALLELIZE
="${TEST_PARALLELIZE:-0}"
45 # Simple wrapper to unify boolean checks.
46 # Note: this function needs to stay near the top of the file, so we can use it
47 # in code in the outermost scope.
49 # Make the value lowercase to make the regex matching simpler
52 # Consider empty value as "false"
53 if [[ -z "$_bool" ||
"$_bool" =~ ^
(0|no|false
)$
]]; then
55 elif [[ "$_bool" =~ ^
(1|
yes|true
)$
]]; then
58 echo >&2 "Value '$_bool' is not a valid boolean value"
63 # Decide if we can (and want to) run QEMU with KVM acceleration.
64 # Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
65 # check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
66 # running under KVM. If these conditions are met, enable KVM (and possibly
67 # nested KVM), otherwise disable it.
68 if get_bool
"${TEST_NESTED_KVM:=}" ||
(! get_bool
"${TEST_NO_KVM:=}" && [[ "$(systemd-detect-virt -v)" != kvm
]]); then
74 if ! ROOTLIBDIR
=$
(pkg-config
--variable=systemdutildir systemd
); then
75 echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
76 ROOTLIBDIR
=/usr
/lib
/systemd
79 # The calling test.sh scripts have TEST_BASE_DIR set via their Makefile, but we don't need them to provide it
80 TEST_BASE_DIR
=${TEST_BASE_DIR:-$(realpath "$(dirname "${BASH_SOURCE[0]}")")}
81 TEST_UNITS_DIR
="$(realpath "$TEST_BASE_DIR/units
")"
82 SOURCE_DIR
=$
(realpath
"$TEST_BASE_DIR/..")
83 TOOLS_DIR
="$SOURCE_DIR/tools"
84 # These variables are used by test scripts
85 export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
87 # note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
88 if get_bool
"${NO_BUILD:=}"; then
89 BUILD_DIR
="$SOURCE_DIR"
90 elif ! BUILD_DIR
="$("$TOOLS_DIR"/find-build-dir.sh)"; then
91 echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
95 PATH_TO_INIT
="$ROOTLIBDIR/systemd"
96 SYSTEMD_JOURNALD
="${SYSTEMD_JOURNALD:-$(command -v "$BUILD_DIR/systemd-journald" || command -v "$ROOTLIBDIR/systemd-journald")}"
97 SYSTEMD_JOURNAL_REMOTE
="${SYSTEMD_JOURNAL_REMOTE:-$(command -v "$BUILD_DIR/systemd-journal-remote" || command -v "$ROOTLIBDIR/systemd-journal-remote")}"
98 SYSTEMD
="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR/systemd")}"
99 SYSTEMD_NSPAWN
="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
100 JOURNALCTL
="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
102 TESTFILE
="${BASH_SOURCE[1]}"
103 if [ -z "$TESTFILE" ]; then
104 echo "ERROR: test-functions must be sourced from one of the TEST-*/test.sh scripts" >&2
107 TESTNAME
="$(basename "$
(dirname "$(realpath "$TESTFILE")")")"
108 STATEDIR
="$BUILD_DIR/test/$TESTNAME"
109 STATEFILE
="$STATEDIR/.testdir"
110 IMAGESTATEDIR
="$STATEDIR/.."
111 TESTLOG
="$STATEDIR/test.log"
113 if ! [[ "$TESTNAME" =~ ^TEST\
-([0-9]+)\
-.
+$
]]; then
114 echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2
117 TESTID
="${BASH_REMATCH[1]:?}"
119 if [[ ! -f "$TEST_UNITS_DIR/testsuite-$TESTID.service" ]]; then
120 echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/testsuite-$TESTID.service" >&2
225 is_built_with_asan
() {
226 if ! type -P objdump
>/dev
/null
; then
227 ddebug
"Failed to find objdump. Assuming systemd hasn't been built with ASAN."
231 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
233 _asan_calls
="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "(callq?|brasl?|bl
)\s.
+__asan
" -c)"
234 if ((_asan_calls
< 1000)); then
241 is_built_with_coverage
() {
242 if get_bool
"${NO_BUILD:=}" ||
! command -v meson
>/dev
/null
; then
246 meson configure
"${BUILD_DIR:?}" |
grep 'b_coverage' |
awk '{ print $2 }' |
grep -q 'true'
249 IS_BUILT_WITH_ASAN
=$
(is_built_with_asan
&& echo yes ||
echo no
)
250 IS_BUILT_WITH_COVERAGE
=$
(is_built_with_coverage
&& echo yes ||
echo no
)
252 if get_bool
"$IS_BUILT_WITH_ASAN"; then
254 SKIP_INITRD
="${SKIP_INITRD:-yes}"
255 PATH_TO_INIT
=$ROOTLIBDIR/systemd-under-asan
257 QEMU_SMP
="${QEMU_SMP:-4}"
259 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
260 if ASAN_RT_NAME
="$(awk '/libasan.so/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
262 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-gcc} --print-file-name "$ASAN_RT_NAME")")"
263 elif ASAN_RT_NAME
="$(awk '/libclang_rt.asan/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
265 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-clang} --print-file-name "$ASAN_RT_NAME")")"
267 # As clang's ASan DSO is usually in a non-standard path, let's check if
268 # the environment is set accordingly. If not, warn the user and exit.
269 # We're not setting the LD_LIBRARY_PATH automagically here, because
270 # user should encounter (and fix) the same issue when running the unit
272 if ldd
"$SYSTEMD" |
grep -q "libclang_rt.asan.*not found"; then
273 echo >&2 "clang's ASan DSO ($ASAN_RT_NAME) is not present in the runtime library path"
274 echo >&2 "Consider setting LD_LIBRARY_PATH=${ASAN_RT_PATH%/*}"
278 echo >&2 "systemd is not linked against the ASan DSO"
279 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
283 echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
287 QEMU_BIN
="${QEMU_BIN:-""}"
288 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
289 if get_bool
"$QEMU_KVM"; then
290 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)"
293 [[ -n "$ARCH" ]] || ARCH
="$(uname -m)"
296 # QEMU's own build system calls it qemu-system-x86_64
297 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)"
300 # new i386 version of QEMU
301 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)"
303 # i386 version of QEMU
304 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu 2>/dev/null | grep '^/' -m1)"
307 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)"
311 if [[ ! -e "$QEMU_BIN" ]]; then
312 echo "Could not find a suitable QEMU binary" >&2
317 # Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu
318 # returns 0 if newer or equal
320 # returns 2 if failing
322 find_qemu_bin ||
return 2
324 # get version from binary
326 qemu_ver
="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')"
328 # Check version string format
329 echo "$qemu_ver" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
330 echo "$1" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
332 # compare as last command to return that value
333 printf "%s\n%s\n" "$1" "$qemu_ver" |
sort -V -C
336 # Return 0 if QEMU did run (then you must check the result state/logs for actual
337 # success), or 1 if QEMU is not available.
339 if [ -f /etc
/machine-id
]; then
340 read -r MACHINE_ID
</etc
/machine-id
341 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
342 && INITRD
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
343 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
344 && KERNEL_BIN
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
349 rm -f "$initdir"/{testok
,failed
,skipped
}
350 # make sure the initdir is not mounted to avoid concurrent access
354 if [[ ! "$KERNEL_BIN" ]]; then
355 if get_bool
"$LOOKS_LIKE_ARCH"; then
356 KERNEL_BIN
=/boot
/vmlinuz-linux
358 [ "$ARCH" ] || ARCH
=$
(uname
-m)
361 # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
362 # uses the "standard" vmlinuz- prefix
363 [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN
="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
367 KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
373 local default_fedora_initrd
="/boot/initramfs-${KERNEL_VER}.img"
374 local default_debian_initrd
="/boot/initrd.img-${KERNEL_VER}"
375 local default_arch_initrd
="/boot/initramfs-linux-fallback.img"
376 local default_suse_initrd
="/boot/initrd-${KERNEL_VER}"
377 if [[ ! "$INITRD" ]]; then
378 if [[ -e "$default_fedora_initrd" ]]; then
379 INITRD
="$default_fedora_initrd"
380 elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then
381 INITRD
="$default_debian_initrd"
382 elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
383 INITRD
="$default_arch_initrd"
384 elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
385 INITRD
="$default_suse_initrd"
389 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
390 # i.e. use the number of online CPUs on the host machine. If the nproc utility
391 # is not installed or there's some other error when calling it, fall back
392 # to the original value (QEMU_SMP=1).
393 if [[ -z "${QEMU_SMP:=}" ]]; then
394 if ! QEMU_SMP
=$
(nproc
); then
395 dwarn
"nproc utility is not installed, falling back to QEMU_SMP=1"
400 find_qemu_bin ||
return 1
402 # Umount initdir to avoid concurrent access to the filesystem
403 _umount_dir
"$initdir"
405 local kernel_params
=()
406 local qemu_options
=()
407 local qemu_cmd
=("$QEMU_BIN")
409 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
410 kernel_params
+=("systemd.unified_cgroup_hierarchy=yes")
411 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
412 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes")
413 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
414 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no")
415 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
416 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
420 if get_bool
"$LOOKS_LIKE_SUSE"; then
421 kernel_params
+=("rd.hostonly=0")
425 "root=LABEL=systemd_boot"
432 "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
433 "systemd.unit=testsuite.target"
434 "systemd.wants=testsuite-$1.service"
437 if ! get_bool
"$INTERACTIVE_DEBUG"; then
438 kernel_params
+=("systemd.wants=end.service")
441 [ -e "$IMAGE_PRIVATE" ] && image
="$IMAGE_PRIVATE" || image
="$IMAGE_PUBLIC"
447 -kernel "$KERNEL_BIN"
448 -drive "format=raw,cache=unsafe,file=$image"
451 if [[ -n "${QEMU_OPTIONS:=}" ]]; then
452 local user_qemu_options
453 read -ra user_qemu_options
<<< "$QEMU_OPTIONS"
454 qemu_options
+=("${user_qemu_options[@]}")
457 if [[ -n "${KERNEL_APPEND:=}" ]]; then
458 local user_kernel_append
459 read -ra user_kernel_append
<<< "$KERNEL_APPEND"
460 kernel_params
+=("${user_kernel_append[@]}")
463 if [[ "$INITRD" ]] && ! get_bool
"$SKIP_INITRD"; then
464 qemu_options
+=(-initrd "$INITRD")
467 # Let's use KVM if possible
468 if [[ -c /dev
/kvm
]] && get_bool
$QEMU_KVM; then
469 qemu_options
+=(-machine "accel=kvm" -enable-kvm -cpu host)
472 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
473 qemu_cmd
=(timeout
--foreground "$QEMU_TIMEOUT" "$QEMU_BIN")
476 (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]}")
478 if [ "$rc" -eq 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
479 derror
"Test timed out after ${QEMU_TIMEOUT}s"
482 [ "$rc" != 0 ] && derror
"QEMU failed with exit code $rc"
487 # Return 0 if nspawn did run (then you must check the result state/logs for actual
488 # success), or 1 if nspawn is not available.
490 [[ -d /run
/systemd
/system
]] ||
return 1
491 rm -f "${initdir:?}"/{testok
,failed
,skipped
}
494 local nspawn_options
=(
496 "--kill-signal=SIGKILL"
498 "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
500 local kernel_params
=(
502 "systemd.unit=testsuite.target"
503 "systemd.wants=testsuite-$2.service"
506 if ! get_bool
"$INTERACTIVE_DEBUG"; then
507 kernel_params
+=("systemd.wants=end.service")
510 if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then
511 local user_nspawn_arguments
512 read -ra user_nspawn_arguments
<<< "$NSPAWN_ARGUMENTS"
513 nspawn_options
+=("${user_nspawn_arguments[@]}")
516 if [[ -n "${KERNEL_APPEND:=}" ]]; then
517 local user_kernel_append
518 read -ra user_kernel_append
<<< "$KERNEL_APPEND"
519 kernel_params
+=("${user_kernel_append[@]}")
522 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
523 dwarn
"nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
525 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ||
"$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
526 nspawn_cmd
+=(env
"SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY")
527 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
528 nspawn_cmd
+=(env
"--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY")
530 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
534 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
535 nspawn_cmd
+=(timeout
--foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN")
537 nspawn_cmd
+=("$SYSTEMD_NSPAWN")
540 (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}")
542 if [ "$rc" -eq 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
543 derror
"Test timed out after ${NSPAWN_TIMEOUT}s"
546 [ "$rc" != 0 ] && derror
"nspawn failed with exit code $rc"
551 # Build two very minimal root images, with two units, one is the same and one is different across them
552 install_verity_minimal
() {
553 dinfo
"Set up a set of minimal images for verity verification"
554 if [ -e "$initdir/usr/share/minimal.raw" ]; then
557 if ! command -v mksquashfs
>/dev
/null
2>&1; then
558 dfatal
"mksquashfs not found"
561 if ! command -v veritysetup
>/dev
/null
2>&1; then
562 dfatal
"veritysetup not found"
565 # Local modifications of some global variables is intentional in this
567 # shellcheck disable=SC2030
576 oldinitdir
="$initdir"
577 rm -rfv "$TESTDIR/minimal"
578 export initdir
="$TESTDIR/minimal"
579 # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
580 mkdir
-p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
583 # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
584 # issue, thus falsely triggering SC2030 in this case
585 # See: koalaman/shellcheck#1409
586 if [[ -v ASAN_RT_PATH
]]; then
587 # If we're compiled with ASan, install the ASan RT (and its dependencies)
588 # into the verity images to get rid of the annoying errors about
589 # missing $LD_PRELOAD libraries.
590 inst_libs
"$ASAN_RT_PATH"
591 inst_library
"$ASAN_RT_PATH"
592 # Create a dummy LSan suppression file otherwise gcc's ASan
593 # complains as it doesn't exist in the minimal image
594 # (i.e. when running TEST-29 or TEST-50 under sanitizers)
595 touch "$initdir/systemd-lsan.supp"
597 cp "$os_release" "$initdir/usr/lib/os-release"
598 ln -s ..
/usr
/lib
/os-release
"$initdir/etc/os-release"
599 touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
600 touch "$initdir/opt/some_file"
601 echo MARKER
=1 >>"$initdir/usr/lib/os-release"
602 echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
603 cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
605 ExecStartPre=cat /usr/lib/os-release
608 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
610 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
611 veritysetup format
"$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
612 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
614 sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
615 rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
616 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
618 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
619 veritysetup format
"$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
620 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
622 # Rolling distros like Arch do not set VERSION_ID
624 if grep -q "^VERSION_ID=" "$os_release"; then
625 version_id
="$(grep "^VERSION_ID
=" "$os_release")"
628 export initdir
="$TESTDIR/app0"
629 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
630 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
631 echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
632 cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
636 ExecStart=/opt/script0.sh
637 TemporaryFileSystem=/var/lib
639 RuntimeDirectory=app0
641 cat >"$initdir/opt/script0.sh" <<EOF
644 test -e /usr/lib/os-release
645 echo bar > \${STATE_DIRECTORY}/foo
646 cat /usr/lib/extension-release.d/extension-release.app0
648 chmod +x
"$initdir/opt/script0.sh"
649 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/some_file"
650 mksquashfs
"$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
652 export initdir
="$TESTDIR/app1"
653 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
654 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
655 ( echo "${version_id}"
656 echo "SYSEXT_SCOPE=portable"
657 echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
658 setfattr
-n user.extension-release.strict
-v false
"$initdir/usr/lib/extension-release.d/extension-release.app2"
659 cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
663 ExecStart=/opt/script1.sh
665 RuntimeDirectory=app1
667 cat >"$initdir/opt/script1.sh" <<EOF
670 test -e /usr/lib/os-release
671 echo baz > \${STATE_DIRECTORY}/foo
672 cat /usr/lib/extension-release.d/extension-release.app2
674 chmod +x
"$initdir/opt/script1.sh"
675 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/other_file"
676 mksquashfs
"$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
680 setup_basic_environment
() {
681 # create the basic filesystem layout
685 install_missing_libraries
704 has_user_dbus_socket
&& install_user_dbus
709 generate_module_dependencies
710 if get_bool
"$IS_BUILT_WITH_ASAN"; then
713 if get_bool
"$TEST_INSTALL_VERITY_MINIMAL"; then
714 install_verity_minimal
719 dinfo
"Setup SELinux"
720 # don't forget KERNEL_APPEND='... selinux=1 ...'
721 if ! get_bool
"$SETUP_SELINUX"; then
722 dinfo
"SETUP_SELINUX != yes, skipping SELinux configuration"
726 local conf_dir
=/etc
/selinux
727 local fixfiles_tools
=(bash uname
cat sort uniq awk grep egrep head expr find rm secon setfiles
)
729 # Make sure the following statement can't expand to "/" to prevent
730 # a potential where-are-my-backups situation
731 rm -rf "${initdir:?}/$conf_dir"
732 if ! cp -ar "$conf_dir" "$initdir/$conf_dir"; then
733 dfatal
"Failed to copy $conf_dir"
737 touch "$initdir/.autorelabel"
738 mkdir
-p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
739 ln -sf ..
/autorelabel.service
"$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
741 image_install
"${fixfiles_tools[@]}"
742 image_install fixfiles
743 image_install sestatus
747 if ! type -p valgrind
; then
748 dfatal
"Failed to install valgrind"
752 local valgrind_bins valgrind_libs valgrind_dbg_and_supp
754 valgrind_bins
="$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^
"]+)"/')"
755 image_install "$valgrind_bins"
757 valgrind_libs="$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print
$1 if m
{calling init
: (/.
*vgpreload_.
*)}')"
758 image_install "$valgrind_libs"
760 valgrind_dbg_and_supp="$(
761 strace -e open valgrind /bin/true 2>&1 >/dev/null |
762 perl -lne 'if (my
($fname) = /^open\
("([^"]+).
*= (?
!-)\d
+/) { print
$fname if $fname =~
/debug|\.supp$
/ }'
764 image_install "$valgrind_dbg_and_supp"
767 create_valgrind_wrapper() {
768 local valgrind_wrapper="$initdir/$ROOTLIBDIR/systemd-under-valgrind"
769 ddebug "Create $valgrind_wrapper"
770 cat >"$valgrind_wrapper" <<EOF
773 mount -t proc proc /proc
774 exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
776 chmod 0755 "$valgrind_wrapper"
779 create_asan_wrapper() {
780 local asan_wrapper="$initdir/$ROOTLIBDIR/systemd-under-asan"
781 dinfo "Create ASan wrapper as '$asan_wrapper'"
783 [[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be
"
785 # clang: install llvm-symbolizer to generate useful reports
786 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
787 [[ "$ASAN_COMPILER" == "clang
" ]] && image_install "llvm-symbolizer
"
789 cat >"$asan_wrapper" <<EOF
794 echo "ASan RT
: $ASAN_RT_PATH"
795 if [[ ! -e "$ASAN_RT_PATH" ]]; then
796 echo >&2 "Couldn
't find ASan RT at '$ASAN_RT_PATH', can't
continue"
800 # Suppress certain leaks reported by LSan (either in external tools or bogus
802 # Docs: # https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions
804 # - fsck is called by systemd-homed and is reporting a leak we're not interested
806 # - libLLVM is a "side effect
" caused by the previous fsck leak
807 cat >/systemd-lsan.supp <<INNER_EOF
813 DEFAULT_LSAN_OPTIONS=${LSAN_OPTIONS:-}:suppressions=/systemd-lsan.supp
814 DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
815 DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
816 DEFAULT_ENVIRONMENT="ASAN_OPTIONS
=\
$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS
=\
$DEFAULT_UBSAN_OPTIONS LSAN_OPTIONS
=\
$DEFAULT_LSAN_OPTIONS"
818 # As right now bash is the PID 1, we can't expect PATH to have a sane value.
819 # Let's make one to prevent unexpected "<bin
> not found
" issues in the future
820 export PATH="/sbin
:/bin
:/usr
/sbin
:/usr
/bin
"
822 mount -t proc proc /proc
823 mount -t sysfs sysfs /sys
824 mount -o remount,rw /
826 # A lot of services (most notably dbus) won't start without preloading libasan
827 # See https://github.com/systemd/systemd/issues/5004
828 DEFAULT_ENVIRONMENT="\
$DEFAULT_ENVIRONMENT LD_PRELOAD
=$ASAN_RT_PATH"
830 if [[ "$ASAN_COMPILER" == "clang
" ]]; then
831 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
832 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
833 # runtime ASan DSO is in a non-standard (library) path.
834 echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf
837 echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
838 echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
839 echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
841 # ASAN and syscall filters aren't compatible with each other.
842 find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
844 # The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
845 # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
846 JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
847 mkdir -p "\
$JOURNALD_CONF_DIR"
848 printf "[Service
]\nEnvironment
=ASAN_OPTIONS
=\
$DEFAULT_ASAN_OPTIONS:log_path
=/systemd-journald.asan.log UBSAN_OPTIONS
=\
$DEFAULT_UBSAN_OPTIONS:log_path
=/systemd-journald.ubsan.log
\n" >"\
$JOURNALD_CONF_DIR/env.conf
"
850 # Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
851 # Let's try to catch them by redirecting stderr (and stdout just in case) to a file
852 # See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
853 printf "[Service
]\nStandardOutput
=file:/systemd-journald.out
\n" >"\
$JOURNALD_CONF_DIR/out.conf
"
855 # 90s isn't enough for some services to finish when literally everything is run
856 # under ASan+UBSan in containers, which, in turn, are run in VMs.
857 # Let's limit which environments such services should be executed in.
858 mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
859 printf "[Unit
]\nConditionVirtualization
=container
\n\n[Service
]\nTimeoutSec
=240s
\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
861 # Let's override another hard-coded timeout that kicks in too early
862 mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
863 printf "[Service
]\nTimeoutSec
=180s
\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf
865 # D-Bus has troubles during system shutdown causing it to fail. Although it's
866 # harmless, it causes unnecessary noise in the logs, so let's disable LSan's
867 # at_exit check just for the dbus.service
868 mkdir -p /etc/systemd/system/dbus.service.d
869 printf "[Service
]\nEnvironment
=ASAN_OPTIONS
=leak_check_at_exit
=false
\n" >/etc/systemd/system/dbus.service.d/disable-lsan.conf
871 # Some utilities run via IMPORT/RUN/PROGRAM udev directives fail because
872 # they're uninstrumented (like dmsetup). Let's add a simple rule which sets
873 # LD_PRELOAD to the ASan RT library to fix this.
874 mkdir -p /etc/udev/rules.d
875 cat >/etc/udev/rules.d/00-set-LD_PRELOAD.rules <<INNER_EOF
876 SUBSYSTEM=="block
", ENV{LD_PRELOAD}="$ASAN_RT_PATH"
878 chmod 0644 /etc/udev/rules.d/00-set-LD_PRELOAD.rules
880 # The 'mount' utility doesn't behave well under libasan, causing unexpected
881 # fails during boot and subsequent test results check:
882 # bash-5.0# mount -o remount,rw -v /
883 # mount: /dev/sda1 mounted on /.
886 # Let's workaround this by clearing the previously set LD_PRELOAD env variable,
887 # so the libasan library is not loaded for this particular service
889 local _dropin_dir="/etc
/systemd
/system
/\
$1.service.d
"
890 mkdir -p "\
$_dropin_dir"
891 printf "[Service
]\nUnsetEnvironment
=LD_PRELOAD
\n" >"\
$_dropin_dir/unset_ld_preload.conf
"
894 unset_ld_preload systemd-remount-fs
895 unset_ld_preload testsuite-
897 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
898 exec "$ROOTLIBDIR/systemd
" "\$@
"
901 chmod 0755 "$asan_wrapper"
904 create_strace_wrapper() {
905 local strace_wrapper="$initdir/$ROOTLIBDIR/systemd-under-strace
"
906 ddebug "Create
$strace_wrapper"
907 cat >"$strace_wrapper" <<EOF
910 exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd
" "\$@
"
912 chmod 0755 "$strace_wrapper"
917 image_install /sbin/fsck*
918 image_install -o /bin/fsck*
920 # fskc.reiserfs calls reiserfsck. so, install it
921 image_install -o reiserfsck
923 # we use mkfs in system-repart tests
924 image_install /sbin/mkfs.ext4
925 image_install /sbin/mkfs.vfat
929 dinfo "Install modules
"
933 instmods nls_ascii =nls
936 if get_bool "$LOOKS_LIKE_SUSE"; then
942 instmods dm_crypt =crypto
944 image_install "${ROOTLIBDIR:?}"/system/dm-event.{service,socket}
945 if get_bool "$LOOKS_LIKE_DEBIAN"; then
946 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
947 # and since buster/bionic 95-dm-notify.rules
948 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
949 inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
951 inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
953 if get_bool "$LOOKS_LIKE_SUSE"; then
954 inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules
958 install_multipath() {
959 instmods "=md
" multipath
960 image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
961 image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
962 if get_bool "$LOOKS_LIKE_DEBIAN"; then
963 inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
965 inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
967 mkdir -p "${initdir:?}/etc
/multipath
"
970 while read -r file; do
971 # Install libraries required by the given library
973 # Install the library itself and create necessary symlinks
975 done < <(find /lib*/multipath -type f)
977 if get_bool "$LOOKS_LIKE_ARCH"; then
978 # On Arch the multipath libraries are not linked against libgcc_s.so.1,
979 # but it's still required at runtime
980 inst_library "/lib64
/libgcc_s.so
.1"
986 image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket}
987 image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service
988 image_install -o "/lib
/tmpfiles.d
/lvm2.conf
"
989 if get_bool "$LOOKS_LIKE_DEBIAN"; then
990 inst_rules 56-lvm.rules 69-lvm-metad.rules
992 # Support the new udev autoactivation introduced in lvm 2.03.14
993 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
994 # Static autoactivation (via lvm2-activation-generator) was dropped
996 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
997 if [[ -f /lib/udev/rules.d/69-dm-lvm.rules ]]; then
998 inst_rules 11-dm-lvm.rules 69-dm-lvm.rules
1000 image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
1001 image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
1002 inst_rules 11-dm-lvm.rules 69-dm-lvm-metad.rules
1005 mkdir -p "${initdir:?}/etc
/lvm
"
1010 # Not all utilities provided by btrfs-progs are listed here; extend the list
1012 image_install btrfs btrfstune mkfs.btrfs
1013 inst_rules 64-btrfs-dm.rules
1017 # Install both client and server side stuff by default
1021 # Install client-side stuff ("initiator
" in iSCSI jargon) - Open-iSCSI in this case
1022 # (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
1023 if [[ -z "$inst" || "$inst" =~ (client|initiator) ]]; then
1024 image_install iscsi-iname iscsiadm iscsid iscsistart
1025 image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service
1026 image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket}
1027 image_install "${ROOTLIBDIR:?}"/system/iscsi.service
1028 mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
1029 mkdir -p "${initdir:?}/etc
/iscsi
"
1030 echo "iscsid.startup
= /bin
/systemctl start iscsid.socket
" >"${initdir:?}/etc
/iscsi
/iscsid.conf
"
1031 inst_simple "/etc
/iscsi
/initiatorname.iscsi
"
1034 # Install server-side stuff ("target
" in iSCSI jargon) - TGT in this case
1035 # (tgt on Debian, scsi-target-utils on Fedora, etc.)
1036 if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then
1037 image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
1038 image_install -o /etc/sysconfig/tgtd
1039 image_install "${ROOTLIBDIR:?}"/system/tgtd.service
1040 mkdir -p "${initdir:?}/etc
/tgt
"
1041 touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf
1042 # Install perl modules required by tgt-admin
1044 # Forgive me father for I have sinned. The monstrosity below appends
1045 # a perl snippet to the `tgt-admin` perl script on the fly, which
1046 # dumps a list of files (perl modules) required by `tgt-admin` at
1047 # the runtime plus any DSOs loaded via DynaLoader. This list is then
1048 # passed to `inst_simple` which installs the necessary files into the image
1050 # shellcheck disable=SC2016
1051 while read -r file; do
1053 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 }')
1057 install_compiled_systemd() {
1058 dinfo "Install compiled systemd
"
1061 ninja_bin="$
(type -P ninja ||
type -P ninja-build
)"
1062 if [[ -z "$ninja_bin" ]]; then
1063 dfatal "ninja was not found
"
1066 (set -x; DESTDIR="$initdir" "$ninja_bin" -C "$BUILD_DIR" install)
1068 # If we are doing coverage runs, copy over the binary notes files, as lcov expects to
1069 # find them in the same directory as the runtime data counts
1070 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
1071 mkdir -p "${initdir}/${BUILD_DIR:?}/"
1072 rsync -am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/"
1076 install_debian_systemd() {
1077 dinfo "Install debian systemd
"
1081 while read -r deb; do
1082 files="$
(dpkg-query
-L "$deb" 2>/dev
/null
)" || continue
1083 ddebug "Install debian files from package
$deb"
1084 for file in $files; do
1085 [ -e "$file" ] || continue
1086 [ -d "$file" ] && continue
1089 done < <(grep -E '^Package:' "${SOURCE_DIR}/debian
/control
" | cut -d ':' -f 2)
1092 install_suse_systemd() {
1093 local testsdir=/usr/lib/systemd/tests
1096 dinfo "Install SUSE systemd
"
1102 systemd-experimental
1103 systemd-journal-remote
1108 for p in "${pkgs[@]}"; do
1109 rpm -q "$p" &>/dev/null || continue
1111 ddebug "Install files from package
$p"
1113 [ -e "$f" ] || continue
1114 [ -d "$f" ] && continue
1116 done < <(rpm -ql "$p")
1119 # we only need testsdata dir as well as the unit tests (for
1120 # TEST-02-UNITTESTS) in the image.
1121 dinfo "Install unit tests and testdata directory
"
1123 mkdir -p "$initdir/$testsdir"
1124 cp "$testsdir"/test-* "$initdir/$testsdir/"
1125 cp -a "$testsdir/testdata
" "$initdir/$testsdir/"
1127 # On openSUSE, these dirs are not created at package install for now on.
1128 mkdir -p "$initdir/var
/log
/journal
/remote
"
1131 install_distro_systemd() {
1132 dinfo "Install distro systemd
"
1134 if get_bool "$LOOKS_LIKE_DEBIAN"; then
1135 install_debian_systemd
1136 elif get_bool "$LOOKS_LIKE_SUSE"; then
1137 install_suse_systemd
1139 dfatal "NO_BUILD not supported
for this distro
"
1145 dinfo "Install systemd
"
1146 if get_bool "$NO_BUILD"; then
1147 install_distro_systemd
1149 install_compiled_systemd
1152 # remove unneeded documentation
1153 rm -fr "${initdir:?}"/usr/share/{man,doc}
1155 # enable debug logging in PID1
1156 echo LogLevel=debug >>"$initdir/etc
/systemd
/system.conf
"
1157 # store coredumps in journal
1158 echo Storage=journal >>"$initdir/etc
/systemd
/coredump.conf
"
1159 # Propagate SYSTEMD_UNIT_PATH to user systemd managers
1160 mkdir "$initdir/etc
/systemd
/system
/user@.service.d
/"
1161 echo -e "[Service
]\nPassEnvironment
=SYSTEMD_UNIT_PATH
\n" >"$initdir/etc
/systemd
/system
/user@.service.d
/override.conf
"
1163 # When built with gcov, disable ProtectSystem= and ProtectHome in the test
1164 # images, since it prevents gcov to write the coverage reports (*.gcda files)
1165 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
1166 mkdir -p "$initdir/etc
/systemd
/system
/service.d
/"
1167 echo -e "[Service
]\nProtectSystem
=no
\nProtectHome
=no
\n" >"$initdir/etc
/systemd
/system
/service.d
/gcov-override.conf
"
1173 rpath="$
(objdump
-p "${1:?}" 2>/dev
/null |
awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" |
paste -sd :)"
1175 if [ -z "$rpath" ] ; then
1182 install_missing_libraries() {
1183 dinfo "Install missing libraries
"
1184 # install possible missing libraries
1185 for i in "${initdir:?}"{,/usr}/{sbin,bin}/* "$initdir"{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
1186 LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$
(get_ldpath
"$i")" inst_libs "$i"
1190 # A number of dependencies is now optional via dlopen, so the install
1191 # script will not pick them up, since it looks at linkage.
1192 for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf libelf libdw; do
1193 ddebug "Searching
for $lib via pkg-config
"
1194 if pkg-config --exists "$lib"; then
1195 path="$
(pkg-config
--variable=libdir
"$lib")"
1196 if [ -z "${path}" ]; then
1197 ddebug "$lib.pc does not contain a libdir variable
, skipping
"
1201 if ! [[ ${lib} =~ ^lib ]]; then
1204 # Some pkg-config files are broken and give out the wrong paths
1205 # (eg: libcryptsetup), so just ignore them
1206 inst_libs "${path}/${lib}.so
" || true
1207 inst_library "${path}/${lib}.so
" || true
1209 ddebug "$lib.pc not found
, skipping
"
1216 if [ -n "${LOOPDEV:=}" ]; then
1217 ddebug "losetup
-d $LOOPDEV"
1218 losetup -d "${LOOPDEV}"
1223 trap cleanup_loopdev EXIT INT QUIT PIPE
1225 create_empty_image() {
1226 if [ -z "${IMAGE_NAME:=}" ]; then
1227 echo "create_empty_image
: \
$IMAGE_NAME not
set"
1232 if ! get_bool "$NO_BUILD"; then
1233 if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
1236 if meson configure "${BUILD_DIR:?}" | grep 'link-.*-shared' | awk '{ print $2 }' | grep -q 'false'; then
1239 if get_bool "$IS_BUILT_WITH_COVERAGE"; then
1243 if ! get_bool "$STRIP_BINARIES"; then
1247 echo "Setting up
${IMAGE_PUBLIC:?} (${size} MB
)"
1248 rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
1250 # Create the blank file to use as a root filesystem
1251 truncate -s "${size}M
" "$IMAGE_PUBLIC"
1253 LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
1254 [ -b "$LOOPDEV" ] || return 1
1255 sfdisk "$LOOPDEV" <<EOF
1262 local label=(-L systemd_boot)
1263 # mkfs.reiserfs doesn't know -L. so, use --label instead
1264 [[ "$FSTYPE" == "reiserfs
" ]] && label=(--label systemd_boot)
1265 if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1
" -q; then
1266 dfatal "Failed to mkfs
-t ${FSTYPE}"
1272 if [ -z "${LOOPDEV:=}" ]; then
1273 [ -e "${IMAGE_PRIVATE:?}" ] && image="$IMAGE_PRIVATE" || image="${IMAGE_PUBLIC:?}"
1274 LOOPDEV="$
(losetup
--show -P -f "$image")"
1275 [ -b "$LOOPDEV" ] || return 1
1280 if ! mountpoint -q "${initdir:?}"; then
1282 mount "${LOOPDEV}p1
" "$initdir"
1283 TEST_SETUP_CLEANUP_ROOTDIR=1
1288 # only umount if create_empty_image_rootdir() was called to mount it
1289 get_bool "$TEST_SETUP_CLEANUP_ROOTDIR" && _umount_dir "${initdir:?}"
1293 # unmount the loopback device from all places. Otherwise we risk file
1294 # system corruption.
1295 for device in $(losetup -l | awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do
1296 ddebug "Unmounting all uses of
$device"
1297 mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
1301 create_empty_image_rootdir() {
1306 check_asan_reports() {
1310 if get_bool "$IS_BUILT_WITH_ASAN"; then
1312 if [[ -e "$root/systemd.asan.log
.1" ]]; then
1313 cat "$root/systemd.asan.log
.1"
1317 journald_report="$
(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \
;)"
1318 if [[ -n "$journald_report" ]]; then
1319 printf "%s
\n" "$journald_report"
1320 cat "$root/systemd-journald.out
" || :
1325 "$JOURNALCTL" -D "$root/var/log/journal" | perl
-alne '
1327 %services_to_ignore = (
1328 "dbus-daemon" => undef,
1331 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
1333 if [[ -n "$pids" ]]; then
1335 for pid in $pids; do
1336 "$JOURNALCTL" -D "$root/var
/log
/journal
" _PID="$pid" --no-pager
1344 check_coverage_reports() {
1347 if get_bool "$NO_BUILD"; then
1350 if ! get_bool "$IS_BUILT_WITH_COVERAGE"; then
1354 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1355 dest="${ARTIFACT_DIRECTORY}/${testname:?}.coverage-info
"
1357 dest="${TESTDIR:?}/coverage-info
"
1360 # Create a coverage report that will later be uploaded. Remove info about
1361 # system libraries/headers, as we don't really care about them.
1362 if [[ -f "$dest" ]]; then
1363 # If the destination report file already exists, don't overwrite it, but
1364 # dump the new report in a temporary file and then merge it with the already
1365 # present one - this usually happens when running both "parts
" of a test
1366 # in one run (the qemu and the nspawn part).
1367 lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new
"
1368 lcov --remove "${dest}.new
" -o "${dest}.new
" '/usr/include/*' '/usr/lib/*'
1369 lcov --add-tracefile "${dest}" --add-tracefile "${dest}.new" -o "${dest}"
1372 lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}"
1373 lcov --remove "${dest}" -o "${dest}" '/usr/include/*' '/usr/lib/*'
1380 # Default to always saving journal
1383 if [ "${TEST_SAVE_JOURNAL}" = "no
" ]; then
1385 elif [ "${TEST_SAVE_JOURNAL}" = "fail
" ] && [ "$2" = "0" ]; then
1389 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1390 dest="${ARTIFACT_DIRECTORY}/${testname:?}.journal
"
1392 dest="${TESTDIR:?}/system.journal
"
1395 for j in "${1:?}"/*; do
1396 if get_bool "$save"; then
1397 "$SYSTEMD_JOURNAL_REMOTE" -o "$dest" --getter="$JOURNALCTL -o export -D $j"
1400 if [ -n "${TEST_SHOW_JOURNAL}" ]; then
1402 "$JOURNALCTL" --no-pager -o short-monotonic --no-hostname --priority="${TEST_SHOW_JOURNAL}" -D "$j"
1408 if ! get_bool "$save"; then
1412 if [ -n "${SUDO_USER}" ]; then
1413 setfacl -m "user
:${SUDO_USER:?}:r-X
" "$dest"*
1416 # we want to print this sometime later, so save this in a variable
1417 JOURNAL_LIST="$
(ls -l "$dest"*)"
1420 check_result_common() {
1421 local workspace="${1:?}"
1424 if [ -s "$workspace/failed
" ]; then
1425 # Non-empty …/failed has highest priority
1426 cp -a "$workspace/failed
" "${TESTDIR:?}/"
1427 if [ -n "${SUDO_USER}" ]; then
1428 setfacl -m "user
:${SUDO_USER:?}:r-X
" "${TESTDIR:?}/"failed
1431 elif get_bool "$TIMED_OUT"; then
1432 echo "(timeout
)" >"${TESTDIR:?}/failed
"
1434 elif [ -e "$workspace/testok
" ]; then
1435 # …/testok always counts (but with lower priority than …/failed)
1437 elif [ -e "$workspace/skipped
" ]; then
1438 # …/skipped always counts (a message is expected)
1439 echo "${TESTNAME:?} was skipped
:"
1440 cat "$workspace/skipped
"
1443 echo "(failed
; see logs
)" >"${TESTDIR:?}/failed
"
1447 check_asan_reports "$workspace" || ret=4
1449 check_coverage_reports "$workspace" || ret=5
1451 save_journal "$workspace/var
/log
/journal
" $ret
1453 if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out
" ]; then
1454 cp "$workspace/strace.out
" "${ARTIFACT_DIRECTORY}/"
1457 if [ ${ret:?} != 0 ] && [ -f "$TESTDIR/failed
" ]; then
1458 echo -n "${TESTNAME:?}: "
1459 cat "$TESTDIR/failed
"
1461 echo "${JOURNAL_LIST:-"No journals were saved"}"
1466 check_result_nspawn() {
1467 local workspace="${1:?}"
1470 check_result_common "${workspace}"
1473 # Run additional test-specific checks if defined by check_result_nspawn_hook()
1474 if declare -F check_result_nspawn_hook >/dev/null; then
1475 if ! check_result_nspawn_hook; then
1476 derror "check_result_nspawn_hook
() returned with EC
> 0"
1481 _umount_dir "${initdir:?}"
1486 # can be overridden in specific test
1487 check_result_qemu() {
1491 check_result_common "${initdir:?}"
1494 _umount_dir "${initdir:?}"
1496 # Run additional test-specific checks if defined by check_result_qemu_hook()
1497 if declare -F check_result_qemu_hook >/dev/null; then
1498 if ! check_result_qemu_hook; then
1499 derror "check_result_qemu_hook
() returned with EC
> 0"
1507 check_result_nspawn_unittests() {
1508 local workspace="${1:?}"
1511 [[ -e "$workspace/testok
" ]] && ret=0
1513 if [[ -s "$workspace/failed
" ]]; then
1515 echo "=== Failed
test log
==="
1516 cat "$workspace/failed
"
1518 if [[ -s "$workspace/skipped
" ]]; then
1519 echo "=== Skipped
test log
=="
1520 cat "$workspace/skipped
"
1521 # We might have only skipped tests - that should not fail the job
1524 if [[ -s "$workspace/testok
" ]]; then
1525 echo "=== Passed tests
==="
1526 cat "$workspace/testok
"
1530 get_bool "${TIMED_OUT:=}" && ret=1
1531 check_coverage_reports "$workspace" || ret=5
1533 save_journal "$workspace/var
/log
/journal
" $ret
1535 _umount_dir "${initdir:?}"
1540 check_result_qemu_unittests() {
1544 [[ -e "${initdir:?}/testok
" ]] && ret=0
1546 if [[ -s "$initdir/failed
" ]]; then
1548 echo "=== Failed
test log
==="
1549 cat "$initdir/failed
"
1551 if [[ -s "$initdir/skipped
" ]]; then
1552 echo "=== Skipped
test log
=="
1553 cat "$initdir/skipped
"
1554 # We might have only skipped tests - that should not fail the job
1557 if [[ -s "$initdir/testok
" ]]; then
1558 echo "=== Passed tests
==="
1559 cat "$initdir/testok
"
1563 get_bool "${TIMED_OUT:=}" && ret=1
1564 check_coverage_reports "$initdir" || ret=5
1566 save_journal "$initdir/var
/log
/journal
" $ret
1568 _umount_dir "$initdir"
1574 dinfo "Strip binaries
"
1575 if ! get_bool "$STRIP_BINARIES"; then
1576 dinfo "STRIP_BINARIES
== no
, keeping binaries unstripped
"
1579 while read -r bin; do
1580 strip --strip-unneeded "$bin" |& grep -vi 'file format not recognized' | ddebug || :
1581 done < <(find "${initdir:?}" -executable -not -path '*/lib/modules/*.ko' -type f)
1585 dinfo "Create rc.
local"
1586 mkdir -p "${initdir:?}/etc
/rc.d
"
1587 cat >"$initdir/etc
/rc.d
/rc.
local" <<EOF
1591 chmod 0755 "$initdir/etc
/rc.d
/rc.
local"
1595 ddebug "Install executables from the service files
"
1597 local pkg_config_path="${BUILD_DIR:?}/src
/core
/"
1598 local systemunitdir userunitdir exe
1599 systemunitdir="$
(PKG_CONFIG_PATH
="$pkg_config_path" pkg-config
--variable=systemdsystemunitdir systemd
)"
1600 userunitdir="$
(PKG_CONFIG_PATH
="$pkg_config_path" pkg-config
--variable=systemduserunitdir systemd
)"
1601 while read -r exe; do
1602 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
1603 # also, plymouth is pulled in by rescue.service, but even there the exit code
1604 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
1605 dinfo "Attempting to
install $exe (based on unit
file reference
)"
1606 inst "$exe" || [ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth
" ]
1607 done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service | sort -u)
1610 generate_module_dependencies() {
1611 dinfo "Generate modules dependencies
"
1612 if [[ -d "${initdir:?}/lib
/modules
/${KERNEL_VER:?}" ]] && \
1613 ! depmod -a -b "$initdir" "$KERNEL_VER"; then
1614 dfatal "\"depmod
-a $KERNEL_VER\" failed.
"
1619 install_depmod_files() {
1620 dinfo "Install depmod files
"
1621 inst "/lib
/modules
/${KERNEL_VER:?}/modules.order
"
1622 inst "/lib
/modules
/$KERNEL_VER/modules.
builtin"
1625 install_plymouth() {
1626 dinfo "Install plymouth
"
1627 # install plymouth, if found... else remove plymouth service files
1628 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
1629 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions
" \
1630 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
1631 # image_install plymouth plymouthd
1633 rm -f "${initdir:?}"/{usr/lib,lib,etc}/systemd/system/plymouth* "$initdir"/{usr/lib,lib,etc}/systemd/system/*/plymouth*
1638 # If haveged is installed, it's probably included in initrd and needs to be
1639 # installed in the image too.
1640 if [ -x /usr/sbin/haveged ]; then
1641 dinfo "Install haveged files
"
1642 inst /usr/sbin/haveged
1643 for u in /usr/lib/systemd/system/haveged*; do
1649 install_ld_so_conf() {
1650 dinfo "Install
/etc
/ld.so.conf
*"
1651 cp -a /etc/ld.so.conf* "${initdir:?}/etc
"
1652 ldconfig -r "$initdir"
1655 install_testuser() {
1656 dinfo "Set up a
test user
"
1657 # create unprivileged user for user manager tests
1658 mkdir -p "${initdir:?}/etc
/sysusers.d
"
1659 cat >"$initdir/etc
/sysusers.d
/testuser.conf
" <<EOF
1660 u testuser 4711 "Test User
" /home/testuser
1663 mkdir -p "$initdir/home
/testuser
"
1664 chmod 0700 "$initdir/home
/testuser
"
1665 chown 4711:4711 "$initdir/home
/testuser
"
1668 install_config_files() {
1669 dinfo "Install config files
"
1670 inst /etc/sysconfig/init || :
1673 inst_any /etc/login.defs /usr/etc/login.defs
1676 inst_any /etc/nsswitch.conf /usr/etc/nsswitch.conf
1677 inst /etc/pam.conf || :
1678 inst_any /etc/os-release /usr/lib/os-release
1680 # we want an empty environment
1681 : >"${initdir:?}/etc
/environment
"
1682 : >"$initdir/etc
/machine-id
"
1683 : >"$initdir/etc
/resolv.conf
"
1686 echo 'H' >"$initdir/etc
/hostname
"
1688 # let's set up just one image with the traditional verbose output
1689 if [ "${IMAGE_NAME:?}" != "basic
" ]; then
1690 mkdir -p "$initdir/etc
/systemd
/system.conf.d
"
1691 echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc
/systemd
/system.conf.d
/status.conf
"
1695 install_basic_tools() {
1696 dinfo "Install basic tools
"
1697 image_install "${BASICTOOLS[@]}"
1698 image_install -o sushell
1699 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
1700 image_install -o ldconfig.real
1703 install_debug_tools() {
1704 dinfo "Install debug tools
"
1705 image_install -o "${DEBUGTOOLS[@]}"
1707 if get_bool "$INTERACTIVE_DEBUG"; then
1708 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
1709 local getty_override="${initdir:?}/etc
/systemd
/system
/serial-getty@.service.d
"
1710 mkdir -p "$getty_override"
1711 echo -e "[Service
]\nEnvironment
=TERM
=linux
" >"$getty_override/default-TERM.conf
"
1713 cat >"$initdir/etc
/motd
" <<EOF
1714 To adjust the terminal size use:
1718 stty cols xx rows yy
1724 dinfo "Install libnss
"
1725 # install libnss_files for login
1727 mapfile -t NSS_LIBS < <(LD_DEBUG=files getent passwd 2>&1 >/dev/null | sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
1728 image_install "${NSS_LIBS[@]}"
1732 dinfo "Install dbus
"
1733 inst "${ROOTLIBDIR:?}/system
/dbus.socket
"
1735 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1736 if [ -f "$ROOTLIBDIR/system
/dbus-broker.service
" ]; then
1737 inst "$ROOTLIBDIR/system
/dbus-broker.service
"
1738 inst_symlink /etc/systemd/system/dbus.service
1739 inst /usr/bin/dbus-broker
1740 inst /usr/bin/dbus-broker-launch
1741 elif [ -f "$ROOTLIBDIR/system
/dbus-daemon.service
" ]; then
1742 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1743 inst "$ROOTLIBDIR/system
/dbus-daemon.service
"
1745 inst_symlink /etc/systemd/system/dbus.service
1747 inst "$ROOTLIBDIR/system
/dbus.service
"
1750 while read -r file; do
1752 done < <(find /etc/dbus-1 /usr/share/dbus-1 -xtype f 2>/dev/null)
1754 # setup policy for Type=dbus test
1755 mkdir -p "${initdir:?}/etc
/dbus-
1/system.d
"
1756 cat >"$initdir/etc
/dbus-
1/system.d
/systemd.
test.ExecStopPost.conf
" <<EOF
1757 <?xml version="1.0"?>
1758 <!DOCTYPE busconfig PUBLIC "-//freedesktop
//DTD D-BUS Bus Configuration
1.0//EN
"
1759 "http
://www.freedesktop.org
/standards
/dbus
/1.0/busconfig.dtd
">
1761 <policy user="root
">
1762 <allow own="systemd.
test.ExecStopPost
"/>
1768 install_user_dbus() {
1769 dinfo "Install user dbus
"
1771 if ! userunitdir="$
(pkg-config
--variable=systemduserunitdir systemd
)"; then
1772 dwarn "WARNING
! Cannot determine userunitdir from pkg-config
, assuming
/usr
/lib
/systemd
/user
"
1773 userunitdir=/usr/lib/systemd/user
1776 inst "$userunitdir/dbus.socket
"
1777 inst_symlink "$userunitdir/sockets.target.wants
/dbus.socket
" || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket
1779 # Append the After= dependency on dbus in case it isn't already set up
1780 mkdir -p "${initdir:?}/etc
/systemd
/system
/user@.service.d
/"
1781 cat >"$initdir/etc
/systemd
/system
/user@.service.d
/dbus.conf
" <<EOF
1786 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1787 if [ -f "$userunitdir/dbus-broker.service
" ]; then
1788 inst "$userunitdir/dbus-broker.service
"
1789 inst_symlink /etc/systemd/user/dbus.service
1790 elif [ -f "${ROOTLIBDIR:?}/system
/dbus-daemon.service
" ]; then
1791 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1792 inst "$userunitdir/dbus-daemon.service
"
1794 inst_symlink /etc/systemd/user/dbus.service
1796 inst "$userunitdir/dbus.service
"
1804 if get_bool "$LOOKS_LIKE_DEBIAN" && type -p dpkg-architecture &>/dev/null; then
1805 paths+=("/lib
/$
(dpkg-architecture
-qDEB_HOST_MULTIARCH)/security
")
1807 paths+=(/lib*/security)
1810 for d in /etc/pam.d /etc/security /usr/{etc,lib}/pam.d; do
1811 [ -d "$d" ] && paths+=("$d")
1814 while read -r file; do
1816 done < <(find "${paths[@]}" -xtype f)
1818 # pam_unix depends on unix_chkpwd.
1819 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
1820 image_install -o unix_chkpwd
1822 # set empty root password for easy debugging
1823 sed -i 's/^root:x:/root::/' "${initdir:?}/etc
/passwd
"
1825 # And make sure pam_unix will accept it by making sure that
1826 # the PAM module has the nullok option.
1827 for d in /etc/pam.d /usr/{etc,lib}/pam.d; do
1828 [ -d "$initdir/$d" ] || continue
1829 sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
1833 # shellcheck disable=SC2120
1835 dinfo "Install keymaps
"
1836 # The first three paths may be deprecated.
1837 # It seems now the last two paths are used by many distributions.
1839 /usr/lib/kbd/keymaps/include/* \
1840 /usr/lib/kbd/keymaps/i386/include/* \
1841 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
1842 /usr/lib/kbd/keymaps/legacy/include/* \
1843 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
1844 [[ -f "$i" ]] || continue
1848 # When it takes any argument, then install more keymaps.
1849 if [[ $# -gt 1 ]]; then
1851 /usr/lib/kbd/keymaps/i386/*/* \
1852 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
1853 [[ -f "$i" ]] || continue
1859 install_zoneinfo() {
1860 dinfo "Install
time zones
"
1861 inst_any /usr/share/zoneinfo/Asia/Seoul
1862 inst_any /usr/share/zoneinfo/Asia/Vladivostok
1863 inst_any /usr/share/zoneinfo/Australia/Sydney
1864 inst_any /usr/share/zoneinfo/Europe/Berlin
1865 inst_any /usr/share/zoneinfo/Europe/Dublin
1866 inst_any /usr/share/zoneinfo/Europe/Kiev
1867 inst_any /usr/share/zoneinfo/Pacific/Auckland
1868 inst_any /usr/share/zoneinfo/Pacific/Honolulu
1869 inst_any /usr/share/zoneinfo/CET
1870 inst_any /usr/share/zoneinfo/EET
1871 inst_any /usr/share/zoneinfo/UTC
1875 dinfo "Install system fonts
"
1877 /usr/lib/kbd/consolefonts/eurlatgr* \
1878 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
1879 [[ -f "$i" ]] || continue
1884 install_terminfo() {
1885 dinfo "Install terminfo files
"
1887 for terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
1888 [ -f "${terminfodir}/l
/linux
" ] && break
1890 image_install -o "${terminfodir}/l
/linux
"
1893 has_user_dbus_socket() {
1894 if [ -f /usr/lib/systemd/user/dbus.socket ] || [ -f /etc/systemd/user/dbus.socket ]; then
1897 echo "Per-user instances are not supported. Skipping...
"
1902 setup_nspawn_root() {
1903 if [ -z "${initdir}" ]; then
1904 dfatal "\
$initdir not defined
"
1908 rm -rf "${TESTDIR:?}/unprivileged-nspawn-root
"
1910 if get_bool "$RUN_IN_UNPRIVILEGED_CONTAINER"; then
1911 ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root
"
1912 cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root
"
1916 setup_basic_dirs() {
1917 mkdir -p "${initdir:?}/run
"
1918 mkdir -p "$initdir/etc
/systemd
/system
"
1919 mkdir -p "$initdir/var
/log
/journal
"
1922 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
1923 if [ -L "/$d" ]; then
1930 ln -sfn /run "$initdir/var
/run
"
1931 ln -sfn /run/lock "$initdir/var
/lock
"
1934 mask_supporting_services() {
1935 # mask some services that we do not want to run in these tests
1936 ln -fsv /dev/null "${initdir:?}/etc
/systemd
/system
/systemd-hwdb-update.service
"
1937 ln -fsv /dev/null "$initdir/etc
/systemd
/system
/systemd-journal-catalog-update.service
"
1938 ln -fsv /dev/null "$initdir/etc
/systemd
/system
/systemd-networkd.service
"
1939 ln -fsv /dev/null "$initdir/etc
/systemd
/system
/systemd-networkd.socket
"
1940 ln -fsv /dev/null "$initdir/etc
/systemd
/system
/systemd-resolved.service
"
1945 local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1948 while read -r line; do
1949 [[ "$line" = 'not a dynamic executable' ]] && break
1950 # Skip a harmless error when running the tests on a system with a significantly
1951 # older systemd version (ldd tries to resolve the unprefixed RPATH for libsystemd.so.0,
1952 # which is in this case older than the already installed libsystemd.so.0 in $initdir).
1953 # The issue is triggered by installing test dependencies in install_missing_libraries().
1954 [[ "$line" =~ libsystemd.so.*:\ version\ .*\ not\ found ]] && continue
1956 if [[ "$line" =~ $so_regex ]]; then
1957 file="${BASH_REMATCH[1]}"
1958 [[ -e "${initdir:?}/$file" ]] && continue
1959 inst_library "$file"
1963 if [[ "$line" =~ not\ found ]]; then
1964 dfatal "Missing a shared library required by
$bin.
"
1965 dfatal "Run
\"ldd
$bin\" to
find out what it is.
"
1967 dfatal "Cannot create a
test image.
"
1970 done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
1974 # make sure we don't get a stale LOOPDEV value from old times
1975 local _LOOPDEV="${LOOPDEV:=}"
1976 # We don't want shellcheck to follow & check the $STATEFILE
1977 # shellcheck source=/dev/null
1978 [[ -e "$STATEFILE" ]] && . "$STATEFILE"
1980 if [[ ! -d "$TESTDIR" ]]; then
1981 if [[ -z "$TESTDIR" ]]; then
1982 TESTDIR="$
(mktemp
--tmpdir=/var
/tmp
-d -t systemd-test.XXXXXX
)"
1987 cat >"$STATEFILE" <<EOF
1993 IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME:?}.img
"
1994 IMAGE_PUBLIC="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img
"
1998 initdir="${TESTDIR:?}/root
"
2003 ## @brief Converts numeric logging level to the first letter of level name.
2005 # @param lvl Numeric logging level in range from 1 to 6.
2006 # @retval 1 if @a lvl is out of range.
2007 # @retval 0 if @a lvl is correct.
2008 # @result Echoes first letter of level name.
2021 ## @brief Internal helper function for _do_dlog()
2023 # @param lvl Numeric logging level.
2024 # @param msg Message.
2025 # @retval 0 It's always returned, even if logging failed.
2027 # @note This function is not supposed to be called manually. Please use
2028 # dtrace(), ddebug(), or others instead which wrap this one.
2030 # This function calls _do_dlog() either with parameter msg, or if
2031 # none is given, it will read standard input and will use every line as
2035 # dwarn "This is a warning
"
2036 # echo "This is a warning
" | dwarn
2037 LOG_LEVEL="${LOG_LEVEL:-4}"
2042 [ -z "$LOG_LEVEL" ] && return 0
2044 [ "$lvl" -le "$LOG_LEVEL" ] || return 0
2045 lvlc="$
(_lvl2char
"$lvl")" || return 0
2047 if [ $# -ge 1 ]; then
2050 while read -r line; do
2051 echo "$lvlc: " "$line"
2056 ## @brief Logs message at TRACE level (6)
2058 # @param msg Message.
2059 # @retval 0 It's always returned, even if logging failed.
2063 if get_bool "${debug:=}"; then
2068 ## @brief Logs message at DEBUG level (5)
2070 # @param msg Message.
2071 # @retval 0 It's always returned, even if logging failed.
2076 ## @brief Logs message at INFO level (4)
2078 # @param msg Message.
2079 # @retval 0 It's always returned, even if logging failed.
2083 if get_bool "${debug:=}"; then
2088 ## @brief Logs message at WARN level (3)
2090 # @param msg Message.
2091 # @retval 0 It's always returned, even if logging failed.
2095 if get_bool "${debug:=}"; then
2100 ## @brief Logs message at ERROR level (2)
2102 # @param msg Message.
2103 # @retval 0 It's always returned, even if logging failed.
2108 ## @brief Logs message at FATAL level (1)
2110 # @param msg Message.
2111 # @retval 0 It's always returned, even if logging failed.
2115 if get_bool "${debug:=}"; then
2121 # Generic substring function. If $2 is in $1, return 0.
2122 strstr() { [ "${1#*"$2"*}" != "$1" ]; }
2124 # normalize_path <path>
2125 # Prints the normalized path, where it removes any duplicated
2126 # and trailing slashes.
2128 # $ normalize_path ///test/test//
2132 set -- "${1//+(\/)//}"
2137 # convert_abs_rel <from> <to>
2138 # Prints the relative path, when creating a symlink to <to> from <from>.
2140 # $ convert_abs_rel /usr/bin/test /bin/test-2
2142 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
2144 local __current __absolute __abssize __cursize __newpath
2145 local -i __i __level
2147 set -- "$
(normalize_path
"${1:?}")" "$
(normalize_path
"${2:?}")"
2149 # corner case #1 - self looping link
2150 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
2152 # corner case #2 - own dir link
2153 [[ "${1%/*}" == "$2" ]] && { echo ".
"; return; }
2155 IFS="/" read -ra __current <<< "$1"
2156 IFS="/" read -ra __absolute <<< "$2"
2158 __abssize=${#__absolute[@]}
2159 __cursize=${#__current[@]}
2161 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
2164 if (( __level > __abssize || __level > __cursize ))
2170 for ((__i = __level; __i < __cursize-1; __i++))
2172 if ((__i > __level))
2174 __newpath=$__newpath"/"
2176 __newpath=$__newpath"..
"
2179 for ((__i = __level; __i < __abssize; __i++))
2181 if [[ -n $__newpath ]]
2183 __newpath=$__newpath"/"
2185 __newpath=$__newpath${__absolute[__i]}
2192 # Install a directory, keeping symlinks as on the original system.
2193 # Example: if /lib points to /lib64 on the host, "inst_dir
/lib
/file"
2194 # will create ${initdir}/lib64, ${initdir}/lib64/file,
2195 # and a symlink ${initdir}/lib -> lib64.
2198 local part="${dir%/*}"
2201 [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there
2203 while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do
2208 # iterate over parent directories
2209 for file in $dir; do
2210 [[ -e "${initdir}/$file" ]] && continue
2211 if [[ -L $file ]]; then
2212 inst_symlink "$file"
2215 mkdir -m 0755 "${initdir}/$file" || return 1
2216 [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file"
2217 chmod u+w "${initdir}/$file"
2222 # $1 = file to copy to ramdisk
2223 # $2 (optional) Name for the file on the ramdisk
2224 # Location of the image dir is assumed to be $initdir
2225 # We never overwrite the target if it exists.
2227 [[ -f "${1:?}" ]] || return 1
2228 strstr "$1" "/" || return 1
2231 local target="${2:-$1}"
2232 if ! [[ -d ${initdir:?}/$target ]]; then
2233 [[ -e ${initdir}/$target ]] && return 0
2234 [[ -L ${initdir}/$target ]] && return 0
2235 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
2237 # install checksum files also
2238 if [[ -e "${src%/*}/.
${src##*/}.hmac
" ]]; then
2239 inst "${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac
"
2241 ddebug "Installing
$src"
2242 cp --sparse=always -pfL "$src" "${initdir}/$target"
2245 # find symlinks linked to given library file
2247 # Function searches for symlinks by stripping version numbers appended to
2248 # library filename, checks if it points to the same target and finally
2249 # prints the list of symlinks to stdout.
2252 # rev_lib_symlinks libfoo.so.8.1
2253 # output: libfoo.so.8 libfoo.so
2254 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
2255 rev_lib_symlinks() {
2259 orig="$
(readlink
-f "$1")"
2261 [[ "${fn}" =~ .*\.so\..* ]] || return 1
2263 until [[ "${fn##*.}" == so ]]; do
2265 [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}"
2271 # Same as above, but specialized to handle dynamic libraries.
2272 # It handles making symlinks according to how the original library
2276 local dest="${2:-$1}"
2277 local reallib symlink
2279 strstr "$1" "/" || return 1
2280 [[ -e ${initdir:?}/$dest ]] && return 0
2281 if [[ -L $src ]]; then
2282 # install checksum files also
2283 if [[ -e "${src%/*}/.
${src##*/}.hmac
" ]]; then
2284 inst "${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac
"
2286 reallib="$
(readlink
-f "$src")"
2287 inst_simple "$reallib" "$reallib"
2288 inst_dir "${dest%/*}"
2289 [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
2290 ln -sfn -- "$
(convert_abs_rel
"${dest}" "${reallib}")" "${initdir}/${dest}"
2292 inst_simple
"$src" "$dest"
2295 # Create additional symlinks. See rev_symlinks description.
2296 for symlink
in $
(rev_lib_symlinks
"$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do
2297 if [[ ! -e "$initdir/$symlink" ]]; then
2298 ddebug
"Creating extra symlink: $symlink"
2299 inst_symlink
"$symlink"
2304 # find a binary. If we were not passed the full path directly,
2305 # search in the usual places to find the binary.
2308 if [[ -z ${bin##/*} ]]; then
2309 if [[ -x "$bin" ]] ||
{ strstr
"$bin" ".so" && ldd
"$bin" &>/dev
/null
; }; then
2318 # Same as above, but specialized to install binary executables.
2319 # Install binary executable, and all shared library dependencies, if any.
2324 # In certain cases we might attempt to install a binary which is already
2325 # present in the test image, yet it's missing from the host system.
2326 # In such cases, let's check if the binary indeed exists in the image
2327 # before doing any other chcecks. If it does, immediately return with
2329 if [[ $# -eq 1 ]]; then
2330 for path
in "" bin sbin usr
/bin usr
/sbin
; do
2331 [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0
2335 bin
="$(find_binary "$bin")" ||
return 1
2337 [[ -e "${initdir:?}/$target" ]] && return 0
2338 [[ -L "$bin" ]] && inst_symlink
"$bin" "$target" && return 0
2341 local so_regex
='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2343 while read -r line
; do
2344 [[ "$line" = 'not a dynamic executable' ]] && break
2346 if [[ "$line" =~
$so_regex ]]; then
2347 file="${BASH_REMATCH[1]}"
2348 [[ -e "${initdir}/$file" ]] && continue
2349 inst_library
"$file"
2353 if [[ "$line" =~ not\ found
]]; then
2354 dfatal
"Missing a shared library required by $bin."
2355 dfatal
"Run \"ldd $bin\" to find out what it is."
2357 dfatal
"Cannot create a test image."
2360 done < <(LC_ALL
=C ldd
"$bin" 2>/dev
/null
)
2361 inst_simple
"$bin" "$target"
2364 # same as above, except for shell scripts.
2365 # If your shell script does not start with shebang, it is not a shell script.
2367 local bin line shebang_regex
2368 bin
="$(find_binary "${1:?}")" ||
return 1
2371 read -r -n 80 line
<"$bin"
2372 # If debug is set, clean unprintable chars to prevent messing up the term
2373 get_bool
"${debug:=}" && line
="$(echo -n "$line" | tr -c -d '[:print:][:space:]')"
2374 shebang_regex
='(#! *)(/[^ ]+).*'
2375 [[ "$line" =~
$shebang_regex ]] ||
return 1
2376 inst
"${BASH_REMATCH[2]}" && inst_simple
"$bin" "$@"
2379 # same as above, but specialized for symlinks
2382 local target
="${2:-$src}"
2385 strstr
"$src" "/" ||
return 1
2386 [[ -L "$src" ]] ||
return 1
2387 [[ -L "${initdir:?}/$target" ]] && return 0
2388 realsrc
="$(readlink -f "$src")"
2389 if ! [[ -e "$initdir/$realsrc" ]]; then
2390 if [[ -d "$realsrc" ]]; then
2396 [[ ! -e "$initdir/${target%/*}" ]] && inst_dir
"${target%/*}"
2397 [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}"
2398 ln -sfn -- "$(convert_abs_rel "${target}" "${realsrc}")" "$initdir/$target"
2401 # attempt to install any programs specified in a udev rule
2402 inst_rule_programs
() {
2406 sed -rn 's/^.*?PROGRAM==?"([^ "]+).*$/\1/p' "$rule" |
while read -r prog
; do
2407 if [ -x "/lib/udev/$prog" ]; then
2408 bin
="/lib/udev/$prog"
2410 if ! bin
="$(find_binary "$prog")"; then
2411 dinfo
"Skipping program $prog used in udev rule $(basename "$rule") as it cannot be found"
2416 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
2417 image_install
"$bin"
2421 # udev rules always get installed in the same place, so
2422 # create a function to install them to make life simpler.
2424 local target
=/etc
/udev
/rules.d
2427 inst_dir
"/lib/udev/rules.d"
2429 for rule
in "$@"; do
2430 if [ "${rule#/}" = "$rule" ]; then
2431 for r
in /lib
/udev
/rules.d
/etc
/udev
/rules.d
; do
2432 if [[ -f "$r/$rule" ]]; then
2434 inst_simple
"$found"
2435 inst_rule_programs
"$found"
2440 if [[ -f "${r}${rule}" ]]; then
2442 inst_simple
"$found" "$target/${found##*/}"
2443 inst_rule_programs
"$found"
2446 [[ $found ]] || dinfo
"Skipping udev rule: $rule"
2451 # general purpose installation function
2452 # Same args as above.
2457 [[ ! "$initdir" && -d "$2" ]] && export initdir
="$2"
2458 [[ "$initdir" = "$2" ]] && set "$1"
2461 [[ -z "$initdir" ]] && export initdir
="$2"
2465 dfatal
"inst only takes 1 or 2 or 3 arguments"
2471 for fun
in inst_symlink inst_script inst_binary inst_simple
; do
2472 "$fun" "$@" && return 0
2475 dwarn
"Failed to install '$1'"
2479 # install any of listed files
2481 # If first argument is '-d' and second some destination path, first accessible
2482 # source is installed into this path, otherwise it will installed in the same
2483 # path as source. If none of listed files was installed, function return 1.
2484 # On first successful installation it returns with 0 status.
2488 # inst_any -d /bin/foo /bin/bar /bin/baz
2490 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
2495 [[ "${1:?}" = '-d' ]] && dest
="${2:?}" && shift 2
2497 for file in "$@"; do
2498 if [[ -e "$file" ]]; then
2499 [[ -n "$dest" ]] && inst
"$file" "$dest" && return 0
2500 inst
"$file" && return 0
2507 # image_install [-o ] <file> [<file> ... ]
2508 # Install <file> to the test image
2509 # -o optionally install the <file> and don't fail, if it is not there
2514 if [[ "$prog" = '-o' ]]; then
2519 for prog
in "$@"; do
2520 if ! inst
"$prog" ; then
2521 if get_bool
"$optional"; then
2522 dinfo
"Skipping program $prog as it cannot be found and is" \
2523 "flagged to be optional"
2525 dfatal
"Failed to install $prog"
2532 # Install a single kernel module along with any firmware it may require.
2533 # $1 = full path to kernel module to install
2534 install_kmod_with_fw
() {
2535 local module
="${1:?}"
2536 # no need to go further if the module is already installed
2537 [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*"/lib/modules/$KERNEL_VER/"}" ]] && return 0
2538 [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0
2540 [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}"
2542 inst_simple
"$module" "/lib/modules/$KERNEL_VER/${module##*"/lib/modules/$KERNEL_VER/"}" ||
return $?
2544 local modname
="${module##*/}"
2545 local fwdir found fw
2546 modname
="${modname%.ko*}"
2548 while read -r fw
; do
2550 for fwdir
in /lib
/firmware
/updates
/lib
/firmware
; do
2551 if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then
2552 inst_simple
"$fwdir/$fw" "/lib/firmware/$fw"
2556 if ! get_bool
"$found"; then
2557 if ! grep -qe "\<${modname//-/_}\>" /proc
/modules
; then
2558 dinfo
"Possible missing firmware \"${fw}\" for kernel module" \
2561 dwarn
"Possible missing firmware \"${fw}\" for kernel module" \
2565 done < <(modinfo
-k "$KERNEL_VER" -F firmware
"$module" 2>/dev
/null
)
2569 # Do something with all the dependencies of a kernel module.
2570 # Note that kernel modules depend on themselves using the technique we use
2571 # $1 = function to call for each dependency we find
2572 # It will be passed the full path to the found kernel module
2573 # $2 = module to get dependencies for
2574 # rest of args = arguments to modprobe
2575 for_each_kmod_dep
() {
2582 while read -r cmd modpath _
; do
2583 [[ "$cmd" = insmod
]] ||
continue
2584 "$func" "$modpath" ||
return $?
2586 done < <(modprobe
"$@" --ignore-install --show-depends "$kmod")
2588 ! get_bool
"$found" && return 1
2592 # instmods [-c] <kernel module> [<kernel module> ... ]
2593 # instmods [-c] <kernel subsystem>
2594 # install kernel modules along with all their dependencies.
2595 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
2596 # FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5
2599 if [[ $# -ge 0 && "$1" = '-c' ]]; then
2607 local mod_dir
="/lib/modules/${KERNEL_VER:?}/"
2611 if [ -f "${mod_dir}/modules.${mod#=}" ]; then
2613 [[ "$mpargs" ]] && echo "$mpargs"
2614 cat "${mod_dir}/modules.${mod#=}"
2618 [[ "$mpargs" ]] && echo "$mpargs"
2619 find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f
-printf '%f\n'
2627 # Do not load this diagnostic-only module
2632 # if we are already installed, skip this module and go on
2634 [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return
2636 # We use '-d' option in modprobe only if modules prefix path
2637 # differs from default '/'. This allows us to use Dracut with
2638 # old version of modprobe which doesn't have '-d' option.
2639 local mod_dirname
=${mod_dir%%/lib/modules/*}
2640 [[ -n ${mod_dirname} ]] && mod_dirname
="-d ${mod_dirname}/"
2642 # ok, load the module, all its dependencies, and any firmware
2644 for_each_kmod_dep install_kmod_with_fw
"$mod" \
2645 --set-version "$KERNEL_VER" \
2646 ${mod_dirname:+"$mod_dirname"} \
2647 ${mpargs:+"$mpargs"}
2656 if [[ $# -eq 0 ]]; then # filenames from stdin
2657 while read -r mod
; do
2658 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2659 dfatal
"Failed to install $mod"
2665 for mod
in "$@"; do # filenames as arguments
2666 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2667 dfatal
"Failed to install $mod"
2676 local mountpoint
="${1:?}"
2677 if mountpoint
-q "$mountpoint"; then
2678 ddebug
"umount $mountpoint"
2679 umount
"$mountpoint"
2683 # can be overridden in specific test
2684 test_setup_cleanup
() {
2689 # (post-test) cleanup should always ignore failure and cleanup as much as possible
2692 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
2693 [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC"
2694 # If multiple setups/cleans are ran in parallel, this can cause a race
2695 if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then
2696 rm -vf "${IMAGESTATEDIR}/default.img"
2698 [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR"
2699 [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE"
2703 # can be overridden in specific test
2708 test_cleanup_again
() {
2709 [ -n "$TESTDIR" ] ||
return
2710 rm -rf "$TESTDIR/unprivileged-nspawn-root"
2711 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
2714 test_create_image
() {
2715 create_empty_image_rootdir
2717 # Create what will eventually be our root filesystem onto an overlay
2720 setup_basic_environment
2725 if get_bool
"${TEST_REQUIRE_INSTALL_TESTS:?}" && \
2726 command -v meson
>/dev
/null
&& \
2727 [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
2728 dfatal
"$BUILD_DIR needs to be built with -Dinstall-tests=true"
2732 if [ -e "${IMAGE_PRIVATE:?}" ]; then
2733 echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")"
2736 if [ ! -e "${IMAGE_PUBLIC:?}" ]; then
2737 # default.img is the base that every test uses and optionally appends to
2738 if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] ||
[ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then
2739 # Create the backing public image, but then completely unmount
2740 # it and drop the loopback device responsible for it, since we're
2741 # going to symlink/copy the image and mount it again from
2743 local image_old
="${IMAGE_PUBLIC}"
2744 if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
2745 IMAGE_PUBLIC
="${IMAGESTATEDIR}/default.img"
2751 IMAGE_PUBLIC
="${image_old}"
2753 if [ "${IMAGE_NAME:?}" != "default" ] && ! get_bool
"${TEST_FORCE_NEWIMAGE}"; then
2754 cp -v "$(realpath "${IMAGESTATEDIR}/default.img
")" "$IMAGE_PUBLIC"
2759 declare -f -F test_append_files
>/dev
/null
&& hook_defined
=yes || hook_defined
=no
2761 echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")"
2762 if get_bool
"$TEST_PARALLELIZE" || get_bool
"$hook_defined"; then
2763 cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
2765 ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
2769 # We want to test all services in TEST-01-BASIC, but mask them in
2771 if [[ "${TESTID:?}" != "01" ]]; then
2772 dinfo
"Masking supporting services"
2773 mask_supporting_services
2776 if get_bool
"$hook_defined"; then
2777 test_append_files
"${initdir:?}"
2785 local test_id
="${1:?}"
2788 if ! get_bool
"${TEST_NO_QEMU:=}"; then
2789 if run_qemu
"$test_id"; then
2790 check_result_qemu ||
{ echo "QEMU test failed"; return 1; }
2792 dwarn
"can't run QEMU, skipping"
2795 if ! get_bool
"${TEST_NO_NSPAWN:=}"; then
2797 if run_nspawn
"${initdir:?}" "$test_id"; then
2798 check_result_nspawn
"$initdir" ||
{ echo "nspawn-root test failed"; return 1; }
2800 dwarn
"can't run systemd-nspawn, skipping"
2803 if get_bool
"${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
2804 dir
="$TESTDIR/unprivileged-nspawn-root"
2805 if NSPAWN_ARGUMENTS
="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn
"$dir" "$test_id"; then
2806 check_result_nspawn
"$dir" ||
{ echo "unprivileged-nspawn-root test failed"; return 1; }
2808 dwarn
"can't run systemd-nspawn, skipping"
2816 if [[ $UID != "0" ]]; then
2817 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
2821 if get_bool
"${TEST_NO_QEMU:=}" && get_bool
"${TEST_NO_NSPAWN:=}"; then
2822 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both QEMU and nspawn disabled" >&2
2826 if get_bool
"${TEST_QEMU_ONLY:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
2827 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: QEMU-only tests requested" >&2
2831 if get_bool
"${TEST_PREFER_NSPAWN:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
2836 [[ "$libdir" ]] ||
for libdir
in /lib64
/lib
; do
2837 [[ -d $libdir ]] && libdirs
+=" $libdir" && break
2840 [[ "$usrlibdir" ]] ||
for usrlibdir
in /usr
/lib64
/usr
/lib
; do
2841 [[ -d $usrlibdir ]] && libdirs
+=" $usrlibdir" && break
2844 mkdir
-p "$STATEDIR"
2849 if [ -n "${SUDO_USER}" ]; then
2850 ddebug
"Making ${TESTDIR:?} readable for ${SUDO_USER} (acquired from sudo)"
2851 setfacl
-m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}"
2854 testname
="$(basename "$PWD")"
2856 while (($# > 0)); do
2859 echo "${testname} RUN: $TEST_DESCRIPTION"
2862 if [ $ret -eq 0 ]; then
2863 echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
2865 echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
2870 echo "${testname} SETUP: $TEST_DESCRIPTION"
2875 echo "${testname} CLEANUP: $TEST_DESCRIPTION"
2879 echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
2884 echo -n "${testname}: $TEST_DESCRIPTION "
2885 # Do not use a subshell, otherwise cleanup variables (LOOPDEV) will be lost
2886 # and loop devices will leak
2887 test_setup
</dev
/null
>"$TESTLOG" 2>&1 || ret
=$?
2888 if [ $ret -eq 0 ]; then
2889 test_setup_cleanup
</dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
2891 if [ $ret -eq 0 ]; then
2892 test_run
"$TESTID" </dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
2895 if [ $ret -eq 0 ]; then