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" || echo "")}"
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
226 is_built_with_asan
() {
227 if ! type -P objdump
>/dev
/null
; then
228 ddebug
"Failed to find objdump. Assuming systemd hasn't been built with ASAN."
232 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
234 _asan_calls
="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "(callq?|brasl?|bl
)\s.
+__asan
" -c)"
235 if ((_asan_calls
< 1000)); then
242 is_built_with_coverage
() {
243 if get_bool
"${NO_BUILD:=}" ||
! command -v meson
>/dev
/null
; then
247 meson configure
"${BUILD_DIR:?}" |
grep 'b_coverage' |
awk '{ print $2 }' |
grep -q 'true'
250 IS_BUILT_WITH_ASAN
=$
(is_built_with_asan
&& echo yes ||
echo no
)
251 IS_BUILT_WITH_COVERAGE
=$
(is_built_with_coverage
&& echo yes ||
echo no
)
253 if get_bool
"$IS_BUILT_WITH_ASAN"; then
255 SKIP_INITRD
="${SKIP_INITRD:-yes}"
256 PATH_TO_INIT
=$ROOTLIBDIR/systemd-under-asan
258 QEMU_SMP
="${QEMU_SMP:-4}"
260 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
261 if ASAN_RT_NAME
="$(awk '/libasan.so/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
263 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-gcc} --print-file-name "$ASAN_RT_NAME")")"
264 elif ASAN_RT_NAME
="$(awk '/libclang_rt.asan/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
266 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-clang} --print-file-name "$ASAN_RT_NAME")")"
268 # As clang's ASan DSO is usually in a non-standard path, let's check if
269 # the environment is set accordingly. If not, warn the user and exit.
270 # We're not setting the LD_LIBRARY_PATH automagically here, because
271 # user should encounter (and fix) the same issue when running the unit
273 if ldd
"$SYSTEMD" |
grep -q "libclang_rt.asan.*not found"; then
274 echo >&2 "clang's ASan DSO ($ASAN_RT_NAME) is not present in the runtime library path"
275 echo >&2 "Consider setting LD_LIBRARY_PATH=${ASAN_RT_PATH%/*}"
279 echo >&2 "systemd is not linked against the ASan DSO"
280 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
284 echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
288 QEMU_BIN
="${QEMU_BIN:-""}"
289 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
290 if get_bool
"$QEMU_KVM"; then
291 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)"
294 [[ -n "$ARCH" ]] || ARCH
="$(uname -m)"
297 # QEMU's own build system calls it qemu-system-x86_64
298 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)"
301 # new i386 version of QEMU
302 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)"
304 # i386 version of QEMU
305 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu 2>/dev/null | grep '^/' -m1)"
308 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)"
312 if [[ ! -e "$QEMU_BIN" ]]; then
313 echo "Could not find a suitable qemu binary" >&2
318 # Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu
319 # returns 0 if newer or equal
321 # returns 2 if failing
323 find_qemu_bin ||
return 2
325 # get version from binary
327 qemu_ver
="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')"
329 # Check version string format
330 echo "$qemu_ver" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
331 echo "$1" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
333 # compare as last command to return that value
334 printf "%s\n%s\n" "$1" "$qemu_ver" |
sort -V -C
337 # Return 0 if qemu did run (then you must check the result state/logs for actual
338 # success), or 1 if qemu is not available.
340 if [ -f /etc
/machine-id
]; then
341 read -r MACHINE_ID
</etc
/machine-id
342 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
343 && INITRD
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
344 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
345 && KERNEL_BIN
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
350 rm -f "$initdir"/{testok
,failed
,skipped
}
351 # make sure the initdir is not mounted to avoid concurrent access
355 if [[ ! "$KERNEL_BIN" ]]; then
356 if get_bool
"$LOOKS_LIKE_ARCH"; then
357 KERNEL_BIN
=/boot
/vmlinuz-linux
359 [ "$ARCH" ] || ARCH
=$
(uname
-m)
362 # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
363 # uses the "standard" vmlinuz- prefix
364 [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN
="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
368 KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
374 local default_fedora_initrd
="/boot/initramfs-${KERNEL_VER}.img"
375 local default_debian_initrd
="/boot/initrd.img-${KERNEL_VER}"
376 local default_arch_initrd
="/boot/initramfs-linux-fallback.img"
377 local default_suse_initrd
="/boot/initrd-${KERNEL_VER}"
378 if [[ ! "$INITRD" ]]; then
379 if [[ -e "$default_fedora_initrd" ]]; then
380 INITRD
="$default_fedora_initrd"
381 elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then
382 INITRD
="$default_debian_initrd"
383 elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
384 INITRD
="$default_arch_initrd"
385 elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
386 INITRD
="$default_suse_initrd"
390 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
391 # i.e. use the number of online CPUs on the host machine. If the nproc utility
392 # is not installed or there's some other error when calling it, fall back
393 # to the original value (QEMU_SMP=1).
394 if [[ -z "${QEMU_SMP:=}" ]]; then
395 if ! QEMU_SMP
=$
(nproc
); then
396 dwarn
"nproc utility is not installed, falling back to QEMU_SMP=1"
401 find_qemu_bin ||
return 1
403 # Umount initdir to avoid concurrent access to the filesystem
404 _umount_dir
"$initdir"
406 local kernel_params
=()
407 local qemu_options
=()
408 local qemu_cmd
=("$QEMU_BIN")
410 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
411 kernel_params
+=("systemd.unified_cgroup_hierarchy=yes")
412 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
413 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes")
414 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
415 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no")
416 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
417 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
421 if get_bool
"$LOOKS_LIKE_SUSE"; then
422 kernel_params
+=("rd.hostonly=0")
426 "root=LABEL=systemd_boot"
433 "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
434 "systemd.unit=testsuite.target"
435 "systemd.wants=testsuite-$1.service"
438 if ! get_bool
"$INTERACTIVE_DEBUG"; then
439 kernel_params
+=("systemd.wants=end.service")
442 [ -e "$IMAGE_PRIVATE" ] && image
="$IMAGE_PRIVATE" || image
="$IMAGE_PUBLIC"
448 -kernel "$KERNEL_BIN"
449 -drive "format=raw,cache=unsafe,file=$image"
452 if [[ -n "${QEMU_OPTIONS:=}" ]]; then
453 local user_qemu_options
454 read -ra user_qemu_options
<<< "$QEMU_OPTIONS"
455 qemu_options
+=("${user_qemu_options[@]}")
458 if [[ -n "${KERNEL_APPEND:=}" ]]; then
459 local user_kernel_append
460 read -ra user_kernel_append
<<< "$KERNEL_APPEND"
461 kernel_params
+=("${user_kernel_append[@]}")
464 if [[ "$INITRD" ]] && ! get_bool
"$SKIP_INITRD"; then
465 qemu_options
+=(-initrd "$INITRD")
468 # Let's use KVM if possible
469 if [[ -c /dev
/kvm
]] && get_bool
$QEMU_KVM; then
470 qemu_options
+=(-machine "accel=kvm" -enable-kvm -cpu host)
473 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
474 qemu_cmd
=(timeout
--foreground "$QEMU_TIMEOUT" "$QEMU_BIN")
477 (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]}")
479 if [ "$rc" -eq 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
480 derror
"Test timed out after ${QEMU_TIMEOUT}s"
483 [ "$rc" != 0 ] && derror
"qemu failed with exit code $rc"
488 # Return 0 if nspawn did run (then you must check the result state/logs for actual
489 # success), or 1 if nspawn is not available.
491 [[ -d /run
/systemd
/system
]] ||
return 1
492 rm -f "${initdir:?}"/{testok
,failed
,skipped
}
495 local nspawn_options
=(
497 "--kill-signal=SIGKILL"
499 "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
501 local kernel_params
=(
503 "systemd.unit=testsuite.target"
504 "systemd.wants=testsuite-$2.service"
507 if ! get_bool
"$INTERACTIVE_DEBUG"; then
508 kernel_params
+=("systemd.wants=end.service")
511 if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then
512 local user_nspawn_arguments
513 read -ra user_nspawn_arguments
<<< "$NSPAWN_ARGUMENTS"
514 nspawn_options
+=("${user_nspawn_arguments[@]}")
517 if [[ -n "${KERNEL_APPEND:=}" ]]; then
518 local user_kernel_append
519 read -ra user_kernel_append
<<< "$KERNEL_APPEND"
520 kernel_params
+=("${user_kernel_append[@]}")
523 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
524 dwarn
"nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
526 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ||
"$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
527 nspawn_cmd
+=(env
"SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY")
528 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
529 nspawn_cmd
+=(env
"--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY")
531 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
535 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
536 nspawn_cmd
+=(timeout
--foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN")
538 nspawn_cmd
+=("$SYSTEMD_NSPAWN")
541 (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}")
543 if [ "$rc" -eq 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
544 derror
"Test timed out after ${NSPAWN_TIMEOUT}s"
547 [ "$rc" != 0 ] && derror
"nspawn failed with exit code $rc"
552 # Build two very minimal root images, with two units, one is the same and one is different across them
553 install_verity_minimal
() {
554 dinfo
"Set up a set of minimal images for verity verification"
555 if [ -e "$initdir/usr/share/minimal.raw" ]; then
558 if ! command -v mksquashfs
>/dev
/null
2>&1; then
559 dfatal
"mksquashfs not found"
562 if ! command -v veritysetup
>/dev
/null
2>&1; then
563 dfatal
"veritysetup not found"
566 # Local modifications of some global variables is intentional in this
568 # shellcheck disable=SC2030
577 oldinitdir
="$initdir"
578 rm -rfv "$TESTDIR/minimal"
579 export initdir
="$TESTDIR/minimal"
580 # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
581 mkdir
-p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
584 # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
585 # issue, thus falsely triggering SC2030 in this case
586 # See: koalaman/shellcheck#1409
587 if [[ -v ASAN_RT_PATH
]]; then
588 # If we're compiled with ASan, install the ASan RT (and its dependencies)
589 # into the verity images to get rid of the annoying errors about
590 # missing $LD_PRELOAD libraries.
591 inst_libs
"$ASAN_RT_PATH"
592 inst_library
"$ASAN_RT_PATH"
593 # Create a dummy LSan suppression file otherwise gcc's ASan
594 # complains as it doesn't exist in the minimal image
595 # (i.e. when running TEST-29 or TEST-50 under sanitizers)
596 touch "$initdir/systemd-lsan.supp"
598 cp "$os_release" "$initdir/usr/lib/os-release"
599 ln -s ..
/usr
/lib
/os-release
"$initdir/etc/os-release"
600 touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
601 touch "$initdir/opt/some_file"
602 echo MARKER
=1 >>"$initdir/usr/lib/os-release"
603 echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
604 cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
606 ExecStartPre=cat /usr/lib/os-release
609 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
611 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
612 veritysetup format
"$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
613 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
615 sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
616 rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
617 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
619 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
620 veritysetup format
"$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
621 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
623 # Rolling distros like Arch do not set VERSION_ID
625 if grep -q "^VERSION_ID=" "$os_release"; then
626 version_id
="$(grep "^VERSION_ID
=" "$os_release")"
629 export initdir
="$TESTDIR/app0"
630 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
631 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
632 echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
633 cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
637 ExecStart=/opt/script0.sh
638 TemporaryFileSystem=/var/lib
640 RuntimeDirectory=app0
642 cat >"$initdir/opt/script0.sh" <<EOF
645 test -e /usr/lib/os-release
646 echo bar > \${STATE_DIRECTORY}/foo
647 cat /usr/lib/extension-release.d/extension-release.app0
649 chmod +x
"$initdir/opt/script0.sh"
650 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/some_file"
651 mksquashfs
"$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
653 export initdir
="$TESTDIR/app1"
654 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
655 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
656 ( echo "${version_id}"
657 echo "SYSEXT_SCOPE=portable"
658 echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
659 setfattr
-n user.extension-release.strict
-v false
"$initdir/usr/lib/extension-release.d/extension-release.app2"
660 cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
664 ExecStart=/opt/script1.sh
666 RuntimeDirectory=app1
668 cat >"$initdir/opt/script1.sh" <<EOF
671 test -e /usr/lib/os-release
672 echo baz > \${STATE_DIRECTORY}/foo
673 cat /usr/lib/extension-release.d/extension-release.app2
675 chmod +x
"$initdir/opt/script1.sh"
676 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/other_file"
677 mksquashfs
"$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
681 setup_basic_environment
() {
682 # create the basic filesystem layout
686 install_missing_libraries
705 has_user_dbus_socket
&& install_user_dbus
710 generate_module_dependencies
711 if get_bool
"$IS_BUILT_WITH_ASAN"; then
714 if get_bool
"$TEST_INSTALL_VERITY_MINIMAL"; then
715 install_verity_minimal
720 dinfo
"Setup SELinux"
721 # don't forget KERNEL_APPEND='... selinux=1 ...'
722 if ! get_bool
"$SETUP_SELINUX"; then
723 dinfo
"SETUP_SELINUX != yes, skipping SELinux configuration"
727 local conf_dir
=/etc
/selinux
728 local fixfiles_tools
=(bash uname
cat sort uniq awk grep egrep head expr find rm secon setfiles
)
730 # Make sure the following statement can't expand to "/" to prevent
731 # a potential where-are-my-backups situation
732 rm -rf "${initdir:?}/$conf_dir"
733 if ! cp -ar "$conf_dir" "$initdir/$conf_dir"; then
734 dfatal
"Failed to copy $conf_dir"
738 touch "$initdir/.autorelabel"
739 mkdir
-p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
740 ln -sf ..
/autorelabel.service
"$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
742 image_install
"${fixfiles_tools[@]}"
743 image_install fixfiles
744 image_install sestatus
748 if ! type -p valgrind
; then
749 dfatal
"Failed to install valgrind"
753 local valgrind_bins valgrind_libs valgrind_dbg_and_supp
755 readarray
-t valgrind_bins
< <(strace
-e execve valgrind
/bin
/true
2>&1 >/dev
/null | perl
-lne 'print $1 if /^execve\("([^"]+)"/')
756 image_install
"${valgrind_bins[@]}"
758 readarray
-t valgrind_libs
< <(LD_DEBUG
=files valgrind
/bin
/true
2>&1 >/dev
/null | perl
-lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
759 image_install
"${valgrind_libs[@]}"
761 readarray
-t valgrind_dbg_and_supp
< <(
762 strace
-e open valgrind
/bin
/true
2>&1 >/dev
/null |
763 perl
-lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
765 image_install
"${valgrind_dbg_and_supp[@]}"
768 create_valgrind_wrapper
() {
769 local valgrind_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-valgrind"
770 ddebug
"Create $valgrind_wrapper"
771 cat >"$valgrind_wrapper" <<EOF
774 mount -t proc proc /proc
775 exec valgrind --leak-check=full --track-fds=yes --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
777 chmod 0755 "$valgrind_wrapper"
780 create_asan_wrapper
() {
781 local asan_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-asan"
782 dinfo
"Create ASan wrapper as '$asan_wrapper'"
784 [[ -z "$ASAN_RT_PATH" ]] && dfatal
"ASAN_RT_PATH is empty, but it shouldn't be"
786 # clang: install llvm-symbolizer to generate useful reports
787 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
788 [[ "$ASAN_COMPILER" == "clang" ]] && image_install
"llvm-symbolizer"
790 cat >"$asan_wrapper" <<EOF
795 echo "ASan RT: $ASAN_RT_PATH"
796 if [[ ! -e "$ASAN_RT_PATH" ]]; then
797 echo >&2 "Couldn't find ASan RT at '$ASAN_RT_PATH', can't continue"
801 # Suppress certain leaks reported by LSan (either in external tools or bogus
803 # Docs: # https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions
805 # - fsck is called by systemd-homed and is reporting a leak we're not interested
807 # - libLLVM is a "side effect" caused by the previous fsck leak
808 cat >/systemd-lsan.supp <<INNER_EOF
814 DEFAULT_LSAN_OPTIONS=${LSAN_OPTIONS:-}:suppressions=/systemd-lsan.supp
815 DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
816 DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
817 DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS LSAN_OPTIONS=\$DEFAULT_LSAN_OPTIONS"
819 # As right now bash is the PID 1, we can't expect PATH to have a sane value.
820 # Let's make one to prevent unexpected "<bin> not found" issues in the future
821 export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
823 mount -t proc proc /proc
824 mount -t sysfs sysfs /sys
825 mount -o remount,rw /
827 # A lot of services (most notably dbus) won't start without preloading libasan
828 # See https://github.com/systemd/systemd/issues/5004
829 DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=$ASAN_RT_PATH"
831 if [[ "$ASAN_COMPILER" == "clang" ]]; then
832 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
833 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
834 # runtime ASan DSO is in a non-standard (library) path.
835 echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf
838 echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
839 echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
840 echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
842 # ASAN and syscall filters aren't compatible with each other.
843 find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
845 # The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
846 # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
847 JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
848 mkdir -p "\$JOURNALD_CONF_DIR"
849 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"
851 # Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
852 # Let's try to catch them by redirecting stderr (and stdout just in case) to a file
853 # See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
854 printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf"
856 # 90s isn't enough for some services to finish when literally everything is run
857 # under ASan+UBSan in containers, which, in turn, are run in VMs.
858 # Let's limit which environments such services should be executed in.
859 mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
860 printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
862 # Let's override another hard-coded timeout that kicks in too early
863 mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
864 printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf
866 # D-Bus has troubles during system shutdown causing it to fail. Although it's
867 # harmless, it causes unnecessary noise in the logs, so let's disable LSan's
868 # at_exit check just for the dbus.service
869 mkdir -p /etc/systemd/system/dbus.service.d
870 printf "[Service]\nEnvironment=ASAN_OPTIONS=leak_check_at_exit=false\n" >/etc/systemd/system/dbus.service.d/disable-lsan.conf
872 # Some utilities run via IMPORT/RUN/PROGRAM udev directives fail because
873 # they're uninstrumented (like dmsetup). Let's add a simple rule which sets
874 # LD_PRELOAD to the ASan RT library to fix this.
875 mkdir -p /etc/udev/rules.d
876 cat >/etc/udev/rules.d/00-set-LD_PRELOAD.rules <<INNER_EOF
877 SUBSYSTEM=="block", ENV{LD_PRELOAD}="$ASAN_RT_PATH"
879 chmod 0644 /etc/udev/rules.d/00-set-LD_PRELOAD.rules
881 # The 'mount' utility doesn't behave well under libasan, causing unexpected
882 # fails during boot and subsequent test results check:
883 # bash-5.0# mount -o remount,rw -v /
884 # mount: /dev/sda1 mounted on /.
887 # Let's workaround this by clearing the previously set LD_PRELOAD env variable,
888 # so the libasan library is not loaded for this particular service
890 local _dropin_dir="/etc/systemd/system/\$1.service.d"
891 mkdir -p "\$_dropin_dir"
892 printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$_dropin_dir/unset_ld_preload.conf"
895 unset_ld_preload systemd-remount-fs
896 unset_ld_preload testsuite-
898 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
899 exec "$ROOTLIBDIR/systemd" "\$@"
902 chmod 0755 "$asan_wrapper"
905 create_strace_wrapper
() {
906 local strace_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-strace"
907 ddebug
"Create $strace_wrapper"
908 cat >"$strace_wrapper" <<EOF
911 exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd" "\$@"
913 chmod 0755 "$strace_wrapper"
918 image_install
/sbin
/fsck
*
919 image_install
-o /bin
/fsck
*
921 # fskc.reiserfs calls reiserfsck. so, install it
922 image_install
-o reiserfsck
924 # we use mkfs in system-repart tests
925 image_install
/sbin
/mkfs.ext4
926 image_install
/sbin
/mkfs.vfat
930 dinfo
"Install modules"
934 instmods nls_ascii
=nls
937 if get_bool
"$LOOKS_LIKE_SUSE"; then
943 instmods dm_crypt
=crypto
945 image_install
"${ROOTLIBDIR:?}"/system
/dm-event.
{service
,socket
}
946 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
947 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
948 # and since buster/bionic 95-dm-notify.rules
949 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
950 inst_rules
55-dm.rules
60-persistent-storage-dm.rules
95-dm-notify.rules
952 inst_rules
10-dm.rules
13-dm-disk.rules
95-dm-notify.rules
954 if get_bool
"$LOOKS_LIKE_SUSE"; then
955 inst_rules
60-persistent-storage.rules
61-persistent-storage-compat.rules
99-systemd.rules
959 install_multipath
() {
960 instmods
"=md" multipath
961 image_install kpartx
/lib
/udev
/kpartx_id lsmod mpathpersist multipath multipathd partx
962 image_install
"${ROOTLIBDIR:?}"/system
/multipathd.
{service
,socket
}
963 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
964 inst_rules
56-dm-parts.rules
56-dm-mpath.rules
60-multipath.rules
68-del-part-nodes.rules
95-kpartx.rules
966 inst_rules
11-dm-mpath.rules
11-dm-parts.rules
62-multipath.rules
66-kpartx.rules
68-del-part-nodes.rules
968 mkdir
-p "${initdir:?}/etc/multipath"
971 while read -r file; do
972 # Install libraries required by the given library
974 # Install the library itself and create necessary symlinks
976 done < <(find /lib
*/multipath
-type f
)
978 if get_bool
"$LOOKS_LIKE_ARCH"; then
979 # On Arch the multipath libraries are not linked against libgcc_s.so.1,
980 # but it's still required at runtime
981 inst_library
"/lib64/libgcc_s.so.1"
987 image_install
"${ROOTLIBDIR:?}"/system
/lvm2-lvmpolld.
{service
,socket
}
988 image_install
"${ROOTLIBDIR:?}"/system
/{blk-availability
,lvm2-monitor
}.service
989 image_install
-o "/lib/tmpfiles.d/lvm2.conf"
990 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
991 inst_rules
56-lvm.rules
69-lvm-metad.rules
993 # Support the new udev autoactivation introduced in lvm 2.03.14
994 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
995 # Static autoactivation (via lvm2-activation-generator) was dropped
997 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
998 if [[ -f /lib
/udev
/rules.d
/69-dm-lvm.rules
]]; then
999 inst_rules
11-dm-lvm.rules
69-dm-lvm.rules
1001 image_install
"${ROOTLIBDIR:?}"/system-generators
/lvm2-activation-generator
1002 image_install
"${ROOTLIBDIR:?}"/system
/lvm2-pvscan@.service
1003 inst_rules
11-dm-lvm.rules
69-dm-lvm-metad.rules
1006 mkdir
-p "${initdir:?}/etc/lvm"
1011 # Not all utilities provided by btrfs-progs are listed here; extend the list
1013 image_install btrfs btrfstune mkfs.btrfs
1014 inst_rules
64-btrfs-dm.rules
1018 # Install both client and server side stuff by default
1022 # Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case
1023 # (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
1024 if [[ -z "$inst" ||
"$inst" =~
(client|initiator
) ]]; then
1025 image_install iscsi-iname iscsiadm iscsid iscsistart
1026 image_install
-o "${ROOTLIBDIR:?}"/system
/iscsi-
{init
,onboot
,shutdown
}.service
1027 image_install
"${ROOTLIBDIR:?}"/system
/iscsid.
{service
,socket
}
1028 image_install
"${ROOTLIBDIR:?}"/system
/iscsi.service
1029 mkdir
-p "${initdir:?}"/var
/lib
/iscsi
/{ifaces
,isns
,nodes
,send_targets
,slp
,static
}
1030 mkdir
-p "${initdir:?}/etc/iscsi"
1031 echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
1032 inst_simple
"/etc/iscsi/initiatorname.iscsi"
1035 # Install server-side stuff ("target" in iSCSI jargon) - TGT in this case
1036 # (tgt on Debian, scsi-target-utils on Fedora, etc.)
1037 if [[ -z "$inst" ||
"$inst" =~
(server|target
) ]]; then
1038 image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
1039 image_install
-o /etc
/sysconfig
/tgtd
1040 image_install
"${ROOTLIBDIR:?}"/system
/tgtd.service
1041 mkdir
-p "${initdir:?}/etc/tgt"
1042 touch "${initdir:?}"/etc
/tgt
/{tgtd
,targets
}.conf
1043 # Install perl modules required by tgt-admin
1045 # Forgive me father for I have sinned. The monstrosity below appends
1046 # a perl snippet to the `tgt-admin` perl script on the fly, which
1047 # dumps a list of files (perl modules) required by `tgt-admin` at
1048 # the runtime plus any DSOs loaded via DynaLoader. This list is then
1049 # passed to `inst_simple` which installs the necessary files into the image
1051 # shellcheck disable=SC2016
1052 while read -r file; do
1054 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 }')
1061 system
/mdadm-grow-continue@.service
1062 system
/mdadm-last-resort@.service
1063 system
/mdadm-last-resort@.timer
1064 system
/mdmon@.service
1065 system
/mdmonitor-oneshot.service
1066 system
/mdmonitor-oneshot.timer
1067 system
/mdmonitor.service
1068 system-shutdown
/mdadm.shutdown
1071 image_install mdadm mdmon
1072 inst_rules
01-md-raid-creating.rules
63-md-raid-arrays.rules
64-md-raid-assembly.rules
69-md-clustered-confirm-device.rules
1073 # Fedora/CentOS/RHEL ships this rule file
1074 [[ -f /lib
/udev
/rules.d
/65-md-incremental.rules
]] && inst_rules
65-md-incremental.rules
1076 for unit
in "${mdadm_units[@]}"; do
1077 image_install
"${ROOTLIBDIR:?}/$unit"
1081 install_compiled_systemd
() {
1082 dinfo
"Install compiled systemd"
1085 ninja_bin
="$(type -P ninja || type -P ninja-build)"
1086 if [[ -z "$ninja_bin" ]]; then
1087 dfatal
"ninja was not found"
1090 (set -x; DESTDIR
="$initdir" "$ninja_bin" -C "$BUILD_DIR" install)
1092 # If we are doing coverage runs, copy over the binary notes files, as lcov expects to
1093 # find them in the same directory as the runtime data counts
1094 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1095 mkdir
-p "${initdir}/${BUILD_DIR:?}/"
1096 rsync
-am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/"
1097 # Set effective & default ACLs for the build dir so unprivileged
1098 # processes can write gcda files with coverage stats
1099 setfacl
-R -m 'd:o:rwX' -m 'o:rwX' "${initdir}/${BUILD_DIR:?}/"
1103 install_debian_systemd
() {
1104 dinfo
"Install debian systemd"
1108 while read -r deb
; do
1109 files
="$(dpkg-query -L "$deb" 2>/dev/null)" ||
continue
1110 ddebug
"Install debian files from package $deb"
1111 for file in $files; do
1112 [ -e "$file" ] ||
continue
1113 [ -d "$file" ] && continue
1116 done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut
-d ':' -f 2)
1119 install_suse_systemd
() {
1120 local testsdir
=/usr
/lib
/systemd
/tests
1123 dinfo
"Install SUSE systemd"
1129 systemd-experimental
1130 systemd-journal-remote
1135 for p
in "${pkgs[@]}"; do
1136 rpm
-q "$p" &>/dev
/null ||
continue
1138 ddebug
"Install files from package $p"
1140 [ -e "$f" ] ||
continue
1141 [ -d "$f" ] && continue
1143 done < <(rpm
-ql "$p")
1146 # we only need testsdata dir as well as the unit tests (for
1147 # TEST-02-UNITTESTS) in the image.
1148 dinfo
"Install unit tests and testdata directory"
1150 mkdir
-p "$initdir/$testsdir"
1151 cp "$testsdir"/test-
* "$initdir/$testsdir/"
1152 cp -a "$testsdir/testdata" "$initdir/$testsdir/"
1154 # On openSUSE, these dirs are not created at package install for now on.
1155 mkdir
-p "$initdir/var/log/journal/remote"
1158 install_distro_systemd
() {
1159 dinfo
"Install distro systemd"
1161 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
1162 install_debian_systemd
1163 elif get_bool
"$LOOKS_LIKE_SUSE"; then
1164 install_suse_systemd
1166 dfatal
"NO_BUILD not supported for this distro"
1172 dinfo
"Install systemd"
1173 if get_bool
"$NO_BUILD"; then
1174 install_distro_systemd
1176 install_compiled_systemd
1179 # remove unneeded documentation
1180 rm -fr "${initdir:?}"/usr
/share
/{man
,doc
}
1182 # enable debug logging in PID1
1183 echo LogLevel
=debug
>>"$initdir/etc/systemd/system.conf"
1184 if [[ -n "$TEST_SYSTEMD_LOG_LEVEL" ]]; then
1185 echo DefaultEnvironment
=SYSTEMD_LOG_LEVEL
="$TEST_SYSTEMD_LOG_LEVEL" >>"$initdir/etc/systemd/system.conf"
1187 # store coredumps in journal
1188 echo Storage
=journal
>>"$initdir/etc/systemd/coredump.conf"
1189 # Propagate SYSTEMD_UNIT_PATH to user systemd managers
1190 mkdir
"$initdir/etc/systemd/system/user@.service.d/"
1191 echo -e "[Service]\nPassEnvironment=SYSTEMD_UNIT_PATH\n" >"$initdir/etc/systemd/system/user@.service.d/override.conf"
1193 # When built with gcov, disable ProtectSystem= and ProtectHome= in the test
1194 # images, since it prevents gcov to write the coverage reports (*.gcda
1196 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1197 mkdir
-p "$initdir/etc/systemd/system/service.d/"
1198 echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/99-gcov-override.conf"
1199 # Similarly, set ReadWritePaths= to the $BUILD_DIR in the test image
1200 # to make the coverage work with units utilizing DynamicUser=yes. Do
1201 # this only for services from TEST-20, as setting this system-wide
1202 # has many undesirable side-effects
1203 mkdir
-p "$initdir/etc/systemd/system/test20-.service.d/"
1204 echo -e "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/test20-.service.d/99-gcov-rwpaths-override.conf"
1207 # If we're built with -Dportabled=false, tests with systemd-analyze
1208 # --profile will fail. Since we need just the profile (text) files, let's
1209 # copy them into the image if they don't exist there.
1210 local portable_dir
="${initdir:?}${ROOTLIBDIR:?}/portable"
1211 if [[ ! -d "$portable_dir/profile/strict" ]]; then
1212 dinfo
"Couldn't find portable profiles in the test image"
1213 dinfo
"Copying them directly from the source tree"
1214 mkdir
-p "$portable_dir"
1215 cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir"
1221 rpath
="$(objdump -p "${1:?}" 2>/dev/null | awk "/R
(UN
)?PATH
/ { print
\"$initdir\" \
$2 }" | paste -sd :)"
1223 if [ -z "$rpath" ] ; then
1230 install_missing_libraries
() {
1231 dinfo
"Install missing libraries"
1232 # install possible missing libraries
1233 for i
in "${initdir:?}"{,/usr
}/{sbin
,bin
}/* "$initdir"{,/usr
}/lib
/systemd
/{,tests
/{,manual
/,unsafe
/}}*; do
1234 LD_LIBRARY_PATH
="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs
"$i"
1238 # A number of dependencies is now optional via dlopen, so the install
1239 # script will not pick them up, since it looks at linkage.
1240 for lib
in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw
; do
1241 ddebug
"Searching for $lib via pkg-config"
1242 if pkg-config
--exists "$lib"; then
1243 path
="$(pkg-config --variable=libdir "$lib")"
1244 if [ -z "${path}" ]; then
1245 ddebug
"$lib.pc does not contain a libdir variable, skipping"
1249 if ! [[ ${lib} =~ ^lib
]]; then
1252 # Some pkg-config files are broken and give out the wrong paths
1253 # (eg: libcryptsetup), so just ignore them
1254 inst_libs
"${path}/${lib}.so" || true
1255 inst_library
"${path}/${lib}.so" || true
1257 ddebug
"$lib.pc not found, skipping"
1264 if [ -n "${LOOPDEV:=}" ]; then
1265 ddebug
"losetup -d $LOOPDEV"
1266 losetup
-d "${LOOPDEV}"
1271 trap cleanup_loopdev EXIT INT QUIT PIPE
1273 create_empty_image
() {
1274 if [ -z "${IMAGE_NAME:=}" ]; then
1275 echo "create_empty_image: \$IMAGE_NAME not set"
1280 if ! get_bool
"$NO_BUILD"; then
1281 if meson configure
"${BUILD_DIR:?}" |
grep 'static-lib\|standalone-binaries' |
awk '{ print $2 }' |
grep -q 'true'; then
1284 if meson configure
"${BUILD_DIR:?}" |
grep 'link-.*-shared' |
awk '{ print $2 }' |
grep -q 'false'; then
1287 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1291 if ! get_bool
"$STRIP_BINARIES"; then
1295 echo "Setting up ${IMAGE_PUBLIC:?} (${size} MB)"
1296 rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
1298 # Create the blank file to use as a root filesystem
1299 truncate
-s "${size}M" "$IMAGE_PUBLIC"
1301 LOOPDEV
=$
(losetup
--show -P -f "$IMAGE_PUBLIC")
1302 [ -b "$LOOPDEV" ] ||
return 1
1303 sfdisk
"$LOOPDEV" <<EOF
1304 ,$((size - 50))M,L,*
1310 local label
=(-L systemd_boot
)
1311 # mkfs.reiserfs doesn't know -L. so, use --label instead
1312 [[ "$FSTYPE" == "reiserfs" ]] && label
=(--label systemd_boot
)
1313 if ! mkfs
-t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then
1314 dfatal
"Failed to mkfs -t ${FSTYPE}"
1320 if [ -z "${LOOPDEV:=}" ]; then
1321 [ -e "${IMAGE_PRIVATE:?}" ] && image
="$IMAGE_PRIVATE" || image
="${IMAGE_PUBLIC:?}"
1322 LOOPDEV
="$(losetup --show -P -f "$image")"
1323 [ -b "$LOOPDEV" ] ||
return 1
1328 if ! mountpoint
-q "${initdir:?}"; then
1330 mount
"${LOOPDEV}p1" "$initdir"
1331 TEST_SETUP_CLEANUP_ROOTDIR
=1
1336 # only umount if create_empty_image_rootdir() was called to mount it
1337 get_bool
"$TEST_SETUP_CLEANUP_ROOTDIR" && _umount_dir
"${initdir:?}"
1341 # unmount the loopback device from all places. Otherwise we risk file
1342 # system corruption.
1343 for device
in $
(losetup
-l |
awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do
1344 ddebug
"Unmounting all uses of $device"
1345 mount |
awk '/^'"${device}"'p/{print $1}' |
xargs --no-run-if-empty umount
-v
1349 create_empty_image_rootdir
() {
1354 check_asan_reports
() {
1358 if get_bool
"$IS_BUILT_WITH_ASAN"; then
1360 if [[ -e "$root/systemd.asan.log.1" ]]; then
1361 cat "$root/systemd.asan.log.1"
1365 journald_report
="$(find "$root" -name "systemd-journald.
*san.log
*" -exec cat {} \;)"
1366 if [[ -n "$journald_report" ]]; then
1367 printf "%s\n" "$journald_report"
1368 cat "$root/systemd-journald.out" ||
:
1373 "$JOURNALCTL" -D "$root/var
/log
/journal
" | perl -alne '
1375 %services_to_ignore = (
1376 "dbus-daemon
" => undef,
1377 "dbus-broker-launch
" => undef,
1380 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
1382 if [[ -n "$pids" ]]; then
1384 for pid
in $pids; do
1385 "$JOURNALCTL" -D "$root/var/log/journal" _PID
="$pid" --no-pager
1393 check_coverage_reports
() {
1396 if get_bool
"$NO_BUILD"; then
1399 if ! get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1403 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1404 dest
="${ARTIFACT_DIRECTORY}/${testname:?}.coverage-info"
1406 dest
="${TESTDIR:?}/coverage-info"
1409 # Create a coverage report that will later be uploaded. Remove info about
1410 # system libraries/headers, as we don't really care about them.
1411 if [[ -f "$dest" ]]; then
1412 # If the destination report file already exists, don't overwrite it, but
1413 # dump the new report in a temporary file and then merge it with the already
1414 # present one - this usually happens when running both "parts" of a test
1415 # in one run (the qemu and the nspawn part).
1416 lcov
--directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new"
1417 lcov
--remove "${dest}.new" -o "${dest}.new" '/usr/include/*' '/usr/lib/*'
1418 lcov
--add-tracefile "${dest}" --add-tracefile "${dest}.new" -o "${dest}"
1421 lcov
--directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}"
1422 lcov
--remove "${dest}" -o "${dest}" '/usr/include/*' '/usr/lib/*'
1425 # If the test logs contain lines like:
1427 # ...systemd-resolved[735885]: profiling:/systemd-meson-build/src/shared/libsystemd-shared-250.a.p/base-filesystem.c.gcda:Cannot open
1429 # it means we're possibly missing some coverage since gcov can't write the stats,
1430 # usually due to the sandbox being too restrictive (e.g. ProtectSystem=yes,
1431 # ProtectHome=yes) or the $BUILD_DIR being inaccessible to non-root users - see
1432 # `setfacl` stuff in install_compiled_systemd().
1433 if ! get_bool
"${IGNORE_MISSING_COVERAGE:=}" && \
1434 "${JOURNALCTL:?}" -q --no-pager -D "${root:?}/var/log/journal" --grep "profiling:.+?gcda:[Cc]annot open"; then
1435 derror
"Detected possibly missing coverage, check the journal"
1443 # Default to always saving journal
1446 if [ "${TEST_SAVE_JOURNAL}" = "no" ]; then
1448 elif [ "${TEST_SAVE_JOURNAL}" = "fail" ] && [ "$2" = "0" ]; then
1452 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1453 dest
="${ARTIFACT_DIRECTORY}/${testname:?}.journal"
1455 dest
="${TESTDIR:?}/system.journal"
1458 for j
in "${1:?}"/*; do
1459 if get_bool
"$save"; then
1460 if [ "$SYSTEMD_JOURNAL_REMOTE" = "" ]; then
1463 "$SYSTEMD_JOURNAL_REMOTE" -o "$dest" --getter="$JOURNALCTL -o export -D $j"
1467 if [ -n "${TEST_SHOW_JOURNAL}" ]; then
1469 "$JOURNALCTL" --no-pager -o short-monotonic
--no-hostname --priority="${TEST_SHOW_JOURNAL}" -D "$j"
1475 if ! get_bool
"$save"; then
1479 if [ -n "${SUDO_USER}" ]; then
1480 setfacl
-m "user:${SUDO_USER:?}:r-X" "$dest"*
1483 # we want to print this sometime later, so save this in a variable
1484 JOURNAL_LIST
="$(ls -l "$dest"*)"
1487 check_result_common
() {
1488 local workspace
="${1:?}"
1491 if [ -s "$workspace/failed" ]; then
1492 # Non-empty …/failed has highest priority
1493 cp -a "$workspace/failed" "${TESTDIR:?}/"
1494 if [ -n "${SUDO_USER}" ]; then
1495 setfacl
-m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed
1498 elif get_bool
"$TIMED_OUT"; then
1499 echo "(timeout)" >"${TESTDIR:?}/failed"
1501 elif [ -e "$workspace/testok" ]; then
1502 # …/testok always counts (but with lower priority than …/failed)
1504 elif [ -e "$workspace/skipped" ]; then
1505 # …/skipped always counts (a message is expected)
1506 echo "${TESTNAME:?} was skipped:"
1507 cat "$workspace/skipped"
1510 echo "(failed; see logs)" >"${TESTDIR:?}/failed"
1514 check_asan_reports
"$workspace" || ret
=4
1516 check_coverage_reports
"$workspace" || ret
=5
1518 save_journal
"$workspace/var/log/journal" $ret
1520 if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out" ]; then
1521 cp "$workspace/strace.out" "${ARTIFACT_DIRECTORY}/"
1524 if [ ${ret:?} != 0 ] && [ -f "$TESTDIR/failed" ]; then
1525 echo -n "${TESTNAME:?}: "
1526 cat "$TESTDIR/failed"
1528 echo "${JOURNAL_LIST:-"No journals were saved"}"
1533 check_result_nspawn
() {
1534 local workspace
="${1:?}"
1537 check_result_common
"${workspace}"
1540 # Run additional test-specific checks if defined by check_result_nspawn_hook()
1541 if declare -F check_result_nspawn_hook
>/dev
/null
; then
1542 if ! check_result_nspawn_hook
; then
1543 derror
"check_result_nspawn_hook() returned with EC > 0"
1548 _umount_dir
"${initdir:?}"
1553 # can be overridden in specific test
1554 check_result_qemu
() {
1558 check_result_common
"${initdir:?}"
1561 _umount_dir
"${initdir:?}"
1563 # Run additional test-specific checks if defined by check_result_qemu_hook()
1564 if declare -F check_result_qemu_hook
>/dev
/null
; then
1565 if ! check_result_qemu_hook
; then
1566 derror
"check_result_qemu_hook() returned with EC > 0"
1574 check_result_nspawn_unittests
() {
1575 local workspace
="${1:?}"
1578 [[ -e "$workspace/testok" ]] && ret
=0
1580 if [[ -s "$workspace/failed" ]]; then
1582 echo "=== Failed test log ==="
1583 cat "$workspace/failed"
1585 if [[ -s "$workspace/skipped" ]]; then
1586 echo "=== Skipped test log =="
1587 cat "$workspace/skipped"
1588 # We might have only skipped tests - that should not fail the job
1591 if [[ -s "$workspace/testok" ]]; then
1592 echo "=== Passed tests ==="
1593 cat "$workspace/testok"
1597 get_bool
"${TIMED_OUT:=}" && ret
=1
1598 check_coverage_reports
"$workspace" || ret
=5
1600 save_journal
"$workspace/var/log/journal" $ret
1602 _umount_dir
"${initdir:?}"
1607 check_result_qemu_unittests
() {
1611 [[ -e "${initdir:?}/testok" ]] && ret
=0
1613 if [[ -s "$initdir/failed" ]]; then
1615 echo "=== Failed test log ==="
1616 cat "$initdir/failed"
1618 if [[ -s "$initdir/skipped" ]]; then
1619 echo "=== Skipped test log =="
1620 cat "$initdir/skipped"
1621 # We might have only skipped tests - that should not fail the job
1624 if [[ -s "$initdir/testok" ]]; then
1625 echo "=== Passed tests ==="
1626 cat "$initdir/testok"
1630 get_bool
"${TIMED_OUT:=}" && ret
=1
1631 check_coverage_reports
"$initdir" || ret
=5
1633 save_journal
"$initdir/var/log/journal" $ret
1635 _umount_dir
"$initdir"
1641 dinfo
"Strip binaries"
1642 if ! get_bool
"$STRIP_BINARIES"; then
1643 dinfo
"STRIP_BINARIES == no, keeping binaries unstripped"
1646 while read -r bin
; do
1647 strip
--strip-unneeded "$bin" |
& grep -vi 'file format not recognized' | ddebug ||
:
1648 done < <(find "${initdir:?}" -executable -not -path '*/lib/modules/*.ko' -type f
)
1652 dinfo
"Create rc.local"
1653 mkdir
-p "${initdir:?}/etc/rc.d"
1654 cat >"$initdir/etc/rc.d/rc.local" <<EOF
1658 chmod 0755 "$initdir/etc/rc.d/rc.local"
1662 ddebug
"Install executables from the service files"
1664 local pkg_config_path
="${BUILD_DIR:?}/src/core/"
1665 local systemunitdir userunitdir exe
1666 systemunitdir
="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemdsystemunitdir systemd)"
1667 userunitdir
="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemduserunitdir systemd)"
1668 while read -r exe
; do
1669 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
1670 # also, plymouth is pulled in by rescue.service, but even there the exit code
1671 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
1672 dinfo
"Attempting to install $exe (based on unit file reference)"
1673 inst
"$exe" ||
[ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth" ]
1674 done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service |
sort -u)
1677 generate_module_dependencies
() {
1678 dinfo
"Generate modules dependencies"
1679 if [[ -d "${initdir:?}/lib/modules/${KERNEL_VER:?}" ]] && \
1680 ! depmod
-a -b "$initdir" "$KERNEL_VER"; then
1681 dfatal
"\"depmod -a $KERNEL_VER\" failed."
1686 install_depmod_files
() {
1687 dinfo
"Install depmod files"
1688 inst
"/lib/modules/${KERNEL_VER:?}/modules.order"
1689 inst
"/lib/modules/$KERNEL_VER/modules.builtin"
1692 install_plymouth
() {
1693 dinfo
"Install plymouth"
1694 # install plymouth, if found... else remove plymouth service files
1695 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
1696 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
1697 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
1698 # image_install plymouth plymouthd
1700 rm -f "${initdir:?}"/{usr
/lib
,lib
,etc
}/systemd
/system
/plymouth
* "$initdir"/{usr
/lib
,lib
,etc
}/systemd
/system
/*/plymouth
*
1705 # If haveged is installed, it's probably included in initrd and needs to be
1706 # installed in the image too.
1707 if [ -x /usr
/sbin
/haveged
]; then
1708 dinfo
"Install haveged files"
1709 inst
/usr
/sbin
/haveged
1710 for u
in /usr
/lib
/systemd
/system
/haveged
*; do
1716 install_ld_so_conf
() {
1717 dinfo
"Install /etc/ld.so.conf*"
1718 cp -a /etc
/ld.so.conf
* "${initdir:?}/etc"
1719 ldconfig
-r "$initdir"
1722 install_testuser
() {
1723 dinfo
"Set up a test user"
1724 # create unprivileged user for user manager tests
1725 mkdir
-p "${initdir:?}/etc/sysusers.d"
1726 cat >"$initdir/etc/sysusers.d/testuser.conf" <<EOF
1727 u testuser 4711 "Test User" /home/testuser
1730 mkdir
-p "$initdir/home/testuser"
1731 chmod 0700 "$initdir/home/testuser"
1732 chown
4711:4711 "$initdir/home/testuser"
1735 install_config_files
() {
1736 dinfo
"Install config files"
1737 inst
/etc
/sysconfig
/init ||
:
1740 inst_any
/etc
/login.defs
/usr
/etc
/login.defs
1743 inst_any
/etc
/nsswitch.conf
/usr
/etc
/nsswitch.conf
1744 inst
/etc
/pam.conf ||
:
1745 inst_any
/etc
/os-release
/usr
/lib
/os-release
1747 # we want an empty environment
1748 : >"${initdir:?}/etc/environment"
1749 : >"$initdir/etc/machine-id"
1750 : >"$initdir/etc/resolv.conf"
1753 echo 'H' >"$initdir/etc/hostname"
1755 # let's set up just one image with the traditional verbose output
1756 if [ "${IMAGE_NAME:?}" != "basic" ]; then
1757 mkdir
-p "$initdir/etc/systemd/system.conf.d"
1758 echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc/systemd/system.conf.d/status.conf"
1762 install_basic_tools
() {
1763 dinfo
"Install basic tools"
1764 image_install
"${BASICTOOLS[@]}"
1765 image_install
-o sushell
1766 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
1767 image_install
-o ldconfig.real
1770 install_debug_tools
() {
1771 dinfo
"Install debug tools"
1772 image_install
-o "${DEBUGTOOLS[@]}"
1774 if get_bool
"$INTERACTIVE_DEBUG"; then
1775 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
1776 local getty_override
="${initdir:?}/etc/systemd/system/serial-getty@.service.d"
1777 mkdir
-p "$getty_override"
1778 echo -e "[Service]\nEnvironment=TERM=linux" >"$getty_override/default-TERM.conf"
1780 cat >"$initdir/etc/motd" <<EOF
1781 To adjust the terminal size use:
1785 stty cols xx rows yy
1791 dinfo
"Install libnss"
1792 # install libnss_files for login
1794 mapfile
-t NSS_LIBS
< <(LD_DEBUG
=files getent passwd
2>&1 >/dev
/null |
sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
1795 image_install
"${NSS_LIBS[@]}"
1799 dinfo
"Install dbus"
1800 inst
"${ROOTLIBDIR:?}/system/dbus.socket"
1802 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1803 if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then
1804 inst
"$ROOTLIBDIR/system/dbus-broker.service"
1805 inst_symlink
/etc
/systemd
/system
/dbus.service
1806 inst
/usr
/bin
/dbus-broker
1807 inst
/usr
/bin
/dbus-broker-launch
1808 elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then
1809 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1810 inst
"$ROOTLIBDIR/system/dbus-daemon.service"
1812 inst_symlink
/etc
/systemd
/system
/dbus.service
1814 inst
"$ROOTLIBDIR/system/dbus.service"
1817 while read -r file; do
1819 done < <(find /etc
/dbus-1
/usr
/share
/dbus-1
-xtype f
2>/dev
/null
)
1821 # setup policy for Type=dbus test
1822 mkdir
-p "${initdir:?}/etc/dbus-1/system.d"
1823 cat >"$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf" <<EOF
1824 <?xml version="1.0"?>
1825 <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
1826 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
1828 <policy user="root">
1829 <allow own="systemd.test.ExecStopPost"/>
1835 install_user_dbus
() {
1836 dinfo
"Install user dbus"
1838 if ! userunitdir
="$(pkg-config --variable=systemduserunitdir systemd)"; then
1839 dwarn
"WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user"
1840 userunitdir
=/usr
/lib
/systemd
/user
1843 inst
"$userunitdir/dbus.socket"
1844 inst_symlink
"$userunitdir/sockets.target.wants/dbus.socket" || inst_symlink
/etc
/systemd
/user
/sockets.target.wants
/dbus.socket
1846 # Append the After= dependency on dbus in case it isn't already set up
1847 mkdir
-p "${initdir:?}/etc/systemd/system/user@.service.d/"
1848 cat >"$initdir/etc/systemd/system/user@.service.d/dbus.conf" <<EOF
1853 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1854 if [ -f "$userunitdir/dbus-broker.service" ]; then
1855 inst
"$userunitdir/dbus-broker.service"
1856 inst_symlink
/etc
/systemd
/user
/dbus.service
1857 elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then
1858 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1859 inst
"$userunitdir/dbus-daemon.service"
1861 inst_symlink
/etc
/systemd
/user
/dbus.service
1863 inst
"$userunitdir/dbus.service"
1871 if get_bool
"$LOOKS_LIKE_DEBIAN" && type -p dpkg-architecture
&>/dev
/null
; then
1872 paths
+=("/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security")
1874 paths
+=(/lib
*/security
)
1877 for d
in /etc
/pam.d
/{usr
/,}etc
/security
/usr
/{etc
,lib
}/pam.d
; do
1878 [ -d "$d" ] && paths
+=("$d")
1881 while read -r file; do
1883 done < <(find "${paths[@]}" -xtype f
)
1885 # pam_unix depends on unix_chkpwd.
1886 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
1887 image_install
-o unix_chkpwd
1889 # set empty root password for easy debugging
1890 sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
1892 # And make sure pam_unix will accept it by making sure that
1893 # the PAM module has the nullok option.
1894 for d
in /etc
/pam.d
/usr
/{etc
,lib
}/pam.d
; do
1895 [ -d "$initdir/$d" ] ||
continue
1896 sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
1900 # shellcheck disable=SC2120
1902 dinfo
"Install keymaps"
1903 # The first three paths may be deprecated.
1904 # It seems now the last two paths are used by many distributions.
1906 /usr
/lib
/kbd
/keymaps
/include
/* \
1907 /usr
/lib
/kbd
/keymaps
/i386
/include
/* \
1908 /usr
/lib
/kbd
/keymaps
/i386
/qwerty
/us.
* \
1909 /usr
/lib
/kbd
/keymaps
/legacy
/include
/* \
1910 /usr
/lib
/kbd
/keymaps
/legacy
/i386
/qwerty
/us.
*; do
1911 [[ -f "$i" ]] ||
continue
1915 # When it takes any argument, then install more keymaps.
1916 if [[ $# -gt 1 ]]; then
1918 /usr
/lib
/kbd
/keymaps
/i386
/*/* \
1919 /usr
/lib
/kbd
/keymaps
/legacy
/i386
/*/*; do
1920 [[ -f "$i" ]] ||
continue
1926 install_zoneinfo
() {
1927 dinfo
"Install time zones"
1928 inst_any
/usr
/share
/zoneinfo
/Asia
/Seoul
1929 inst_any
/usr
/share
/zoneinfo
/Asia
/Vladivostok
1930 inst_any
/usr
/share
/zoneinfo
/Australia
/Sydney
1931 inst_any
/usr
/share
/zoneinfo
/Europe
/Berlin
1932 inst_any
/usr
/share
/zoneinfo
/Europe
/Dublin
1933 inst_any
/usr
/share
/zoneinfo
/Europe
/Kiev
1934 inst_any
/usr
/share
/zoneinfo
/Pacific
/Auckland
1935 inst_any
/usr
/share
/zoneinfo
/Pacific
/Honolulu
1936 inst_any
/usr
/share
/zoneinfo
/CET
1937 inst_any
/usr
/share
/zoneinfo
/EET
1938 inst_any
/usr
/share
/zoneinfo
/UTC
1942 dinfo
"Install system fonts"
1944 /usr
/lib
/kbd
/consolefonts
/eurlatgr
* \
1945 /usr
/lib
/kbd
/consolefonts
/latarcyrheb-sun16
*; do
1946 [[ -f "$i" ]] ||
continue
1951 install_terminfo
() {
1952 dinfo
"Install terminfo files"
1954 for terminfodir
in /lib
/terminfo
/etc
/terminfo
/usr
/share
/terminfo
; do
1955 [ -f "${terminfodir}/l/linux" ] && break
1957 image_install
-o "${terminfodir}/l/linux"
1960 has_user_dbus_socket
() {
1961 if [ -f /usr
/lib
/systemd
/user
/dbus.socket
] ||
[ -f /etc
/systemd
/user
/dbus.socket
]; then
1964 echo "Per-user instances are not supported. Skipping..."
1969 setup_nspawn_root_hook
() { :;}
1971 setup_nspawn_root
() {
1972 if [ -z "${initdir}" ]; then
1973 dfatal
"\$initdir not defined"
1977 rm -rf "${TESTDIR:?}/unprivileged-nspawn-root"
1979 if get_bool
"$RUN_IN_UNPRIVILEGED_CONTAINER"; then
1980 ddebug
"cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
1981 cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
1984 setup_nspawn_root_hook
1987 setup_basic_dirs
() {
1988 mkdir
-p "${initdir:?}/run"
1989 mkdir
-p "$initdir/etc/systemd/system"
1990 mkdir
-p "$initdir/var/log/journal"
1993 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
1994 if [ -L "/$d" ]; then
2001 ln -sfn /run
"$initdir/var/run"
2002 ln -sfn /run
/lock
"$initdir/var/lock"
2005 mask_supporting_services
() {
2006 # mask some services that we do not want to run in these tests
2007 ln -fsv /dev
/null
"${initdir:?}/etc/systemd/system/systemd-hwdb-update.service"
2008 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-journal-catalog-update.service"
2009 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-networkd.service"
2010 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-networkd.socket"
2011 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-resolved.service"
2016 local so_regex
='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2019 while read -r line
; do
2020 [[ "$line" = 'not a dynamic executable' ]] && break
2021 # Ignore errors about our own stuff missing. This is most likely caused
2022 # by ldd attempting to use the unprefixed RPATH.
2023 [[ "$line" =~ libsystemd.
*\ not\ found
]] && continue
2025 if [[ "$line" =~
$so_regex ]]; then
2026 file="${BASH_REMATCH[1]}"
2027 [[ -e "${initdir:?}/$file" ]] && continue
2028 inst_library
"$file"
2032 if [[ "$line" =~ not\ found
]]; then
2033 dfatal
"Missing a shared library required by $bin."
2034 dfatal
"Run \"ldd $bin\" to find out what it is."
2036 dfatal
"Cannot create a test image."
2039 done < <(LC_ALL
=C ldd
"$bin" 2>/dev
/null
)
2043 # make sure we don't get a stale LOOPDEV value from old times
2044 local _LOOPDEV
="${LOOPDEV:=}"
2045 # We don't want shellcheck to follow & check the $STATEFILE
2046 # shellcheck source=/dev/null
2047 [[ -e "$STATEFILE" ]] && .
"$STATEFILE"
2049 if [[ ! -d "$TESTDIR" ]]; then
2050 if [[ -z "$TESTDIR" ]]; then
2051 TESTDIR
="$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)"
2056 cat >"$STATEFILE" <<EOF
2062 IMAGE_PRIVATE
="${TESTDIR}/${IMAGE_NAME:?}.img"
2063 IMAGE_PUBLIC
="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img"
2067 initdir
="${TESTDIR:?}/root"
2072 get_cgroup_hierarchy
() {
2073 case "$(stat -c '%T' -f /sys/fs/cgroup)" in
2078 if [[ -d /sys
/fs
/cgroup
/unified
&& "$(stat -c '%T' -f /sys/fs/cgroup/unified)" == cgroup2fs
]]; then
2085 dfatal
"Failed to determine host's cgroup hierarchy"
2090 ## @brief Converts numeric logging level to the first letter of level name.
2092 # @param lvl Numeric logging level in range from 1 to 6.
2093 # @retval 1 if @a lvl is out of range.
2094 # @retval 0 if @a lvl is correct.
2095 # @result Echoes first letter of level name.
2108 ## @brief Internal helper function for _do_dlog()
2110 # @param lvl Numeric logging level.
2111 # @param msg Message.
2112 # @retval 0 It's always returned, even if logging failed.
2114 # @note This function is not supposed to be called manually. Please use
2115 # dtrace(), ddebug(), or others instead which wrap this one.
2117 # This function calls _do_dlog() either with parameter msg, or if
2118 # none is given, it will read standard input and will use every line as
2122 # dwarn "This is a warning"
2123 # echo "This is a warning" | dwarn
2124 LOG_LEVEL
="${LOG_LEVEL:-4}"
2129 [ -z "$LOG_LEVEL" ] && return 0
2131 [ "$lvl" -le "$LOG_LEVEL" ] ||
return 0
2132 lvlc
="$(_lvl2char "$lvl")" ||
return 0
2134 if [ $# -ge 1 ]; then
2137 while read -r line
; do
2138 echo "$lvlc: " "$line"
2143 ## @brief Logs message at TRACE level (6)
2145 # @param msg Message.
2146 # @retval 0 It's always returned, even if logging failed.
2150 if get_bool
"${debug:=}"; then
2155 ## @brief Logs message at DEBUG level (5)
2157 # @param msg Message.
2158 # @retval 0 It's always returned, even if logging failed.
2163 ## @brief Logs message at INFO level (4)
2165 # @param msg Message.
2166 # @retval 0 It's always returned, even if logging failed.
2170 if get_bool
"${debug:=}"; then
2175 ## @brief Logs message at WARN level (3)
2177 # @param msg Message.
2178 # @retval 0 It's always returned, even if logging failed.
2182 if get_bool
"${debug:=}"; then
2187 ## @brief Logs message at ERROR level (2)
2189 # @param msg Message.
2190 # @retval 0 It's always returned, even if logging failed.
2195 ## @brief Logs message at FATAL level (1)
2197 # @param msg Message.
2198 # @retval 0 It's always returned, even if logging failed.
2202 if get_bool
"${debug:=}"; then
2208 # Generic substring function. If $2 is in $1, return 0.
2209 strstr
() { [ "${1#*"$2"*}" != "$1" ]; }
2211 # normalize_path <path>
2212 # Prints the normalized path, where it removes any duplicated
2213 # and trailing slashes.
2215 # $ normalize_path ///test/test//
2219 set -- "${1//+(\/)//}"
2224 # convert_abs_rel <from> <to>
2225 # Prints the relative path, when creating a symlink to <to> from <from>.
2227 # $ convert_abs_rel /usr/bin/test /bin/test-2
2229 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
2231 local __current __absolute __abssize __cursize __newpath
2232 local -i __i __level
2234 set -- "$(normalize_path "${1:?}")" "$(normalize_path "${2:?}")"
2236 # corner case #1 - self looping link
2237 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
2239 # corner case #2 - own dir link
2240 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
2242 IFS
="/" read -ra __current
<<< "$1"
2243 IFS
="/" read -ra __absolute
<<< "$2"
2245 __abssize
=${#__absolute[@]}
2246 __cursize
=${#__current[@]}
2248 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
2251 if (( __level
> __abssize || __level
> __cursize
))
2257 for ((__i
= __level
; __i
< __cursize-1
; __i
++))
2259 if ((__i
> __level
))
2261 __newpath
=$__newpath"/"
2263 __newpath
=$__newpath".."
2266 for ((__i
= __level
; __i
< __abssize
; __i
++))
2268 if [[ -n $__newpath ]]
2270 __newpath
=$__newpath"/"
2272 __newpath
=$__newpath${__absolute[__i]}
2279 # Install a directory, keeping symlinks as on the original system.
2280 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
2281 # will create ${initdir}/lib64, ${initdir}/lib64/file,
2282 # and a symlink ${initdir}/lib -> lib64.
2285 local part
="${dir%/*}"
2288 [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there
2290 while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do
2295 # iterate over parent directories
2296 for file in $dir; do
2297 [[ -e "${initdir}/$file" ]] && continue
2298 if [[ -L $file ]]; then
2299 inst_symlink
"$file"
2302 mkdir
-m 0755 "${initdir}/$file" ||
return 1
2303 [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file"
2304 chmod u
+w
"${initdir}/$file"
2309 # $1 = file to copy to ramdisk
2310 # $2 (optional) Name for the file on the ramdisk
2311 # Location of the image dir is assumed to be $initdir
2312 # We never overwrite the target if it exists.
2314 [[ -f "${1:?}" ]] ||
return 1
2315 strstr
"$1" "/" ||
return 1
2318 local target
="${2:-$1}"
2319 if ! [[ -d ${initdir:?}/$target ]]; then
2320 [[ -e ${initdir}/$target ]] && return 0
2321 [[ -L ${initdir}/$target ]] && return 0
2322 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
2324 # install checksum files also
2325 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2326 inst
"${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
2328 ddebug
"Installing $src"
2329 cp --sparse=always
-pfL "$src" "${initdir}/$target"
2332 # find symlinks linked to given library file
2334 # Function searches for symlinks by stripping version numbers appended to
2335 # library filename, checks if it points to the same target and finally
2336 # prints the list of symlinks to stdout.
2339 # rev_lib_symlinks libfoo.so.8.1
2340 # output: libfoo.so.8 libfoo.so
2341 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
2342 rev_lib_symlinks
() {
2346 orig
="$(readlink -f "$1")"
2348 [[ "${fn}" =~ .
*\.so\..
* ]] ||
return 1
2350 until [[ "${fn##*.}" == so
]]; do
2352 [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}"
2358 # Same as above, but specialized to handle dynamic libraries.
2359 # It handles making symlinks according to how the original library
2363 local dest
="${2:-$1}"
2364 local reallib symlink
2366 strstr
"$1" "/" ||
return 1
2367 [[ -e ${initdir:?}/$dest ]] && return 0
2368 if [[ -L $src ]]; then
2369 # install checksum files also
2370 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2371 inst
"${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac"
2373 reallib
="$(readlink -f "$src")"
2374 inst_simple
"$reallib" "$reallib"
2375 inst_dir
"${dest%/*}"
2376 [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
2377 ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
2379 inst_simple "$src" "$dest"
2382 # Create additional symlinks. See rev_symlinks description.
2383 for symlink in $(rev_lib_symlinks "$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do
2384 if [[ ! -e "$initdir/$symlink" ]]; then
2385 ddebug "Creating extra symlink
: $symlink"
2386 inst_symlink "$symlink"
2391 # find a binary. If we were not passed the full path directly,
2392 # search in the usual places to find the binary.
2395 if [[ -z ${bin##/*} ]]; then
2396 if [[ -x "$bin" ]] || { strstr "$bin" ".so
" && ldd "$bin" &>/dev/null; }; then
2405 # Same as above, but specialized to install binary executables.
2406 # Install binary executable, and all shared library dependencies, if any.
2411 # In certain cases we might attempt to install a binary which is already
2412 # present in the test image, yet it's missing from the host system.
2413 # In such cases, let's check if the binary indeed exists in the image
2414 # before doing any other checks. If it does, immediately return with
2416 if [[ $# -eq 1 ]]; then
2417 for path in "" bin sbin usr/bin usr/sbin; do
2418 [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0
2422 bin="$
(find_binary
"$bin")" || return 1
2424 [[ -e "${initdir:?}/$target" ]] && return 0
2425 [[ -L "$bin" ]] && inst_symlink "$bin" "$target" && return 0
2428 local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2430 while read -r line; do
2431 [[ "$line" = 'not a dynamic executable' ]] && break
2433 # Ignore errors about our own stuff missing. This is most likely caused
2434 # by ldd attempting to use the unprefixed RPATH.
2435 [[ "$line" =~ libsystemd.*\ not\ found ]] && continue
2437 if [[ "$line" =~ $so_regex ]]; then
2438 file="${BASH_REMATCH[1]}"
2439 [[ -e "${initdir}/$file" ]] && continue
2440 inst_library "$file"
2444 if [[ "$line" =~ not\ found ]]; then
2445 dfatal "Missing a shared library required by
$bin.
"
2446 dfatal "Run
\"ldd
$bin\" to
find out what it is.
"
2448 dfatal "Cannot create a
test image.
"
2451 done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
2452 inst_simple "$bin" "$target"
2455 # same as above, except for shell scripts.
2456 # If your shell script does not start with shebang, it is not a shell script.
2458 local bin line shebang_regex
2459 bin="$
(find_binary
"${1:?}")" || return 1
2462 read -r -n 80 line <"$bin"
2463 # If debug is set, clean unprintable chars to prevent messing up the term
2464 get_bool "${debug:=}" && line="$
(echo -n "$line" |
tr -c -d '[:print:][:space:]')"
2465 shebang_regex='(#! *)(/[^ ]+).*'
2466 [[ "$line" =~ $shebang_regex ]] || return 1
2467 inst "${BASH_REMATCH[2]}" && inst_simple "$bin" "$@
"
2470 # same as above, but specialized for symlinks
2473 local target="${2:-$src}"
2476 strstr "$src" "/" || return 1
2477 [[ -L "$src" ]] || return 1
2478 [[ -L "${initdir:?}/$target" ]] && return 0
2479 realsrc="$
(readlink
-f "$src")"
2480 if ! [[ -e "$initdir/$realsrc" ]]; then
2481 if [[ -d "$realsrc" ]]; then
2487 [[ ! -e "$initdir/${target%/*}" ]] && inst_dir "${target%/*}"
2488 [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}"
2489 ln -sfn -- "$
(convert_abs_rel
"${target}" "${realsrc}")" "$initdir/$target"
2492 # attempt to install any programs specified in a udev rule
2493 inst_rule_programs() {
2497 sed -rn 's/^.*?PROGRAM==?"([^
"]+).*$/\1/p' "$rule" | while read -r prog; do
2498 if [ -x "/lib
/udev
/$prog" ]; then
2499 bin="/lib
/udev
/$prog"
2501 if ! bin="$
(find_binary
"$prog")"; then
2502 dinfo "Skipping program
$prog used
in udev rule $
(basename "$rule") as it cannot be found
"
2507 #dinfo "Installing
$_bin due to it
's use in the udev rule $(basename $1)"
2508 image_install "$bin"
2512 # udev rules always get installed in the same place, so
2513 # create a function to install them to make life simpler.
2515 local target=/etc/udev/rules.d
2518 inst_dir "/lib/udev/rules.d"
2520 for rule in "$@"; do
2521 if [ "${rule#/}" = "$rule" ]; then
2522 for r in /lib/udev/rules.d /etc/udev/rules.d; do
2523 if [[ -f "$r/$rule" ]]; then
2525 inst_simple "$found"
2526 inst_rule_programs "$found"
2531 if [[ -f "${r}${rule}" ]]; then
2533 inst_simple "$found" "$target/${found##*/}"
2534 inst_rule_programs "$found"
2537 [[ $found ]] || dinfo "Skipping udev rule: $rule"
2542 # general purpose installation function
2543 # Same args as above.
2548 [[ ! "$initdir" && -d "$2" ]] && export initdir="$2"
2549 [[ "$initdir" = "$2" ]] && set "$1"
2552 [[ -z "$initdir" ]] && export initdir="$2"
2556 dfatal "inst only takes 1 or 2 or 3 arguments"
2562 for fun in inst_symlink inst_script inst_binary inst_simple; do
2563 "$fun" "$@" && return 0
2566 dwarn "Failed to install '$1'"
2570 # install any of listed files
2572 # If first argument is '-d' and second some destination path, first accessible
2573 # source is installed into this path, otherwise it will installed in the same
2574 # path as source. If none of listed files was installed, function return 1.
2575 # On first successful installation it returns with 0 status.
2579 # inst_any -d /bin/foo /bin/bar /bin/baz
2581 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
2586 [[ "${1:?}" = '-d' ]] && dest="${2:?}" && shift 2
2588 for file in "$@"; do
2589 if [[ -e "$file" ]]; then
2590 [[ -n "$dest" ]] && inst "$file" "$dest" && return 0
2591 inst "$file" && return 0
2598 # image_install [-o ] <file> [<file> ... ]
2599 # Install <file> to the test image
2600 # -o optionally install the <file> and don't fail
, if it is not there
2605 if [[ "$prog" = '-o' ]]; then
2610 for prog
in "$@"; do
2611 if ! inst
"$prog" ; then
2612 if get_bool
"$optional"; then
2613 dinfo
"Skipping program $prog as it cannot be found and is" \
2614 "flagged to be optional"
2616 dfatal
"Failed to install $prog"
2623 # Install a single kernel module along with any firmware it may require.
2624 # $1 = full path to kernel module to install
2625 install_kmod_with_fw
() {
2626 local module
="${1:?}"
2627 # no need to go further if the module is already installed
2628 [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*"/lib/modules/$KERNEL_VER/"}" ]] && return 0
2629 [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0
2631 [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}"
2633 inst_simple
"$module" "/lib/modules/$KERNEL_VER/${module##*"/lib/modules/$KERNEL_VER/"}" ||
return $?
2635 local modname
="${module##*/}"
2636 local fwdir found fw
2637 modname
="${modname%.ko*}"
2639 while read -r fw
; do
2641 for fwdir
in /lib
/firmware
/updates
/lib
/firmware
; do
2642 if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then
2643 inst_simple
"$fwdir/$fw" "/lib/firmware/$fw"
2647 if ! get_bool
"$found"; then
2648 if ! grep -qe "\<${modname//-/_}\>" /proc
/modules
; then
2649 dinfo
"Possible missing firmware \"${fw}\" for kernel module" \
2652 dwarn
"Possible missing firmware \"${fw}\" for kernel module" \
2656 done < <(modinfo
-k "$KERNEL_VER" -F firmware
"$module" 2>/dev
/null
)
2660 # Do something with all the dependencies of a kernel module.
2661 # Note that kernel modules depend on themselves using the technique we use
2662 # $1 = function to call for each dependency we find
2663 # It will be passed the full path to the found kernel module
2664 # $2 = module to get dependencies for
2665 # rest of args = arguments to modprobe
2666 for_each_kmod_dep
() {
2673 while read -r cmd modpath _
; do
2674 [[ "$cmd" = insmod
]] ||
continue
2675 "$func" "$modpath" ||
return $?
2677 done < <(modprobe
"$@" --ignore-install --show-depends "$kmod")
2679 ! get_bool
"$found" && return 1
2683 # instmods [-c] <kernel module> [<kernel module> ... ]
2684 # instmods [-c] <kernel subsystem>
2685 # install kernel modules along with all their dependencies.
2686 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
2687 # FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5
2690 if [[ $# -ge 0 && "$1" = '-c' ]]; then
2698 local mod_dir
="/lib/modules/${KERNEL_VER:?}/"
2702 if [ -f "${mod_dir}/modules.${mod#=}" ]; then
2704 [[ "$mpargs" ]] && echo "$mpargs"
2705 cat "${mod_dir}/modules.${mod#=}"
2709 [[ "$mpargs" ]] && echo "$mpargs"
2710 find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f
-printf '%f\n'
2718 # Do not load this diagnostic-only module
2723 # if we are already installed, skip this module and go on
2725 [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return
2727 # We use '-d' option in modprobe only if modules prefix path
2728 # differs from default '/'. This allows us to use Dracut with
2729 # old version of modprobe which doesn't have '-d' option.
2730 local mod_dirname
=${mod_dir%%/lib/modules/*}
2731 [[ -n ${mod_dirname} ]] && mod_dirname
="-d ${mod_dirname}/"
2733 # ok, load the module, all its dependencies, and any firmware
2735 for_each_kmod_dep install_kmod_with_fw
"$mod" \
2736 --set-version "$KERNEL_VER" \
2737 ${mod_dirname:+"$mod_dirname"} \
2738 ${mpargs:+"$mpargs"}
2747 if [[ $# -eq 0 ]]; then # filenames from stdin
2748 while read -r mod
; do
2749 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2750 dfatal
"Failed to install $mod"
2756 for mod
in "$@"; do # filenames as arguments
2757 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2758 dfatal
"Failed to install $mod"
2767 local mountpoint
="${1:?}"
2768 if mountpoint
-q "$mountpoint"; then
2769 ddebug
"umount $mountpoint"
2770 umount
"$mountpoint"
2774 # can be overridden in specific test
2775 test_setup_cleanup
() {
2780 # (post-test) cleanup should always ignore failure and cleanup as much as possible
2783 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
2784 [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC"
2785 # If multiple setups/cleans are ran in parallel, this can cause a race
2786 if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then
2787 rm -vf "${IMAGESTATEDIR}/default.img"
2789 [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR"
2790 [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE"
2794 # can be overridden in specific test
2799 test_cleanup_again
() {
2800 [ -n "$TESTDIR" ] ||
return
2801 rm -rf "$TESTDIR/unprivileged-nspawn-root"
2802 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
2805 test_create_image
() {
2806 create_empty_image_rootdir
2808 # Create what will eventually be our root filesystem onto an overlay
2811 setup_basic_environment
2816 if get_bool
"${TEST_REQUIRE_INSTALL_TESTS:?}" && \
2817 command -v meson
>/dev
/null
&& \
2818 [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
2819 dfatal
"$BUILD_DIR needs to be built with -Dinstall-tests=true"
2823 if [ -e "${IMAGE_PRIVATE:?}" ]; then
2824 echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")"
2827 if [ ! -e "${IMAGE_PUBLIC:?}" ]; then
2828 # default.img is the base that every test uses and optionally appends to
2829 if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] ||
[ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then
2830 # Create the backing public image, but then completely unmount
2831 # it and drop the loopback device responsible for it, since we're
2832 # going to symlink/copy the image and mount it again from
2834 local image_old
="${IMAGE_PUBLIC}"
2835 if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
2836 IMAGE_PUBLIC
="${IMAGESTATEDIR}/default.img"
2842 IMAGE_PUBLIC
="${image_old}"
2844 if [ "${IMAGE_NAME:?}" != "default" ] && ! get_bool
"${TEST_FORCE_NEWIMAGE}"; then
2845 cp -v "$(realpath "${IMAGESTATEDIR}/default.img
")" "$IMAGE_PUBLIC"
2850 declare -f -F test_append_files
>/dev
/null
&& hook_defined
=yes || hook_defined
=no
2852 echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")"
2853 if get_bool
"$TEST_PARALLELIZE" || get_bool
"$hook_defined"; then
2854 cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
2856 ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
2860 # We want to test all services in TEST-01-BASIC, but mask them in
2862 if [[ "${TESTID:?}" != "01" ]]; then
2863 dinfo
"Masking supporting services"
2864 mask_supporting_services
2867 if get_bool
"$hook_defined"; then
2868 test_append_files
"${initdir:?}"
2876 local test_id
="${1:?}"
2879 if ! get_bool
"${TEST_NO_QEMU:=}"; then
2880 if run_qemu
"$test_id"; then
2881 check_result_qemu ||
{ echo "qemu test failed"; return 1; }
2883 dwarn
"can't run qemu, skipping"
2886 if ! get_bool
"${TEST_NO_NSPAWN:=}"; then
2888 if run_nspawn
"${initdir:?}" "$test_id"; then
2889 check_result_nspawn
"$initdir" ||
{ echo "nspawn-root test failed"; return 1; }
2891 dwarn
"can't run systemd-nspawn, skipping"
2894 if get_bool
"${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
2895 dir
="$TESTDIR/unprivileged-nspawn-root"
2896 if NSPAWN_ARGUMENTS
="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn
"$dir" "$test_id"; then
2897 check_result_nspawn
"$dir" ||
{ echo "unprivileged-nspawn-root test failed"; return 1; }
2899 dwarn
"can't run systemd-nspawn, skipping"
2907 if [[ $UID != "0" ]]; then
2908 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
2912 if get_bool
"${TEST_NO_QEMU:=}" && get_bool
"${TEST_NO_NSPAWN:=}"; then
2913 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both qemu and nspawn disabled" >&2
2917 if get_bool
"${TEST_QEMU_ONLY:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
2918 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: qemu-only tests requested" >&2
2922 if get_bool
"${TEST_PREFER_NSPAWN:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
2927 [[ "$libdir" ]] ||
for libdir
in /lib64
/lib
; do
2928 [[ -d $libdir ]] && libdirs
+=" $libdir" && break
2931 [[ "$usrlibdir" ]] ||
for usrlibdir
in /usr
/lib64
/usr
/lib
; do
2932 [[ -d $usrlibdir ]] && libdirs
+=" $usrlibdir" && break
2935 mkdir
-p "$STATEDIR"
2940 if [ -n "${SUDO_USER}" ]; then
2941 ddebug
"Making ${TESTDIR:?} readable for ${SUDO_USER} (acquired from sudo)"
2942 setfacl
-m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}"
2945 testname
="$(basename "$PWD")"
2947 while (($# > 0)); do
2950 echo "${testname} RUN: $TEST_DESCRIPTION"
2953 if [ $ret -eq 0 ]; then
2954 echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
2956 echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
2961 echo "${testname} SETUP: $TEST_DESCRIPTION"
2966 echo "${testname} CLEANUP: $TEST_DESCRIPTION"
2970 echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
2975 echo -n "${testname}: $TEST_DESCRIPTION "
2976 # Do not use a subshell, otherwise cleanup variables (LOOPDEV) will be lost
2977 # and loop devices will leak
2978 test_setup
</dev
/null
>"$TESTLOG" 2>&1 || ret
=$?
2979 if [ $ret -eq 0 ]; then
2980 test_setup_cleanup
</dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
2982 if [ $ret -eq 0 ]; then
2983 test_run
"$TESTID" </dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
2986 if [ $ret -eq 0 ]; then