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:-1800}"
28 NSPAWN_TIMEOUT
="${NSPAWN_TIMEOUT:-1800}"
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 # Note that defining a different IMAGE_NAME in a test setup script will only result
34 # in default.img being copied and renamed. It can then be extended by defining
35 # a test_append_files() function. The $1 parameter will be the root directory.
36 # To force creating a new image from scratch (eg: to encrypt it), also define
37 # TEST_FORCE_NEWIMAGE=1 in the test setup script.
38 IMAGE_NAME
=${IMAGE_NAME:-default}
39 STRIP_BINARIES
="${STRIP_BINARIES:-yes}"
40 TEST_REQUIRE_INSTALL_TESTS
="${TEST_REQUIRE_INSTALL_TESTS:-1}"
41 TEST_PARALLELIZE
="${TEST_PARALLELIZE:-0}"
42 TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED
="${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED:-1}"
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 # Since in Bash we can have only one handler per signal, let's overcome this
64 # limitation by having one global handler for the EXIT signal which executes
65 # all registered handlers
70 # Run the EXIT handlers in reverse order
71 for ((i
= ${#_AT_EXIT_HANDLERS[@]} - 1; i
>= 0; i--
)); do
72 ddebug
"Running EXIT handler '${_AT_EXIT_HANDLERS[$i]}'"
73 "${_AT_EXIT_HANDLERS[$i]}"
79 add_at_exit_handler
() {
82 if [[ "$(type -t "$handler")" != "function" ]]; then
83 dfatal
"'$handler' is not a function"
87 _AT_EXIT_HANDLERS
+=("$handler")
90 # Decide if we can (and want to) run qemu with KVM acceleration.
91 # Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
92 # check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
93 # running under KVM. If these conditions are met, enable KVM (and possibly
94 # nested KVM), otherwise disable it.
95 if get_bool
"${TEST_NESTED_KVM:=}" ||
(! get_bool
"${TEST_NO_KVM:=}" && ! systemd-detect-virt
-qv); then
101 if ! ROOTLIBDIR
=$
(pkg-config
--variable=systemdutildir systemd
); then
102 echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
103 ROOTLIBDIR
=/usr
/lib
/systemd
106 # The calling test.sh scripts have TEST_BASE_DIR set via their Makefile, but we don't need them to provide it
107 TEST_BASE_DIR
=${TEST_BASE_DIR:-$(realpath "$(dirname "${BASH_SOURCE[0]}")")}
108 TEST_UNITS_DIR
="$(realpath "$TEST_BASE_DIR/units
")"
109 SOURCE_DIR
=$
(realpath
"$TEST_BASE_DIR/..")
110 TOOLS_DIR
="$SOURCE_DIR/tools"
111 # These variables are used by test scripts
112 export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
114 # note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
115 if get_bool
"${NO_BUILD:=}"; then
116 BUILD_DIR
="$SOURCE_DIR"
117 elif ! BUILD_DIR
="$("$TOOLS_DIR"/find-build-dir.sh)"; then
118 echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
122 PATH_TO_INIT
="$ROOTLIBDIR/systemd"
123 SYSTEMD_JOURNALD
="${SYSTEMD_JOURNALD:-$(command -v "$BUILD_DIR/systemd-journald" || command -v "$ROOTLIBDIR/systemd-journald")}"
124 SYSTEMD_JOURNAL_REMOTE
="${SYSTEMD_JOURNAL_REMOTE:-$(command -v "$BUILD_DIR/systemd-journal-remote" || command -v "$ROOTLIBDIR/systemd-journal-remote" || echo "")}"
125 SYSTEMD
="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR/systemd")}"
126 SYSTEMD_NSPAWN
="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
127 JOURNALCTL
="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
128 SYSTEMCTL
="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}"
130 TESTFILE
="${BASH_SOURCE[1]}"
131 if [ -z "$TESTFILE" ]; then
132 echo "ERROR: test-functions must be sourced from one of the TEST-*/test.sh scripts" >&2
135 TESTNAME
="$(basename "$
(dirname "$(realpath "$TESTFILE")")")"
136 STATEDIR
="$BUILD_DIR/test/$TESTNAME"
137 STATEFILE
="$STATEDIR/.testdir"
138 IMAGESTATEDIR
="$STATEDIR/.."
139 TESTLOG
="$STATEDIR/test.log"
141 if ! [[ "$TESTNAME" =~ ^TEST\
-([0-9]+)\
-.
+$
]]; then
142 echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2
145 TESTID
="${BASH_REMATCH[1]:?}"
147 if [[ ! -f "$TEST_UNITS_DIR/testsuite-$TESTID.service" ]]; then
148 echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/testsuite-$TESTID.service" >&2
263 is_built_with_asan
() {
266 if ! type -P objdump
>/dev
/null
; then
267 ddebug
"Failed to find objdump. Assuming systemd hasn't been built with ASAN."
271 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
273 _asan_calls
="$(objdump -dC "$_bin" | grep -E "(callq?|brasl?|bl
)\s.
+__asan
" -c)"
274 if ((_asan_calls
< 1000)); then
281 is_built_with_coverage
() {
282 if get_bool
"${NO_BUILD:=}" ||
! command -v meson
>/dev
/null
; then
286 meson configure
"${BUILD_DIR:?}" |
grep 'b_coverage' |
awk '{ print $2 }' |
grep -q 'true'
289 IS_BUILT_WITH_ASAN
=$
(is_built_with_asan
"$SYSTEMD_JOURNALD" && echo yes ||
echo no
)
290 IS_BUILT_WITH_COVERAGE
=$
(is_built_with_coverage
&& echo yes ||
echo no
)
292 if get_bool
"$IS_BUILT_WITH_ASAN"; then
294 SKIP_INITRD
="${SKIP_INITRD:-yes}"
295 PATH_TO_INIT
=$ROOTLIBDIR/systemd-under-asan
296 QEMU_MEM
="${QEMU_MEM:-2G}"
297 QEMU_SMP
="${QEMU_SMP:-4}"
299 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
300 if ASAN_RT_NAME
="$(awk '/libasan.so/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
302 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-gcc} --print-file-name "$ASAN_RT_NAME")")"
303 elif ASAN_RT_NAME
="$(awk '/libclang_rt.asan/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then
305 ASAN_RT_PATH
="$(readlink -f "$
(${CC:-clang} --print-file-name "$ASAN_RT_NAME")")"
307 # As clang's ASan DSO is usually in a non-standard path, let's check if
308 # the environment is set accordingly. If not, warn the user and exit.
309 # We're not setting the LD_LIBRARY_PATH automagically here, because
310 # user should encounter (and fix) the same issue when running the unit
312 if ldd
"$SYSTEMD" |
grep -q "libclang_rt.asan.*not found"; then
313 echo >&2 "clang's ASan DSO ($ASAN_RT_NAME) is not present in the runtime library path"
314 echo >&2 "Consider setting LD_LIBRARY_PATH=${ASAN_RT_PATH%/*}"
318 echo >&2 "systemd is not linked against the ASan DSO"
319 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
323 echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
327 QEMU_BIN
="${QEMU_BIN:-""}"
328 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
329 if get_bool
"$QEMU_KVM"; then
330 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)"
333 [[ -n "$ARCH" ]] || ARCH
="$(uname -m)"
336 # QEMU's own build system calls it qemu-system-x86_64
337 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)"
340 # new i386 version of QEMU
341 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)"
343 # i386 version of QEMU
344 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu 2>/dev/null | grep '^/' -m1)"
347 [[ -n "$QEMU_BIN" ]] || QEMU_BIN
="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)"
351 if [[ ! -e "$QEMU_BIN" ]]; then
352 echo "Could not find a suitable qemu binary" >&2
357 # Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu
358 # returns 0 if newer or equal
360 # returns 2 if failing
362 find_qemu_bin ||
return 2
364 # get version from binary
366 qemu_ver
="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')"
368 # Check version string format
369 echo "$qemu_ver" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
370 echo "$1" |
grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' ||
return 2
372 # compare as last command to return that value
373 printf "%s\n%s\n" "$1" "$qemu_ver" |
sort -V -C
376 # Return 0 if qemu did run (then you must check the result state/logs for actual
377 # success), or 1 if qemu is not available.
379 # If the test provided its own initrd, use it (e.g. TEST-24)
380 if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then
381 INITRD
="$TESTDIR/initrd.img"
384 if [ -f /etc
/machine-id
]; then
385 read -r MACHINE_ID
</etc
/machine-id
386 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
387 && INITRD
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
388 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
389 && KERNEL_BIN
="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
394 rm -f "$initdir"/{testok
,failed
,skipped
}
395 # make sure the initdir is not mounted to avoid concurrent access
399 if [[ ! "$KERNEL_BIN" ]]; then
400 if get_bool
"$LOOKS_LIKE_ARCH"; then
401 KERNEL_BIN
=/boot
/vmlinuz-linux
403 [ "$ARCH" ] || ARCH
=$
(uname
-m)
406 # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
407 # uses the "standard" vmlinuz- prefix
408 [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN
="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
412 KERNEL_BIN
="/boot/vmlinuz-$KERNEL_VER"
418 local default_fedora_initrd
="/boot/initramfs-${KERNEL_VER}.img"
419 local default_debian_initrd
="/boot/initrd.img-${KERNEL_VER}"
420 local default_arch_initrd
="/boot/initramfs-linux-fallback.img"
421 local default_suse_initrd
="/boot/initrd-${KERNEL_VER}"
422 if [[ ! "$INITRD" ]]; then
423 if [[ -e "$default_fedora_initrd" ]]; then
424 INITRD
="$default_fedora_initrd"
425 elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then
426 INITRD
="$default_debian_initrd"
427 elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
428 INITRD
="$default_arch_initrd"
429 elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
430 INITRD
="$default_suse_initrd"
434 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
435 # i.e. use the number of online CPUs on the host machine. If the nproc utility
436 # is not installed or there's some other error when calling it, fall back
437 # to the original value (QEMU_SMP=1).
438 if [[ -z "${QEMU_SMP:=}" ]]; then
439 if ! QEMU_SMP
=$
(nproc
); then
440 dwarn
"nproc utility is not installed, falling back to QEMU_SMP=1"
445 find_qemu_bin ||
return 1
447 # Umount initdir to avoid concurrent access to the filesystem
448 _umount_dir
"$initdir"
450 local kernel_params
=()
451 local qemu_options
=()
452 local qemu_cmd
=("$QEMU_BIN")
454 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
455 kernel_params
+=("systemd.unified_cgroup_hierarchy=yes")
456 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
457 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes")
458 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
459 kernel_params
+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no")
460 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
461 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
465 if get_bool
"$LOOKS_LIKE_SUSE"; then
466 kernel_params
+=("rd.hostonly=0")
470 "root=LABEL=systemd_boot"
477 "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
478 "systemd.unit=testsuite.target"
479 "systemd.wants=testsuite-$1.service"
482 if ! get_bool
"$INTERACTIVE_DEBUG"; then
487 "systemd.wants=end.service"
491 [ -e "$IMAGE_PRIVATE" ] && image
="$IMAGE_PRIVATE" || image
="$IMAGE_PUBLIC"
495 -m "${QEMU_MEM:-768M}"
497 -kernel "$KERNEL_BIN"
498 -drive "format=raw,cache=unsafe,file=$image"
499 -device "virtio-rng-pci,max-bytes=1024,period=1000"
502 if [[ -n "${QEMU_OPTIONS:=}" ]]; then
503 local user_qemu_options
504 read -ra user_qemu_options
<<< "$QEMU_OPTIONS"
505 qemu_options
+=("${user_qemu_options[@]}")
508 if [[ -n "${KERNEL_APPEND:=}" ]]; then
509 local user_kernel_append
510 readarray user_kernel_append
<<< "$KERNEL_APPEND"
511 kernel_params
+=("${user_kernel_append[@]}")
514 if [[ "$INITRD" ]] && ! get_bool
"$SKIP_INITRD"; then
515 qemu_options
+=(-initrd "$INITRD")
518 # Let's use KVM if possible
519 if [[ -c /dev
/kvm
]] && get_bool
$QEMU_KVM; then
520 qemu_options
+=(-machine "accel=kvm" -enable-kvm -cpu host)
523 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
524 qemu_cmd
=(timeout
--foreground "$QEMU_TIMEOUT" "$QEMU_BIN")
527 (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]}")
529 if [ "$rc" -eq 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
530 derror
"Test timed out after ${QEMU_TIMEOUT}s"
533 [ "$rc" != 0 ] && derror
"qemu failed with exit code $rc"
538 # Return 0 if nspawn did run (then you must check the result state/logs for actual
539 # success), or 1 if nspawn is not available.
541 [[ -d /run
/systemd
/system
]] ||
return 1
542 rm -f "${initdir:?}"/{testok
,failed
,skipped
}
545 local nspawn_options
=(
547 "--kill-signal=SIGKILL"
549 "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
550 "--machine=TEST-$TESTID"
552 local kernel_params
=(
554 "systemd.unit=testsuite.target"
555 "systemd.wants=testsuite-$2.service"
558 if ! get_bool
"$INTERACTIVE_DEBUG"; then
559 kernel_params
+=("systemd.wants=end.service")
562 if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then
563 local user_nspawn_arguments
564 read -ra user_nspawn_arguments
<<< "$NSPAWN_ARGUMENTS"
565 nspawn_options
+=("${user_nspawn_arguments[@]}")
568 if [[ -n "${KERNEL_APPEND:=}" ]]; then
569 local user_kernel_append
570 readarray user_kernel_append
<<< "$KERNEL_APPEND"
571 kernel_params
+=("${user_kernel_append[@]}")
574 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
575 dwarn
"nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
577 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ||
"$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
578 nspawn_cmd
+=(env
"SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY")
579 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
580 nspawn_cmd
+=(env
"--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY")
582 dfatal
"Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
586 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
587 nspawn_cmd
+=(timeout
--foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN")
589 nspawn_cmd
+=("$SYSTEMD_NSPAWN")
592 (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}")
594 if [ "$rc" -eq 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
595 derror
"Test timed out after ${NSPAWN_TIMEOUT}s"
598 [ "$rc" != 0 ] && derror
"nspawn failed with exit code $rc"
603 # Build two very minimal root images, with two units, one is the same and one is different across them
604 install_verity_minimal
() {
605 dinfo
"Set up a set of minimal images for verity verification"
606 if [ -e "$initdir/usr/share/minimal.raw" ]; then
609 if ! command -v mksquashfs
>/dev
/null
2>&1; then
610 dfatal
"mksquashfs not found"
613 if ! command -v veritysetup
>/dev
/null
2>&1; then
614 dfatal
"veritysetup not found"
617 # Local modifications of some global variables is intentional in this
619 # shellcheck disable=SC2030
628 oldinitdir
="$initdir"
629 rm -rfv "$TESTDIR/minimal"
630 export initdir
="$TESTDIR/minimal"
631 # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
632 mkdir
-p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
636 # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
637 # issue, thus falsely triggering SC2030 in this case
638 # See: koalaman/shellcheck#1409
639 if [[ -v ASAN_RT_PATH
]]; then
640 # If we're compiled with ASan, install the ASan RT (and its dependencies)
641 # into the verity images to get rid of the annoying errors about
642 # missing $LD_PRELOAD libraries.
643 inst_libs
"$ASAN_RT_PATH"
644 inst_library
"$ASAN_RT_PATH"
646 cp "$os_release" "$initdir/usr/lib/os-release"
647 ln -s ..
/usr
/lib
/os-release
"$initdir/etc/os-release"
648 touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
649 touch "$initdir/opt/some_file"
650 echo MARKER
=1 >>"$initdir/usr/lib/os-release"
651 echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
652 cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
654 ExecStartPre=cat /usr/lib/os-release
657 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
659 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
660 veritysetup format
"$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
661 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
663 sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
664 rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
665 cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
667 mksquashfs
"$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
668 veritysetup format
"$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
669 grep '^Root hash:' | cut
-f2 |
tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
671 # Rolling distros like Arch do not set VERSION_ID
673 if grep -q "^VERSION_ID=" "$os_release"; then
674 version_id
="$(grep "^VERSION_ID
=" "$os_release")"
677 export initdir
="$TESTDIR/app0"
678 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
679 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
680 echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
681 cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
685 ExecStart=/opt/script0.sh
686 TemporaryFileSystem=/var/lib
688 RuntimeDirectory=app0
690 cat >"$initdir/opt/script0.sh" <<EOF
693 test -e /usr/lib/os-release
694 echo bar > \${STATE_DIRECTORY}/foo
695 cat /usr/lib/extension-release.d/extension-release.app0
697 chmod +x
"$initdir/opt/script0.sh"
698 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/some_file"
699 mksquashfs
"$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
701 export initdir
="$TESTDIR/app1"
702 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
703 grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
704 ( echo "${version_id}"
705 echo "SYSEXT_SCOPE=portable"
706 echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
707 setfattr
-n user.extension-release.strict
-v false
"$initdir/usr/lib/extension-release.d/extension-release.app2"
708 cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
712 ExecStart=/opt/script1.sh
714 RuntimeDirectory=app1
716 cat >"$initdir/opt/script1.sh" <<EOF
719 test -e /usr/lib/os-release
720 echo baz > \${STATE_DIRECTORY}/foo
721 cat /usr/lib/extension-release.d/extension-release.app2
723 chmod +x
"$initdir/opt/script1.sh"
724 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/other_file"
725 mksquashfs
"$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
727 export initdir
="$TESTDIR/app-nodistro"
728 mkdir
-p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
730 echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro"
731 echo MARKER
=1 >"$initdir/usr/lib/systemd/system/some_file"
732 mksquashfs
"$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend
736 setup_basic_environment
() {
737 # create the basic filesystem layout
741 install_missing_libraries
762 has_user_dbus_socket
&& install_user_dbus
767 generate_module_dependencies
768 if get_bool
"$IS_BUILT_WITH_ASAN"; then
771 if get_bool
"$TEST_INSTALL_VERITY_MINIMAL"; then
772 install_verity_minimal
777 dinfo
"Setup SELinux"
778 # don't forget KERNEL_APPEND='... selinux=1 ...'
779 if ! get_bool
"$SETUP_SELINUX"; then
780 dinfo
"SETUP_SELINUX != yes, skipping SELinux configuration"
784 local conf_dir
=/etc
/selinux
785 local fixfiles_tools
=(bash uname
cat sort uniq awk grep egrep head expr find rm secon setfiles
)
787 # Make sure the following statement can't expand to "/" to prevent
788 # a potential where-are-my-backups situation
789 rm -rf "${initdir:?}/$conf_dir"
790 if ! cp -ar "$conf_dir" "$initdir/$conf_dir"; then
791 dfatal
"Failed to copy $conf_dir"
795 touch "$initdir/.autorelabel"
796 mkdir
-p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
797 ln -sf ..
/autorelabel.service
"$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
799 image_install
"${fixfiles_tools[@]}"
800 image_install fixfiles
801 image_install sestatus
805 if ! type -p valgrind
; then
806 dfatal
"Failed to install valgrind"
810 local valgrind_bins valgrind_libs valgrind_dbg_and_supp
812 readarray
-t valgrind_bins
< <(strace
-e execve valgrind
/bin
/true
2>&1 >/dev
/null | perl
-lne 'print $1 if /^execve\("([^"]+)"/')
813 image_install
"${valgrind_bins[@]}"
815 readarray
-t valgrind_libs
< <(LD_DEBUG
=files valgrind
/bin
/true
2>&1 >/dev
/null | perl
-lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
816 image_install
"${valgrind_libs[@]}"
818 readarray
-t valgrind_dbg_and_supp
< <(
819 strace
-e open valgrind
/bin
/true
2>&1 >/dev
/null |
820 perl
-lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
822 image_install
"${valgrind_dbg_and_supp[@]}"
825 create_valgrind_wrapper
() {
826 local valgrind_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-valgrind"
827 ddebug
"Create $valgrind_wrapper"
828 cat >"$valgrind_wrapper" <<EOF
831 mount -t proc proc /proc
832 exec valgrind --leak-check=full --track-fds=yes --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
834 chmod 0755 "$valgrind_wrapper"
837 create_asan_wrapper
() {
838 local asan_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-asan"
839 dinfo
"Create ASan wrapper as '$asan_wrapper'"
841 [[ -z "$ASAN_RT_PATH" ]] && dfatal
"ASAN_RT_PATH is empty, but it shouldn't be"
843 # clang: install llvm-symbolizer to generate useful reports
844 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
845 [[ "$ASAN_COMPILER" == "clang" ]] && image_install
"llvm-symbolizer"
847 cat >"$asan_wrapper" <<EOF
852 echo "ASan RT: $ASAN_RT_PATH"
853 if [[ ! -e "$ASAN_RT_PATH" ]]; then
854 echo >&2 "Couldn't find ASan RT at '$ASAN_RT_PATH', can't continue"
858 DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
859 DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
860 DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
862 # Create a simple environment file which can be included by systemd services
863 # that need it (i.e. services that utilize DynamicUser=true and bash, etc.)
864 cat >/usr/lib/systemd/systemd-asan-env <<INNER_EOF
865 LD_PRELOAD=$ASAN_RT_PATH
866 ASAN_OPTIONS=$DEFAULT_ASAN_OPTIONS
867 LSAN_OPTIONS=detect_leaks=0
868 UBSAN_OPTIONS=$DEFAULT_UBSAN_OPTIONS
871 # As right now bash is the PID 1, we can't expect PATH to have a sane value.
872 # Let's make one to prevent unexpected "<bin> not found" issues in the future
873 export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
875 mount -t proc proc /proc
876 mount -t sysfs sysfs /sys
877 mount -o remount,rw /
879 DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT ASAN_RT_PATH=$ASAN_RT_PATH"
881 if [[ "$ASAN_COMPILER" == "clang" ]]; then
882 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
883 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
884 # runtime ASan DSO is in a non-standard (library) path.
885 echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf
888 echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
889 echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
890 echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
892 # ASAN and syscall filters aren't compatible with each other.
893 find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
895 # The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
896 # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
897 JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
898 mkdir -p "\$JOURNALD_CONF_DIR"
899 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"
901 # Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
902 # Let's try to catch them by redirecting stderr (and stdout just in case) to a file
903 # See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
904 printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf"
906 # 90s isn't enough for some services to finish when literally everything is run
907 # under ASan+UBSan in containers, which, in turn, are run in VMs.
908 # Let's limit which environments such services should be executed in.
909 mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
910 printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
912 # Let's override another hard-coded timeout that kicks in too early
913 mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
914 printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf
916 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
917 exec "$ROOTLIBDIR/systemd" "\$@"
920 chmod 0755 "$asan_wrapper"
923 create_strace_wrapper
() {
924 local strace_wrapper
="$initdir/$ROOTLIBDIR/systemd-under-strace"
925 ddebug
"Create $strace_wrapper"
926 cat >"$strace_wrapper" <<EOF
929 exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd" "\$@"
931 chmod 0755 "$strace_wrapper"
936 image_install
/sbin
/fsck
*
937 image_install
-o /bin
/fsck
*
939 # fskc.reiserfs calls reiserfsck. so, install it
940 image_install
-o reiserfsck
942 # we use mkfs in system-repart tests
943 image_install
/sbin
/mkfs.ext4
944 image_install
/sbin
/mkfs.vfat
948 dinfo
"Install modules"
952 instmods nls_ascii
=nls
955 instmods scsi_debug uinput
957 if get_bool
"$LOOKS_LIKE_SUSE"; then
965 instmods dm_crypt
=crypto
967 image_install
"${ROOTLIBDIR:?}"/system
/dm-event.
{service
,socket
}
968 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
969 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
970 # and since buster/bionic 95-dm-notify.rules
971 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
972 inst_rules
55-dm.rules
60-persistent-storage-dm.rules
95-dm-notify.rules
974 inst_rules
10-dm.rules
13-dm-disk.rules
95-dm-notify.rules
976 if get_bool
"$LOOKS_LIKE_SUSE"; then
977 inst_rules
60-persistent-storage.rules
61-persistent-storage-compat.rules
99-systemd.rules
981 install_multipath
() {
982 instmods
"=md" multipath
983 image_install kpartx
/lib
/udev
/kpartx_id lsmod mpathpersist multipath multipathd partx
984 image_install
"${ROOTLIBDIR:?}"/system
/multipathd.
{service
,socket
}
985 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
986 inst_rules
56-dm-parts.rules
56-dm-mpath.rules
60-multipath.rules
68-del-part-nodes.rules
95-kpartx.rules
988 inst_rules
11-dm-mpath.rules
11-dm-parts.rules
62-multipath.rules
66-kpartx.rules
68-del-part-nodes.rules
990 mkdir
-p "${initdir:?}/etc/multipath"
993 while read -r file; do
994 # Install libraries required by the given library
996 # Install the library itself and create necessary symlinks
998 done < <(find /lib
*/multipath
-type f
)
1003 image_install
"${ROOTLIBDIR:?}"/system
/lvm2-lvmpolld.
{service
,socket
}
1004 image_install
"${ROOTLIBDIR:?}"/system
/{blk-availability
,lvm2-monitor
}.service
1005 image_install
-o "/lib/tmpfiles.d/lvm2.conf"
1006 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
1007 inst_rules
56-lvm.rules
69-lvm-metad.rules
1009 # Support the new udev autoactivation introduced in lvm 2.03.14
1010 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
1011 # Static autoactivation (via lvm2-activation-generator) was dropped
1013 # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
1014 if [[ -f /lib
/udev
/rules.d
/69-dm-lvm.rules
]]; then
1015 inst_rules
11-dm-lvm.rules
69-dm-lvm.rules
1017 image_install
"${ROOTLIBDIR:?}"/system-generators
/lvm2-activation-generator
1018 image_install
"${ROOTLIBDIR:?}"/system
/lvm2-pvscan@.service
1019 inst_rules
11-dm-lvm.rules
69-dm-lvm-metad.rules
1022 mkdir
-p "${initdir:?}/etc/lvm"
1027 # Not all utilities provided by btrfs-progs are listed here; extend the list
1029 image_install btrfs btrfstune mkfs.btrfs
1030 inst_rules
64-btrfs-dm.rules
1034 # Install both client and server side stuff by default
1038 # Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case
1039 # (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
1040 if [[ -z "$inst" ||
"$inst" =~
(client|initiator
) ]]; then
1041 image_install iscsi-iname iscsiadm iscsid iscsistart
1042 image_install
-o "${ROOTLIBDIR:?}"/system
/iscsi-
{init
,onboot
,shutdown
}.service
1043 image_install
"${ROOTLIBDIR:?}"/system
/iscsid.
{service
,socket
}
1044 image_install
"${ROOTLIBDIR:?}"/system
/iscsi.service
1045 mkdir
-p "${initdir:?}"/var
/lib
/iscsi
/{ifaces
,isns
,nodes
,send_targets
,slp
,static
}
1046 mkdir
-p "${initdir:?}/etc/iscsi"
1047 echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
1048 # Since open-iscsi 2.1.2 [0] the initiator name should be generated via
1049 # a one-time service instead of distro package's post-install scripts.
1050 # However, some distros still use this approach even after this patch,
1051 # so prefer the already existing initiatorname.iscsi file if it exists.
1053 # [0] https://github.com/open-iscsi/open-iscsi/commit/f37d5b653f9f251845db3f29b1a3dcb90ec89731
1054 if [[ ! -e /etc
/iscsi
/initiatorname.iscsi
]]; then
1055 image_install
"${ROOTLIBDIR:?}"/system
/iscsi-init.service
1056 if get_bool
"$IS_BUILT_WITH_ASAN"; then
1057 # The iscsi-init.service calls `sh` which might, in certain circumstances,
1058 # pull in instrumented systemd NSS modules causing `sh` to fail. Let's mitigate
1059 # this by pulling in an env file crafted by `create_asan_wrapper()` that
1060 # (among others) pre-loads ASan's DSO.
1061 mkdir
-p "${initdir:?}/etc/systemd/system/iscsi-init.service.d/"
1062 printf "[Service]\nEnvironmentFile=/usr/lib/systemd/systemd-asan-env" >"${initdir:?}/etc/systemd/system/iscsi-init.service.d/asan-env.conf"
1065 inst_simple
"/etc/iscsi/initiatorname.iscsi"
1069 # Install server-side stuff ("target" in iSCSI jargon) - TGT in this case
1070 # (tgt on Debian, scsi-target-utils on Fedora, etc.)
1071 if [[ -z "$inst" ||
"$inst" =~
(server|target
) ]]; then
1072 image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
1073 image_install
-o /etc
/sysconfig
/tgtd
1074 image_install
"${ROOTLIBDIR:?}"/system
/tgtd.service
1075 mkdir
-p "${initdir:?}/etc/tgt"
1076 touch "${initdir:?}"/etc
/tgt
/{tgtd
,targets
}.conf
1077 # Install perl modules required by tgt-admin
1079 # Forgive me father for I have sinned. The monstrosity below appends
1080 # a perl snippet to the `tgt-admin` perl script on the fly, which
1081 # dumps a list of files (perl modules) required by `tgt-admin` at
1082 # the runtime plus any DSOs loaded via DynaLoader. This list is then
1083 # passed to `inst_simple` which installs the necessary files into the image
1085 # shellcheck disable=SC2016
1086 while read -r file; do
1088 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 }')
1095 system
/mdadm-grow-continue@.service
1096 system
/mdadm-last-resort@.service
1097 system
/mdadm-last-resort@.timer
1098 system
/mdmon@.service
1099 system
/mdmonitor-oneshot.service
1100 system
/mdmonitor-oneshot.timer
1101 system
/mdmonitor.service
1102 system-shutdown
/mdadm.shutdown
1105 image_install mdadm mdmon
1106 inst_rules
01-md-raid-creating.rules
63-md-raid-arrays.rules
64-md-raid-assembly.rules
69-md-clustered-confirm-device.rules
1107 # Fedora/CentOS/RHEL ships this rule file
1108 [[ -f /lib
/udev
/rules.d
/65-md-incremental.rules
]] && inst_rules
65-md-incremental.rules
1110 for unit
in "${mdadm_units[@]}"; do
1111 image_install
"${ROOTLIBDIR:?}/$unit"
1115 install_compiled_systemd
() {
1116 dinfo
"Install compiled systemd"
1119 ninja_bin
="$(type -P ninja || type -P ninja-build)"
1120 if [[ -z "$ninja_bin" ]]; then
1121 dfatal
"ninja was not found"
1124 (set -x; DESTDIR
="$initdir" "$ninja_bin" -C "$BUILD_DIR" install)
1126 # If we are doing coverage runs, copy over the binary notes files, as lcov expects to
1127 # find them in the same directory as the runtime data counts
1128 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1129 mkdir
-p "${initdir}/${BUILD_DIR:?}/"
1130 rsync
-am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/"
1131 # Set effective & default ACLs for the build dir so unprivileged
1132 # processes can write gcda files with coverage stats
1133 setfacl
-R -m 'd:o:rwX' -m 'o:rwX' "${initdir}/${BUILD_DIR:?}/"
1137 install_debian_systemd
() {
1138 dinfo
"Install debian systemd"
1142 while read -r deb
; do
1143 files
="$(dpkg-query -L "$deb" 2>/dev/null)" ||
continue
1144 ddebug
"Install debian files from package $deb"
1145 for file in $files; do
1146 [ -e "$file" ] ||
continue
1147 [ -d "$file" ] && continue
1150 done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut
-d ':' -f 2)
1153 install_suse_systemd
() {
1156 dinfo
"Install SUSE systemd"
1162 systemd-experimental
1163 systemd-journal-remote
1164 # Since commit fb6f25d7b979134a, systemd-resolved, which is shipped by
1165 # systemd-network sub-package on openSUSE, has its own testsuite.
1171 for p
in "${pkgs[@]}"; do
1172 rpm
-q "$p" &>/dev
/null ||
continue
1174 ddebug
"Install files from package $p"
1176 [ -e "$f" ] ||
continue
1177 [ -d "$f" ] && continue
1179 done < <(rpm
-ql "$p")
1182 # Embed the files needed by the extended testsuite at runtime. Also include
1183 # the unit tests needed by TEST-02-UNITTESTS. This is mostly equivalent to
1184 # what `ninja install` does for the tests when '-Dinstall-tests=true'.
1186 # Why? openSUSE ships a package named 'systemd-testsuite' which contains
1187 # the minimal set of files that allows to run the testsuite on the host (as
1188 # long as it runs an equivalent version of systemd) getting rid of the
1189 # hassles of fetching, configuring, building the source code.
1190 dinfo
"Install the files needed by the tests at runtime"
1191 image_install
"${SOURCE_DIR}"/test-
*
1192 inst_recursive
"${SOURCE_DIR}/testdata"
1193 inst_recursive
"${SOURCE_DIR}/manual"
1195 # On openSUSE, this directory is not created at package install, at least
1197 mkdir
-p "$initdir/var/log/journal/remote"
1200 install_distro_systemd
() {
1201 dinfo
"Install distro systemd"
1203 if get_bool
"$LOOKS_LIKE_DEBIAN"; then
1204 install_debian_systemd
1205 elif get_bool
"$LOOKS_LIKE_SUSE"; then
1206 install_suse_systemd
1208 dfatal
"NO_BUILD not supported for this distro"
1214 dinfo
"Install systemd"
1215 if get_bool
"$NO_BUILD"; then
1216 install_distro_systemd
1218 install_compiled_systemd
1221 # remove unneeded documentation
1222 rm -fr "${initdir:?}"/usr
/share
/{man
,doc
}
1224 # enable debug logging in PID1
1225 echo LogLevel
=debug
>>"$initdir/etc/systemd/system.conf"
1226 if [[ -n "$TEST_SYSTEMD_LOG_LEVEL" ]]; then
1227 echo DefaultEnvironment
=SYSTEMD_LOG_LEVEL
="$TEST_SYSTEMD_LOG_LEVEL" >>"$initdir/etc/systemd/system.conf"
1229 # store coredumps in journal
1230 echo Storage
=journal
>>"$initdir/etc/systemd/coredump.conf"
1231 # Propagate SYSTEMD_UNIT_PATH to user systemd managers
1232 mkdir
"$initdir/etc/systemd/system/user@.service.d/"
1233 echo -e "[Service]\nPassEnvironment=SYSTEMD_UNIT_PATH\n" >"$initdir/etc/systemd/system/user@.service.d/override.conf"
1235 # When built with gcov, disable ProtectSystem= and ProtectHome= in the test
1236 # images, since it prevents gcov to write the coverage reports (*.gcda
1238 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1239 mkdir
-p "$initdir/etc/systemd/system/service.d/"
1240 echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/99-gcov-override.conf"
1241 # Similarly, set ReadWritePaths= to the $BUILD_DIR in the test image
1242 # to make the coverage work with units utilizing DynamicUser=yes. Do
1243 # this only for services from TEST-20, as setting this system-wide
1244 # has many undesirable side-effects
1245 mkdir
-p "$initdir/etc/systemd/system/test20-.service.d/"
1246 echo -e "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/test20-.service.d/99-gcov-rwpaths-override.conf"
1249 # If we're built with -Dportabled=false, tests with systemd-analyze
1250 # --profile will fail. Since we need just the profile (text) files, let's
1251 # copy them into the image if they don't exist there.
1252 local portable_dir
="${initdir:?}${ROOTLIBDIR:?}/portable"
1253 if [[ ! -d "$portable_dir/profile/strict" ]]; then
1254 dinfo
"Couldn't find portable profiles in the test image"
1255 dinfo
"Copying them directly from the source tree"
1256 mkdir
-p "$portable_dir"
1257 cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir"
1263 rpath
="$(objdump -p "${1:?}" 2>/dev/null | awk "/R
(UN
)?PATH
/ { print
\"$initdir\" \
$2 }" | paste -sd :)"
1265 if [ -z "$rpath" ] ; then
1272 install_missing_libraries
() {
1273 dinfo
"Install missing libraries"
1274 # install possible missing libraries
1275 for i
in "${initdir:?}"{,/usr
}/{sbin
,bin
}/* "$initdir"{,/usr
}/lib
/systemd
/{,tests
/{,manual
/,unsafe
/}}*; do
1276 LD_LIBRARY_PATH
="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs
"$i"
1279 # Install libgcc_s.so if available, since it's dlopen()ed by libpthread
1280 # and might cause unexpected failures during pthread_exit()/pthread_cancel()
1282 # See: https://github.com/systemd/systemd/pull/23858
1283 while read -r libgcc_s
; do
1284 [[ -e "$libgcc_s" ]] && inst_library
"$libgcc_s"
1285 done < <(ldconfig
-p |
awk '/\/libgcc_s.so.1$/ { print $4 }')
1288 # A number of dependencies is now optional via dlopen, so the install
1289 # script will not pick them up, since it looks at linkage.
1290 for lib
in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1
; do
1291 ddebug
"Searching for $lib via pkg-config"
1292 if pkg-config
--exists "$lib"; then
1293 path
="$(pkg-config --variable=libdir "$lib")"
1294 if [ -z "${path}" ]; then
1295 ddebug
"$lib.pc does not contain a libdir variable, skipping"
1299 if ! [[ ${lib} =~ ^lib
]]; then
1302 # p11-kit-1's .so doesn't have the API level in the name
1303 if [[ ${lib} =~ p11-kit-1$
]]; then
1306 # Some pkg-config files are broken and give out the wrong paths
1307 # (eg: libcryptsetup), so just ignore them
1308 inst_libs
"${path}/${lib}.so" || true
1309 inst_library
"${path}/${lib}.so" || true
1311 if [[ "$lib" == "libxkbcommon" ]]; then
1312 install_x11_keymaps full
1315 ddebug
"$lib.pc not found, skipping"
1320 # Install extra openssl 3 stuff
1321 path
="$(pkg-config --variable=libdir libcrypto)"
1322 inst_simple
"${path}/ossl-modules/legacy.so" || true
1323 inst_simple
"${path}/ossl-modules/fips.so" || true
1324 inst_simple
"${path}/engines-3/afalg.so" || true
1325 inst_simple
"${path}/engines-3/capi.so" || true
1326 inst_simple
"${path}/engines-3/loader_attic.so" || true
1327 inst_simple
"${path}/engines-3/padlock.so" || true
1329 # Binaries from mtools depend on the gconv modules to translate between codepages. Because there's no
1330 # pkg-config file for these, we copy every gconv/ directory we can find in /usr/lib and /usr/lib64.
1331 # shellcheck disable=SC2046
1332 inst_recursive $
(find /usr
/lib
* -name gconv
2>/dev
/null
)
1336 if [ -n "${LOOPDEV:=}" ]; then
1337 ddebug
"losetup -d $LOOPDEV"
1338 losetup
-d "${LOOPDEV}"
1343 add_at_exit_handler cleanup_loopdev
1345 create_empty_image
() {
1346 if [ -z "${IMAGE_NAME:=}" ]; then
1347 echo "create_empty_image: \$IMAGE_NAME not set"
1351 # Partition sizes are in MiBs
1352 local root_size
=1000
1354 if ! get_bool
"$NO_BUILD"; then
1355 if meson configure
"${BUILD_DIR:?}" |
grep 'static-lib\|standalone-binaries' |
awk '{ print $2 }' |
grep -q 'true'; then
1356 root_size
=$
((root_size
+=200))
1358 if meson configure
"${BUILD_DIR:?}" |
grep 'link-.*-shared' |
awk '{ print $2 }' |
grep -q 'false'; then
1359 root_size
=$
((root_size
+=200))
1361 if get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1362 root_size
=$
((root_size
+=250))
1365 if ! get_bool
"$STRIP_BINARIES"; then
1366 root_size
=$
((4 * root_size
))
1367 data_size
=$
((2 * data_size
))
1369 if [ "$IMAGE_NAME" = "repart" ]; then
1370 root_size
=$
((root_size
+=1000))
1373 echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)"
1374 rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
1376 # Create the blank file to use as a root filesystem
1377 truncate
-s "${root_size}M" "$IMAGE_PUBLIC"
1379 LOOPDEV
=$
(losetup
--show -P -f "$IMAGE_PUBLIC")
1380 [ -b "$LOOPDEV" ] ||
return 1
1381 # Create two partitions - a root one and a data one (utilized by some tests)
1382 sfdisk
"$LOOPDEV" <<EOF
1384 type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=$((root_size - data_size))M bootable
1385 type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
1390 local label
=(-L systemd_boot
)
1391 # mkfs.reiserfs doesn't know -L. so, use --label instead
1392 [[ "$FSTYPE" == "reiserfs" ]] && label
=(--label systemd_boot
)
1393 if ! mkfs
-t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then
1394 dfatal
"Failed to mkfs -t ${FSTYPE}"
1400 if [ -z "${LOOPDEV:=}" ]; then
1401 [ -e "${IMAGE_PRIVATE:?}" ] && image
="$IMAGE_PRIVATE" || image
="${IMAGE_PUBLIC:?}"
1402 LOOPDEV
="$(losetup --show -P -f "$image")"
1403 [ -b "$LOOPDEV" ] ||
return 1
1408 if ! mountpoint
-q "${initdir:?}"; then
1410 mount
"${LOOPDEV}p1" "$initdir"
1411 TEST_SETUP_CLEANUP_ROOTDIR
=1
1416 # only umount if create_empty_image_rootdir() was called to mount it
1417 if get_bool
"$TEST_SETUP_CLEANUP_ROOTDIR"; then
1418 _umount_dir
"${initdir:?}"
1423 # unmount the loopback device from all places. Otherwise we risk file
1424 # system corruption.
1425 for device
in $
(losetup
-l |
awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do
1426 ddebug
"Unmounting all uses of $device"
1427 mount |
awk '/^'"${device}"'p/{print $1}' |
xargs --no-run-if-empty umount
-v
1431 create_empty_image_rootdir
() {
1436 check_asan_reports
() {
1440 if get_bool
"$IS_BUILT_WITH_ASAN"; then
1442 if [[ -e "$root/systemd.asan.log.1" ]]; then
1443 cat "$root/systemd.asan.log.1"
1447 journald_report
="$(find "$root" -name "systemd-journald.
*san.log
*" -exec cat {} \;)"
1448 if [[ -n "$journald_report" ]]; then
1449 printf "%s\n" "$journald_report"
1450 cat "$root/systemd-journald.out" ||
:
1455 "$JOURNALCTL" -D "$root/var
/log
/journal
" | perl -alne '
1457 %services_to_ignore = (
1458 "dbus-daemon
" => undef,
1459 "dbus-broker-launch
" => undef,
1462 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
1464 if [[ -n "$pids" ]]; then
1466 for pid
in $pids; do
1467 "$JOURNALCTL" -D "$root/var/log/journal" _PID
="$pid" --no-pager
1475 check_coverage_reports
() {
1478 if get_bool
"$NO_BUILD"; then
1481 if ! get_bool
"$IS_BUILT_WITH_COVERAGE"; then
1485 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1486 dest
="${ARTIFACT_DIRECTORY}/${testname:?}.coverage-info"
1488 dest
="${TESTDIR:?}/coverage-info"
1491 # Create a coverage report that will later be uploaded. Remove info about
1492 # system libraries/headers, as we don't really care about them.
1493 if [[ -f "$dest" ]]; then
1494 # If the destination report file already exists, don't overwrite it, but
1495 # dump the new report in a temporary file and then merge it with the already
1496 # present one - this usually happens when running both "parts" of a test
1497 # in one run (the qemu and the nspawn part).
1498 lcov
--directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new"
1499 lcov
--remove "${dest}.new" -o "${dest}.new" '/usr/include/*' '/usr/lib/*'
1500 lcov
--add-tracefile "${dest}" --add-tracefile "${dest}.new" -o "${dest}"
1503 lcov
--directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}"
1504 lcov
--remove "${dest}" -o "${dest}" '/usr/include/*' '/usr/lib/*'
1507 # If the test logs contain lines like:
1509 # ...systemd-resolved[735885]: profiling:/systemd-meson-build/src/shared/libsystemd-shared-250.a.p/base-filesystem.c.gcda:Cannot open
1511 # it means we're possibly missing some coverage since gcov can't write the stats,
1512 # usually due to the sandbox being too restrictive (e.g. ProtectSystem=yes,
1513 # ProtectHome=yes) or the $BUILD_DIR being inaccessible to non-root users - see
1514 # `setfacl` stuff in install_compiled_systemd().
1515 if ! get_bool
"${IGNORE_MISSING_COVERAGE:=}" && \
1516 "${JOURNALCTL:?}" -q --no-pager -D "${root:?}/var/log/journal" --grep "profiling:.+?gcda:[Cc]annot open"; then
1517 derror
"Detected possibly missing coverage, check the journal"
1525 # Default to always saving journal
1528 if [ "${TEST_SAVE_JOURNAL}" = "no" ]; then
1530 elif [ "${TEST_SAVE_JOURNAL}" = "fail" ] && [ "$2" = "0" ]; then
1534 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
1535 dest
="${ARTIFACT_DIRECTORY}/${testname:?}.journal"
1537 dest
="${TESTDIR:?}/system.journal"
1540 for j
in "${1:?}"/*; do
1541 if get_bool
"$save"; then
1542 if [ "$SYSTEMD_JOURNAL_REMOTE" = "" ]; then
1545 "$SYSTEMD_JOURNAL_REMOTE" -o "$dest" --getter="$JOURNALCTL -o export -D $j"
1549 if [ -n "${TEST_SHOW_JOURNAL}" ]; then
1551 "$JOURNALCTL" --no-pager -o short-monotonic
--no-hostname --priority="${TEST_SHOW_JOURNAL}" -D "$j"
1557 if ! get_bool
"$save"; then
1561 if [ -n "${SUDO_USER}" ]; then
1562 setfacl
-m "user:${SUDO_USER:?}:r-X" "$dest"*
1565 # we want to print this sometime later, so save this in a variable
1566 JOURNAL_LIST
="$(ls -l "$dest"*)"
1569 check_result_common
() {
1570 local workspace
="${1:?}"
1573 if [ -s "$workspace/failed" ]; then
1574 # Non-empty …/failed has highest priority
1575 cp -a "$workspace/failed" "${TESTDIR:?}/"
1576 if [ -n "${SUDO_USER}" ]; then
1577 setfacl
-m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed
1580 elif get_bool
"$TIMED_OUT"; then
1581 echo "(timeout)" >"${TESTDIR:?}/failed"
1583 elif [ -e "$workspace/testok" ]; then
1584 # …/testok always counts (but with lower priority than …/failed)
1586 elif [ -e "$workspace/skipped" ]; then
1587 # …/skipped always counts (a message is expected)
1588 echo "${TESTNAME:?} was skipped:"
1589 cat "$workspace/skipped"
1592 echo "(failed; see logs)" >"${TESTDIR:?}/failed"
1596 check_asan_reports
"$workspace" || ret
=4
1598 check_coverage_reports
"$workspace" || ret
=5
1600 save_journal
"$workspace/var/log/journal" $ret
1602 if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out" ]; then
1603 cp "$workspace/strace.out" "${ARTIFACT_DIRECTORY}/"
1606 if [ ${ret:?} != 0 ] && [ -f "$TESTDIR/failed" ]; then
1607 echo -n "${TESTNAME:?}: "
1608 cat "$TESTDIR/failed"
1610 echo "${JOURNAL_LIST:-"No journals were saved"}"
1615 check_result_nspawn
() {
1616 local workspace
="${1:?}"
1619 # Run a test-specific checks if defined by check_result_nspawn_hook()
1620 if declare -F check_result_nspawn_hook
>/dev
/null
; then
1621 if ! check_result_nspawn_hook
"${workspace}"; then
1622 derror
"check_result_nspawn_hook() returned with EC > 0"
1627 check_result_common
"${workspace}"
1630 _umount_dir
"${initdir:?}"
1635 # can be overridden in specific test
1636 check_result_qemu
() {
1640 # Run a test-specific checks if defined by check_result_qemu_hook()
1641 if declare -F check_result_qemu_hook
>/dev
/null
; then
1642 if ! check_result_qemu_hook
"${initdir:?}"; then
1643 derror
"check_result_qemu_hook() returned with EC > 0"
1648 check_result_common
"${initdir:?}"
1651 _umount_dir
"${initdir:?}"
1656 check_result_nspawn_unittests
() {
1657 local workspace
="${1:?}"
1660 [[ -e "$workspace/testok" ]] && ret
=0
1662 if [[ -s "$workspace/failed" ]]; then
1664 echo "=== Failed test log ==="
1665 cat "$workspace/failed"
1667 if [[ -s "$workspace/skipped" ]]; then
1668 echo "=== Skipped test log =="
1669 cat "$workspace/skipped"
1670 # We might have only skipped tests - that should not fail the job
1673 if [[ -s "$workspace/testok" ]]; then
1674 echo "=== Passed tests ==="
1675 cat "$workspace/testok"
1679 get_bool
"${TIMED_OUT:=}" && ret
=1
1680 check_coverage_reports
"$workspace" || ret
=5
1682 save_journal
"$workspace/var/log/journal" $ret
1684 _umount_dir
"${initdir:?}"
1689 check_result_qemu_unittests
() {
1693 [[ -e "${initdir:?}/testok" ]] && ret
=0
1695 if [[ -s "$initdir/failed" ]]; then
1697 echo "=== Failed test log ==="
1698 cat "$initdir/failed"
1700 if [[ -s "$initdir/skipped" ]]; then
1701 echo "=== Skipped test log =="
1702 cat "$initdir/skipped"
1703 # We might have only skipped tests - that should not fail the job
1706 if [[ -s "$initdir/testok" ]]; then
1707 echo "=== Passed tests ==="
1708 cat "$initdir/testok"
1712 get_bool
"${TIMED_OUT:=}" && ret
=1
1713 check_coverage_reports
"$initdir" || ret
=5
1715 save_journal
"$initdir/var/log/journal" $ret
1717 _umount_dir
"$initdir"
1723 dinfo
"Strip binaries"
1724 if ! get_bool
"$STRIP_BINARIES"; then
1725 dinfo
"STRIP_BINARIES == no, keeping binaries unstripped"
1728 while read -r bin
; do
1729 strip
--strip-unneeded "$bin" |
& grep -vi 'file format not recognized' | ddebug ||
:
1730 done < <(find "${initdir:?}" -executable -not -path '*/lib/modules/*.ko' -type f
)
1734 dinfo
"Create rc.local"
1735 mkdir
-p "${initdir:?}/etc/rc.d"
1736 cat >"$initdir/etc/rc.d/rc.local" <<EOF
1740 chmod 0755 "$initdir/etc/rc.d/rc.local"
1744 ddebug
"Install executables from the service files"
1746 local pkg_config_path
="${BUILD_DIR:?}/src/core/"
1747 local systemunitdir userunitdir exe
1748 systemunitdir
="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemdsystemunitdir systemd)"
1749 userunitdir
="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemduserunitdir systemd)"
1750 while read -r exe
; do
1751 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
1752 # also, plymouth is pulled in by rescue.service, but even there the exit code
1753 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
1754 dinfo
"Attempting to install $exe (based on unit file reference)"
1755 inst
"$exe" ||
[ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth" ]
1756 done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service |
sort -u)
1759 generate_module_dependencies
() {
1760 dinfo
"Generate modules dependencies"
1761 if [[ -d "${initdir:?}/lib/modules/${KERNEL_VER:?}" ]] && \
1762 ! depmod
-a -b "$initdir" "$KERNEL_VER"; then
1763 dfatal
"\"depmod -a $KERNEL_VER\" failed."
1768 install_depmod_files
() {
1769 dinfo
"Install depmod files"
1770 inst
"/lib/modules/${KERNEL_VER:?}/modules.order"
1771 inst
"/lib/modules/$KERNEL_VER/modules.builtin"
1774 install_plymouth
() {
1775 dinfo
"Install plymouth"
1776 # install plymouth, if found... else remove plymouth service files
1777 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
1778 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
1779 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
1780 # image_install plymouth plymouthd
1782 rm -f "${initdir:?}"/{usr
/lib
,lib
,etc
}/systemd
/system
/plymouth
* "$initdir"/{usr
/lib
,lib
,etc
}/systemd
/system
/*/plymouth
*
1787 # If haveged is installed, it's probably included in initrd and needs to be
1788 # installed in the image too.
1789 if [ -x /usr
/sbin
/haveged
]; then
1790 dinfo
"Install haveged files"
1791 inst
/usr
/sbin
/haveged
1792 for u
in /usr
/lib
/systemd
/system
/haveged
*; do
1798 install_ld_so_conf
() {
1799 dinfo
"Install /etc/ld.so.conf*"
1800 cp -a /etc
/ld.so.conf
* "${initdir:?}/etc"
1801 ldconfig
-r "$initdir"
1804 install_testuser
() {
1805 dinfo
"Set up a test user"
1806 # create unprivileged user for user manager tests
1807 mkdir
-p "${initdir:?}/etc/sysusers.d"
1808 cat >"$initdir/etc/sysusers.d/testuser.conf" <<EOF
1809 u testuser 4711 "Test User" /home/testuser
1812 mkdir
-p "$initdir/home/testuser"
1813 chmod 0700 "$initdir/home/testuser"
1814 chown
4711:4711 "$initdir/home/testuser"
1817 install_config_files
() {
1818 dinfo
"Install config files"
1819 inst
/etc
/sysconfig
/init ||
:
1822 inst_any
/etc
/login.defs
/usr
/etc
/login.defs
1825 inst_any
/etc
/nsswitch.conf
/usr
/etc
/nsswitch.conf
1826 inst
/etc
/pam.conf ||
:
1827 inst_any
/etc
/os-release
/usr
/lib
/os-release
1829 # we want an empty environment
1830 : >"${initdir:?}/etc/environment"
1831 : >"$initdir/etc/machine-id"
1832 : >"$initdir/etc/resolv.conf"
1835 echo 'H' >"$initdir/etc/hostname"
1837 # let's set up just one image with the traditional verbose output
1838 if [ "${IMAGE_NAME:?}" != "basic" ]; then
1839 mkdir
-p "$initdir/etc/systemd/system.conf.d"
1840 echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc/systemd/system.conf.d/status.conf"
1844 install_basic_tools
() {
1845 dinfo
"Install basic tools"
1846 image_install
"${BASICTOOLS[@]}"
1847 image_install
-o sushell
1848 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
1849 image_install
-o ldconfig.real
1852 install_debug_tools
() {
1853 dinfo
"Install debug tools"
1854 image_install
-o "${DEBUGTOOLS[@]}"
1856 if get_bool
"$INTERACTIVE_DEBUG"; then
1857 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
1858 local getty_override
="${initdir:?}/etc/systemd/system/serial-getty@.service.d"
1859 mkdir
-p "$getty_override"
1860 echo -e "[Service]\nEnvironment=TERM=linux" >"$getty_override/default-TERM.conf"
1861 echo 'export TERM=linux' >>"$initdir/etc/profile"
1863 if command -v resize
>/dev
/null
; then
1864 image_install resize
1865 echo "resize" >>"$initdir/etc/profile"
1868 # Sometimes we might end up with plymouthd still running (especially
1869 # with the initrd -> asan_wrapper -> systemd transition), which will eat
1870 # our inputs and make debugging via tty impossible. Let's fix this by
1871 # killing plymouthd explicitly for the interactive sessions.
1872 # Note: we can't use pkill/pidof/etc. here due to a bug in libasan, see:
1873 # - https://github.com/llvm/llvm-project/issues/49223
1874 # - https://bugzilla.redhat.com/show_bug.cgi?id=2098125
1875 local plymouth_unit
="${initdir:?}/etc/systemd/system/kill-plymouth.service"
1876 cat >"$plymouth_unit" <<EOF
1878 After=multi-user.target
1881 ExecStart=sh -c 'killall --verbose plymouthd || :'
1884 WantedBy=multi-user.target
1886 "${SYSTEMCTL:?}" enable --root "${initdir:?}" kill-plymouth.service
1891 dinfo
"Install libnss"
1892 # install libnss_files for login
1894 mapfile
-t NSS_LIBS
< <(LD_DEBUG
=files getent passwd
2>&1 >/dev
/null |
sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
1895 if [[ ${#NSS_LIBS[@]} -gt 0 ]]; then
1896 image_install
"${NSS_LIBS[@]}"
1901 dinfo
"Install dbus"
1902 inst
"${ROOTLIBDIR:?}/system/dbus.socket"
1904 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1905 if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then
1906 inst
"$ROOTLIBDIR/system/dbus-broker.service"
1907 inst_symlink
/etc
/systemd
/system
/dbus.service
1908 inst
/usr
/bin
/dbus-broker
1909 inst
/usr
/bin
/dbus-broker-launch
1910 elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then
1911 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1912 inst
"$ROOTLIBDIR/system/dbus-daemon.service"
1914 inst_symlink
/etc
/systemd
/system
/dbus.service
1916 inst
"$ROOTLIBDIR/system/dbus.service"
1919 while read -r file; do
1921 done < <(find /etc
/dbus-1
/usr
/share
/dbus-1
-xtype f
2>/dev
/null
)
1923 # setup policy for Type=dbus test
1924 mkdir
-p "${initdir:?}/etc/dbus-1/system.d"
1925 cat >"$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf" <<EOF
1926 <?xml version="1.0"?>
1927 <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
1928 "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
1930 <policy user="root">
1931 <allow own="systemd.test.ExecStopPost"/>
1936 # If we run without KVM, bump the service start timeout
1937 if ! get_bool
"$QEMU_KVM"; then
1938 cat >"$initdir/etc/dbus-1/system.d/service.timeout.conf" <<EOF
1939 <?xml version="1.0"?>
1940 <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
1941 "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
1943 <limit name="service_start_timeout">60000</limit>
1946 # Bump the client-side timeout in sd-bus as well
1947 mkdir
-p "$initdir/etc/systemd/system.conf.d"
1948 echo -e '[Manager]\nDefaultEnvironment=SYSTEMD_BUS_TIMEOUT=60' >"$initdir/etc/systemd/system.conf.d/bus-timeout.conf"
1952 install_user_dbus
() {
1953 dinfo
"Install user dbus"
1955 if ! userunitdir
="$(pkg-config --variable=systemduserunitdir systemd)"; then
1956 dwarn
"WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user"
1957 userunitdir
=/usr
/lib
/systemd
/user
1960 inst
"$userunitdir/dbus.socket"
1961 inst_symlink
"$userunitdir/sockets.target.wants/dbus.socket" || inst_symlink
/etc
/systemd
/user
/sockets.target.wants
/dbus.socket
1963 # Append the After= dependency on dbus in case it isn't already set up
1964 mkdir
-p "${initdir:?}/etc/systemd/system/user@.service.d/"
1965 cat >"$initdir/etc/systemd/system/user@.service.d/dbus.conf" <<EOF
1970 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1971 if [ -f "$userunitdir/dbus-broker.service" ]; then
1972 inst
"$userunitdir/dbus-broker.service"
1973 inst_symlink
/etc
/systemd
/user
/dbus.service
1974 elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then
1975 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1976 inst
"$userunitdir/dbus-daemon.service"
1978 inst_symlink
/etc
/systemd
/user
/dbus.service
1980 inst
"$userunitdir/dbus.service"
1988 if get_bool
"$LOOKS_LIKE_DEBIAN" && type -p dpkg-architecture
&>/dev
/null
; then
1989 paths
+=("/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security")
1991 paths
+=(/lib
*/security
)
1994 for d
in /etc
/pam.d
/{usr
/,}etc
/security
/usr
/{etc
,lib
}/pam.d
; do
1995 [ -d "$d" ] && paths
+=("$d")
1998 while read -r file; do
2000 done < <(find "${paths[@]}" -xtype f
)
2002 # pam_unix depends on unix_chkpwd.
2003 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
2004 image_install
-o unix_chkpwd
2006 # set empty root password for easy debugging
2007 sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
2009 # And make sure pam_unix will accept it by making sure that
2010 # the PAM module has the nullok option.
2011 for d
in /etc
/pam.d
/usr
/{etc
,lib
}/pam.d
; do
2012 [ -d "$initdir/$d" ] ||
continue
2013 sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
2018 # install only C.UTF-8 and English locales
2019 dinfo
"Install locales"
2021 if command -v meson
>/dev
/null \
2022 && (meson configure
"${BUILD_DIR:?}" |
grep 'localegen-path */') \
2023 || get_bool
"$LOOKS_LIKE_DEBIAN"; then
2024 # locale-gen support
2025 image_install
-o locale-gen localedef
2026 inst
/etc
/locale.gen ||
:
2027 inst
/usr
/share
/i18n
/SUPPORTED ||
:
2028 inst_recursive
/usr
/share
/i18n
/charmaps
2029 inst_recursive
/usr
/share
/i18n
/locales
2030 inst_recursive
/usr
/share
/locale
/en
*
2031 inst_recursive
/usr
/share
/locale
/de
*
2032 image_install
/usr
/share
/locale
/locale.
alias
2033 # locale-gen might either generate each locale separately or merge them
2034 # into a single archive
2035 if ! (inst_recursive
/usr
/lib
/locale
/C.
*8 /usr
/lib
/locale
/en_
*8 ||
2036 image_install
/usr
/lib
/locale
/locale-archive
); then
2037 dfatal
"Failed to install required locales"
2041 inst_recursive
/usr
/lib
/locale
/C.
*8 /usr
/lib
/locale
/en_
*8
2045 # shellcheck disable=SC2120
2053 dinfo
"Install console keymaps"
2055 if command -v meson
>/dev
/null \
2056 && [[ "$(meson configure "${BUILD_DIR:?}" | grep 'split-usr' | awk '{ print $2 }')" == "true" ]] \
2057 ||
[[ ! -L /lib
]]; then
2063 if (( $# == 0 )); then
2064 for p
in "${prefix[@]}"; do
2065 # The first three paths may be deprecated.
2066 # It seems now the last three paths are used by many distributions.
2068 "$p"/kbd
/keymaps
/include
/* \
2069 "$p"/kbd
/keymaps
/i386
/include
/* \
2070 "$p"/kbd
/keymaps
/i386
/qwerty
/us.
* \
2071 "$p"/kbd
/keymaps
/legacy
/include
/* \
2072 "$p"/kbd
/keymaps
/legacy
/i386
/qwerty
/us.
* \
2073 "$p"/kbd
/keymaps
/xkb
/us
*; do
2074 [[ -f "$i" ]] ||
continue
2079 # When it takes any argument, then install more keymaps.
2080 for p
in "${prefix[@]}"; do
2082 "$p"/kbd
/keymaps
/include
/* \
2083 "$p"/kbd
/keymaps
/i386
/*/* \
2084 "$p"/kbd
/keymaps
/legacy
/i386
/*/* \
2085 "$p"/kbd
/keymaps
/xkb
/*; do
2086 [[ -f "$i" ]] ||
continue
2093 install_x11_keymaps
() {
2094 dinfo
"Install x11 keymaps"
2096 if (( $# == 0 )); then
2097 # Install only keymap list.
2098 inst
/usr
/share
/X11
/xkb
/rules
/base.lst
2100 # When it takes any argument, then install all keymaps.
2101 inst_recursive
/usr
/share
/X11
/xkb
2105 install_zoneinfo
() {
2106 dinfo
"Install time zones"
2107 inst_any
/usr
/share
/zoneinfo
/Asia
/Seoul
2108 inst_any
/usr
/share
/zoneinfo
/Asia
/Vladivostok
2109 inst_any
/usr
/share
/zoneinfo
/Australia
/Sydney
2110 inst_any
/usr
/share
/zoneinfo
/Europe
/Berlin
2111 inst_any
/usr
/share
/zoneinfo
/Europe
/Dublin
2112 inst_any
/usr
/share
/zoneinfo
/Europe
/Kiev
2113 inst_any
/usr
/share
/zoneinfo
/Pacific
/Auckland
2114 inst_any
/usr
/share
/zoneinfo
/Pacific
/Honolulu
2115 inst_any
/usr
/share
/zoneinfo
/CET
2116 inst_any
/usr
/share
/zoneinfo
/EET
2117 inst_any
/usr
/share
/zoneinfo
/UTC
2121 dinfo
"Install system fonts"
2123 /usr
/lib
/kbd
/consolefonts
/eurlatgr
* \
2124 /usr
/lib
/kbd
/consolefonts
/latarcyrheb-sun16
*; do
2125 [[ -f "$i" ]] ||
continue
2130 install_terminfo
() {
2131 dinfo
"Install terminfo files"
2133 for terminfodir
in /lib
/terminfo
/etc
/terminfo
/usr
/share
/terminfo
; do
2134 [ -f "${terminfodir}/l/linux" ] && break
2136 image_install
-o "${terminfodir}/l/linux"
2139 has_user_dbus_socket
() {
2140 if [ -f /usr
/lib
/systemd
/user
/dbus.socket
] ||
[ -f /etc
/systemd
/user
/dbus.socket
]; then
2143 echo "Per-user instances are not supported. Skipping..."
2148 setup_nspawn_root_hook
() { :;}
2150 setup_nspawn_root
() {
2151 if [ -z "${initdir}" ]; then
2152 dfatal
"\$initdir not defined"
2156 rm -rf "${TESTDIR:?}/unprivileged-nspawn-root"
2158 if get_bool
"$RUN_IN_UNPRIVILEGED_CONTAINER"; then
2159 ddebug
"cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
2160 cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
2163 setup_nspawn_root_hook
2166 setup_basic_dirs
() {
2167 mkdir
-p "${initdir:?}/run"
2168 mkdir
-p "$initdir/etc/systemd/system"
2169 mkdir
-p "$initdir/var/log/journal"
2172 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
2173 if [ -L "/$d" ]; then
2180 ln -sfn /run
"$initdir/var/run"
2181 ln -sfn /run
/lock
"$initdir/var/lock"
2184 mask_supporting_services
() {
2185 # mask some services that we do not want to run in these tests
2186 ln -fsv /dev
/null
"${initdir:?}/etc/systemd/system/systemd-hwdb-update.service"
2187 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-journal-catalog-update.service"
2188 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-networkd.service"
2189 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-networkd.socket"
2190 ln -fsv /dev
/null
"$initdir/etc/systemd/system/systemd-resolved.service"
2195 local so_regex
='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2198 while read -r line
; do
2199 [[ "$line" = 'not a dynamic executable' ]] && break
2200 # Ignore errors about our own stuff missing. This is most likely caused
2201 # by ldd attempting to use the unprefixed RPATH.
2202 [[ "$line" =~ libsystemd.
*\ not\ found
]] && continue
2204 if [[ "$line" =~
$so_regex ]]; then
2205 file="${BASH_REMATCH[1]}"
2206 [[ -e "${initdir:?}/$file" ]] && continue
2207 inst_library
"$file"
2211 if [[ "$line" =~ not\ found
]]; then
2212 dfatal
"Missing a shared library required by $bin."
2213 dfatal
"Run \"ldd $bin\" to find out what it is."
2215 dfatal
"Cannot create a test image."
2218 done < <(LC_ALL
=C ldd
"$bin" 2>/dev
/null
)
2222 # make sure we don't get a stale LOOPDEV value from old times
2223 local _LOOPDEV
="${LOOPDEV:=}"
2224 # We don't want shellcheck to follow & check the $STATEFILE
2225 # shellcheck source=/dev/null
2226 [[ -e "$STATEFILE" ]] && .
"$STATEFILE"
2228 if [[ ! -d "$TESTDIR" ]]; then
2229 if [[ -z "$TESTDIR" ]]; then
2230 TESTDIR
="$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)"
2235 cat >"$STATEFILE" <<EOF
2241 IMAGE_PRIVATE
="${TESTDIR}/${IMAGE_NAME:?}.img"
2242 IMAGE_PUBLIC
="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img"
2246 initdir
="${TESTDIR:?}/root"
2251 get_cgroup_hierarchy
() {
2252 case "$(stat -c '%T' -f /sys/fs/cgroup)" in
2257 if [[ -d /sys
/fs
/cgroup
/unified
&& "$(stat -c '%T' -f /sys/fs/cgroup/unified)" == cgroup2fs
]]; then
2264 dfatal
"Failed to determine host's cgroup hierarchy"
2269 ## @brief Converts numeric logging level to the first letter of level name.
2271 # @param lvl Numeric logging level in range from 1 to 6.
2272 # @retval 1 if @a lvl is out of range.
2273 # @retval 0 if @a lvl is correct.
2274 # @result Echoes first letter of level name.
2287 ## @brief Internal helper function for _do_dlog()
2289 # @param lvl Numeric logging level.
2290 # @param msg Message.
2291 # @retval 0 It's always returned, even if logging failed.
2293 # @note This function is not supposed to be called manually. Please use
2294 # dtrace(), ddebug(), or others instead which wrap this one.
2296 # This function calls _do_dlog() either with parameter msg, or if
2297 # none is given, it will read standard input and will use every line as
2301 # dwarn "This is a warning"
2302 # echo "This is a warning" | dwarn
2303 LOG_LEVEL
="${LOG_LEVEL:-4}"
2308 [ -z "$LOG_LEVEL" ] && return 0
2310 [ "$lvl" -le "$LOG_LEVEL" ] ||
return 0
2311 lvlc
="$(_lvl2char "$lvl")" ||
return 0
2313 if [ $# -ge 1 ]; then
2316 while read -r line
; do
2317 echo "$lvlc: " "$line"
2322 ## @brief Logs message at TRACE level (6)
2324 # @param msg Message.
2325 # @retval 0 It's always returned, even if logging failed.
2329 if get_bool
"${debug:=}"; then
2334 ## @brief Logs message at DEBUG level (5)
2336 # @param msg Message.
2337 # @retval 0 It's always returned, even if logging failed.
2342 ## @brief Logs message at INFO level (4)
2344 # @param msg Message.
2345 # @retval 0 It's always returned, even if logging failed.
2349 if get_bool
"${debug:=}"; then
2354 ## @brief Logs message at WARN level (3)
2356 # @param msg Message.
2357 # @retval 0 It's always returned, even if logging failed.
2361 if get_bool
"${debug:=}"; then
2366 ## @brief Logs message at ERROR level (2)
2368 # @param msg Message.
2369 # @retval 0 It's always returned, even if logging failed.
2374 ## @brief Logs message at FATAL level (1)
2376 # @param msg Message.
2377 # @retval 0 It's always returned, even if logging failed.
2381 if get_bool
"${debug:=}"; then
2387 # Generic substring function. If $2 is in $1, return 0.
2388 strstr
() { [ "${1#*"$2"*}" != "$1" ]; }
2390 # normalize_path <path>
2391 # Prints the normalized path, where it removes any duplicated
2392 # and trailing slashes.
2394 # $ normalize_path ///test/test//
2398 set -- "${1//+(\/)//}"
2403 # convert_abs_rel <from> <to>
2404 # Prints the relative path, when creating a symlink to <to> from <from>.
2406 # $ convert_abs_rel /usr/bin/test /bin/test-2
2408 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
2410 local __current __absolute __abssize __cursize __newpath
2411 local -i __i __level
2413 set -- "$(normalize_path "${1:?}")" "$(normalize_path "${2:?}")"
2415 # corner case #1 - self looping link
2416 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
2418 # corner case #2 - own dir link
2419 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
2421 IFS
="/" read -ra __current
<<< "$1"
2422 IFS
="/" read -ra __absolute
<<< "$2"
2424 __abssize
=${#__absolute[@]}
2425 __cursize
=${#__current[@]}
2427 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]; do
2429 if (( __level
> __abssize || __level
> __cursize
))
2435 for ((__i
= __level
; __i
< __cursize-1
; __i
++)); do
2436 if ((__i
> __level
))
2438 __newpath
=$__newpath"/"
2440 __newpath
=$__newpath".."
2443 for ((__i
= __level
; __i
< __abssize
; __i
++)); do
2444 if [[ -n $__newpath ]]
2446 __newpath
=$__newpath"/"
2448 __newpath
=$__newpath${__absolute[__i]}
2455 # Install a directory, keeping symlinks as on the original system.
2456 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
2457 # will create ${initdir}/lib64, ${initdir}/lib64/file,
2458 # and a symlink ${initdir}/lib -> lib64.
2461 local part
="${dir%/*}"
2464 [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there
2466 while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do
2471 # iterate over parent directories
2472 for file in $dir; do
2473 [[ -e "${initdir}/$file" ]] && continue
2474 if [[ -L $file ]]; then
2475 inst_symlink
"$file"
2478 mkdir
-m 0755 "${initdir}/$file" ||
return 1
2479 [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file"
2480 chmod u
+w
"${initdir}/$file"
2485 # $1 = file to copy to ramdisk
2486 # $2 (optional) Name for the file on the ramdisk
2487 # Location of the image dir is assumed to be $initdir
2488 # We never overwrite the target if it exists.
2490 [[ -f "${1:?}" ]] ||
return 1
2491 strstr
"$1" "/" ||
return 1
2494 local target
="${2:-$1}"
2495 if ! [[ -d ${initdir:?}/$target ]]; then
2496 [[ -e ${initdir}/$target ]] && return 0
2497 [[ -L ${initdir}/$target ]] && return 0
2498 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
2500 # install checksum files also
2501 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2502 inst
"${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
2504 ddebug
"Installing $src"
2505 cp --sparse=always
-pfL "$src" "${initdir}/$target"
2508 # find symlinks linked to given library file
2510 # Function searches for symlinks by stripping version numbers appended to
2511 # library filename, checks if it points to the same target and finally
2512 # prints the list of symlinks to stdout.
2515 # rev_lib_symlinks libfoo.so.8.1
2516 # output: libfoo.so.8 libfoo.so
2517 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
2518 rev_lib_symlinks
() {
2522 orig
="$(readlink -f "$1")"
2524 [[ "${fn}" =~ .
*\.so\..
* ]] ||
return 1
2526 until [[ "${fn##*.}" == so
]]; do
2528 [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}"
2534 # Same as above, but specialized to handle dynamic libraries.
2535 # It handles making symlinks according to how the original library
2539 local dest
="${2:-$1}"
2540 local reallib symlink
2542 strstr
"$1" "/" ||
return 1
2543 [[ -e ${initdir:?}/$dest ]] && return 0
2544 if [[ -L $src ]]; then
2545 # install checksum files also
2546 if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
2547 inst
"${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac"
2549 reallib
="$(readlink -f "$src")"
2550 inst_simple
"$reallib" "$reallib"
2551 inst_dir
"${dest%/*}"
2552 [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
2553 ddebug
"Creating symlink $reallib -> $dest"
2554 ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
2556 inst_simple "$src" "$dest"
2559 # Create additional symlinks. See rev_symlinks description.
2560 for symlink in $(rev_lib_symlinks "$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do
2561 if [[ ! -e "$initdir/$symlink" ]]; then
2562 ddebug "Creating extra symlink
: $symlink"
2563 inst_symlink "$symlink"
2568 # find a binary. If we were not passed the full path directly,
2569 # search in the usual places to find the binary.
2572 if [[ -z ${bin##/*} ]]; then
2573 if [[ -x "$bin" ]] || { strstr "$bin" ".so
" && ldd "$bin" &>/dev/null; }; then
2582 # Same as above, but specialized to install binary executables.
2583 # Install binary executable, and all shared library dependencies, if any.
2588 # In certain cases we might attempt to install a binary which is already
2589 # present in the test image, yet it's missing from the host system.
2590 # In such cases, let's check if the binary indeed exists in the image
2591 # before doing any other checks. If it does, immediately return with
2593 if [[ $# -eq 1 ]]; then
2594 for path in "" bin sbin usr/bin usr/sbin; do
2595 [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0
2599 bin="$
(find_binary
"$bin")" || return 1
2601 [[ -e "${initdir:?}/$target" ]] && return 0
2602 [[ -L "$bin" ]] && inst_symlink "$bin" "$target" && return 0
2605 local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
2606 # DSOs provided by systemd
2607 local systemd_so_regex='/(libudev|libsystemd.*|.+[\-_]systemd([\-_].+)?|libnss_(mymachines|myhostname|resolve)).so'
2610 while read -r line; do
2611 [[ "$line" = 'not a dynamic executable' ]] && break
2613 # Ignore errors about our own stuff missing. This is most likely caused
2614 # by ldd attempting to use the unprefixed RPATH.
2615 [[ "$line" =~ libsystemd.*\ not\ found ]] && continue
2617 # We're built with ASan and the target binary loads one of the systemd's
2618 # DSOs, so we need to tweak the environment before executing the binary
2619 if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$line" =~ $systemd_so_regex ]]; then
2623 if [[ "$line" =~ $so_regex ]]; then
2624 file="${BASH_REMATCH[1]}"
2625 [[ -e "${initdir}/$file" ]] && continue
2626 inst_library "$file"
2630 if [[ "$line" =~ not\ found ]]; then
2631 dfatal "Missing a shared library required by
$bin.
"
2632 dfatal "Run
\"ldd
$bin\" to
find out what it is.
"
2634 dfatal "Cannot create a
test image.
"
2637 done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
2639 # Same as above, but we need to wrap certain libraries unconditionally
2641 # chown, getent, login, su, useradd, userdel - dlopen()s (not only) systemd's PAM modules
2642 # ls, stat - pulls in nss_systemd with certain options (like ls -l) when
2643 # nsswitch.conf uses [SUCCESS=merge] (like on Arch Linux)
2644 # delv, dig - pulls in nss_resolve if `resolve` is in nsswitch.conf
2645 # tar - called by machinectl in TEST-25
2646 if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ /(chown|delv|dig|getent|login|ls|stat|su|tar|useradd|userdel)$ ]]; then
2650 # If the target binary is built with ASan support, we don't need to wrap
2651 # it, as it should handle everything by itself
2652 if get_bool "$wrap_binary" && ! is_built_with_asan "$bin"; then
2653 dinfo "Creating ASan-compatible wrapper
for binary
'$target'"
2654 # Install the target binary with a ".orig
" suffix
2655 inst_simple "$bin" "${target}.orig
"
2656 # Create a simple shell wrapper in place of the target binary, which
2657 # sets necessary ASan-related env variables and then exec()s the
2658 # suffixed target binary
2659 cat >"$initdir/$target" <<EOF
2661 # Preload the ASan runtime DSO, otherwise ASAn will complain
2662 export LD_PRELOAD="$ASAN_RT_PATH"
2663 # Disable LSan to speed things up, since we don't care about leak reports
2664 # from 'external' binaries
2665 export ASAN_OPTIONS=detect_leaks=0
2666 # Set argv[0] to the original binary name without the ".orig
" suffix
2667 exec -a "\
$0" -- "${target}.orig
" "\$@
"
2669 chmod +x "$initdir/$target"
2671 inst_simple "$bin" "$target"
2675 # same as above, except for shell scripts.
2676 # If your shell script does not start with shebang, it is not a shell script.
2678 local bin line shebang_regex
2679 bin="$
(find_binary
"${1:?}")" || return 1
2682 read -r -n 80 line <"$bin"
2683 # If debug is set, clean unprintable chars to prevent messing up the term
2684 get_bool "${debug:=}" && line="$
(echo -n "$line" |
tr -c -d '[:print:][:space:]')"
2685 shebang_regex='(#! *)(/[^ ]+).*'
2686 [[ "$line" =~ $shebang_regex ]] || return 1
2687 inst "${BASH_REMATCH[2]}" && inst_simple "$bin" "$@
"
2690 # same as above, but specialized for symlinks
2693 local target="${2:-$src}"
2696 strstr "$src" "/" || return 1
2697 [[ -L "$src" ]] || return 1
2698 [[ -L "${initdir:?}/$target" ]] && return 0
2699 realsrc="$
(readlink
-f "$src")"
2700 if ! [[ -e "$initdir/$realsrc" ]]; then
2701 if [[ -d "$realsrc" ]]; then
2707 [[ ! -e "$initdir/${target%/*}" ]] && inst_dir "${target%/*}"
2708 [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}"
2709 ln -sfn -- "$
(convert_abs_rel
"${target}" "${realsrc}")" "$initdir/$target"
2712 # attempt to install any programs specified in a udev rule
2713 inst_rule_programs() {
2717 sed -rn 's/^.*?PROGRAM==?"([^
"]+).*$/\1/p' "$rule" | while read -r prog; do
2718 if [ -x "/lib
/udev
/$prog" ]; then
2719 bin="/lib
/udev
/$prog"
2721 if ! bin="$
(find_binary
"$prog")"; then
2722 dinfo "Skipping program
$prog used
in udev rule $
(basename "$rule") as it cannot be found
"
2727 #dinfo "Installing
$_bin due to it
's use in the udev rule $(basename $1)"
2728 image_install "$bin"
2732 # udev rules always get installed in the same place, so
2733 # create a function to install them to make life simpler.
2735 local target=/etc/udev/rules.d
2738 inst_dir "/lib/udev/rules.d"
2740 for rule in "$@"; do
2741 if [ "${rule#/}" = "$rule" ]; then
2742 for r in /lib/udev/rules.d /etc/udev/rules.d; do
2743 if [[ -f "$r/$rule" ]]; then
2745 inst_simple "$found"
2746 inst_rule_programs "$found"
2751 if [[ -f "${r}${rule}" ]]; then
2753 inst_simple "$found" "$target/${found##*/}"
2754 inst_rule_programs "$found"
2757 [[ $found ]] || dinfo "Skipping udev rule: $rule"
2762 # general purpose installation function
2763 # Same args as above.
2768 [[ ! "$initdir" && -d "$2" ]] && export initdir="$2"
2769 [[ "$initdir" = "$2" ]] && set "$1"
2772 [[ -z "$initdir" ]] && export initdir="$2"
2776 dfatal "inst only takes 1 or 2 or 3 arguments"
2782 for fun in inst_symlink inst_script inst_binary inst_simple; do
2783 "$fun" "$@" && return 0
2786 dwarn "Failed to install '$1'"
2790 # install any of listed files
2792 # If first argument is '-d' and second some destination path, first accessible
2793 # source is installed into this path, otherwise it will installed in the same
2794 # path as source. If none of listed files was installed, function return 1.
2795 # On first successful installation it returns with 0 status.
2799 # inst_any -d /bin/foo /bin/bar /bin/baz
2801 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
2806 [[ "${1:?}" = '-d' ]] && dest="${2:?}" && shift 2
2808 for file in "$@"; do
2809 if [[ -e "$file" ]]; then
2810 [[ -n "$dest" ]] && inst "$file" "$dest" && return 0
2811 inst "$file" && return 0
2822 # Make sure the source exists, as the process substitution below
2824 stat "$p" >/dev/null || return 1
2826 while read -r item; do
2827 if [[ -d "$item" ]]; then
2829 elif [[ -f "$item" ]]; then
2832 done < <(find "$p" 2>/dev/null)
2836 # image_install [-o ] <file> [<file> ... ]
2837 # Install <file> to the test image
2838 # -o optionally install the <file> and don't fail
, if it is not there
2843 if [[ "$prog" = '-o' ]]; then
2848 for prog
in "$@"; do
2849 if ! inst
"$prog" ; then
2850 if get_bool
"$optional"; then
2851 dinfo
"Skipping program $prog as it cannot be found and is" \
2852 "flagged to be optional"
2854 dfatal
"Failed to install $prog"
2861 # Install a single kernel module along with any firmware it may require.
2862 # $1 = full path to kernel module to install
2863 install_kmod_with_fw
() {
2864 local module
="${1:?}"
2865 # no need to go further if the module is already installed
2866 [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*"/lib/modules/$KERNEL_VER/"}" ]] && return 0
2867 [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0
2869 [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}"
2871 inst_simple
"$module" "/lib/modules/$KERNEL_VER/${module##*"/lib/modules/$KERNEL_VER/"}" ||
return $?
2873 local modname
="${module##*/}"
2874 local fwdir found fw
2875 modname
="${modname%.ko*}"
2877 while read -r fw
; do
2879 for fwdir
in /lib
/firmware
/updates
/lib
/firmware
; do
2880 if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then
2881 inst_simple
"$fwdir/$fw" "/lib/firmware/$fw"
2885 if ! get_bool
"$found"; then
2886 if ! grep -qe "\<${modname//-/_}\>" /proc
/modules
; then
2887 dinfo
"Possible missing firmware \"${fw}\" for kernel module" \
2890 dwarn
"Possible missing firmware \"${fw}\" for kernel module" \
2894 done < <(modinfo
-k "$KERNEL_VER" -F firmware
"$module" 2>/dev
/null
)
2898 # Do something with all the dependencies of a kernel module.
2899 # Note that kernel modules depend on themselves using the technique we use
2900 # $1 = function to call for each dependency we find
2901 # It will be passed the full path to the found kernel module
2902 # $2 = module to get dependencies for
2903 # rest of args = arguments to modprobe
2904 for_each_kmod_dep
() {
2911 while read -r cmd modpath _
; do
2912 [[ "$cmd" = insmod
]] ||
continue
2913 "$func" "$modpath" ||
return $?
2915 done < <(modprobe
"$@" --ignore-install --show-depends "$kmod")
2917 ! get_bool
"$found" && return 1
2921 # instmods [-c] <kernel module> [<kernel module> ... ]
2922 # instmods [-c] <kernel subsystem>
2923 # install kernel modules along with all their dependencies.
2924 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
2925 # FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5
2928 if [[ $# -ge 0 && "$1" = '-c' ]]; then
2936 local mod_dir
="/lib/modules/${KERNEL_VER:?}/"
2940 if [ -f "${mod_dir}/modules.${mod#=}" ]; then
2942 [[ "$mpargs" ]] && echo "$mpargs"
2943 cat "${mod_dir}/modules.${mod#=}"
2947 [[ "$mpargs" ]] && echo "$mpargs"
2948 find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f
-printf '%f\n'
2956 # Do not load this diagnostic-only module
2961 # if we are already installed, skip this module and go on
2963 [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return
2965 # We use '-d' option in modprobe only if modules prefix path
2966 # differs from default '/'. This allows us to use Dracut with
2967 # old version of modprobe which doesn't have '-d' option.
2968 local mod_dirname
=${mod_dir%%/lib/modules/*}
2969 [[ -n ${mod_dirname} ]] && mod_dirname
="-d ${mod_dirname}/"
2971 # ok, load the module, all its dependencies, and any firmware
2973 for_each_kmod_dep install_kmod_with_fw
"$mod" \
2974 --set-version "$KERNEL_VER" \
2975 ${mod_dirname:+"$mod_dirname"} \
2976 ${mpargs:+"$mpargs"}
2985 if [[ $# -eq 0 ]]; then # filenames from stdin
2986 while read -r mod
; do
2987 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2988 dfatal
"Failed to install $mod"
2994 for mod
in "$@"; do # filenames as arguments
2995 if ! inst1mod
"${mod%.ko*}" && [ "$check" = "yes" ]; then
2996 dfatal
"Failed to install $mod"
3005 local mountpoint
="${1:?}"
3006 if mountpoint
-q "$mountpoint"; then
3007 ddebug
"umount $mountpoint"
3008 umount
"$mountpoint"
3012 # can be overridden in specific test
3013 test_setup_cleanup
() {
3018 # (post-test) cleanup should always ignore failure and cleanup as much as possible
3021 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
3022 [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC"
3023 # If multiple setups/cleans are ran in parallel, this can cause a race
3024 if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then
3025 rm -vf "${IMAGESTATEDIR}/default.img"
3027 [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR"
3028 [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE"
3032 # can be overridden in specific test
3037 test_cleanup_again
() {
3038 [ -n "$TESTDIR" ] ||
return
3039 rm -rf "$TESTDIR/unprivileged-nspawn-root"
3040 [[ -n "$initdir" ]] && _umount_dir
"$initdir"
3043 test_create_image
() {
3044 create_empty_image_rootdir
3046 # Create what will eventually be our root filesystem onto an overlay
3049 setup_basic_environment
3054 if get_bool
"${TEST_REQUIRE_INSTALL_TESTS:?}" && \
3055 command -v meson
>/dev
/null
&& \
3056 [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
3057 dfatal
"$BUILD_DIR needs to be built with -Dinstall-tests=true"
3061 if [ -e "${IMAGE_PRIVATE:?}" ]; then
3062 echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")"
3065 if [ ! -e "${IMAGE_PUBLIC:?}" ]; then
3066 # default.img is the base that every test uses and optionally appends to
3067 if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] ||
[ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then
3068 # Create the backing public image, but then completely unmount
3069 # it and drop the loopback device responsible for it, since we're
3070 # going to symlink/copy the image and mount it again from
3072 local image_old
="${IMAGE_PUBLIC}"
3073 if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
3074 IMAGE_PUBLIC
="${IMAGESTATEDIR}/default.img"
3080 IMAGE_PUBLIC
="${image_old}"
3082 if [ "${IMAGE_NAME:?}" != "default" ] && ! get_bool
"${TEST_FORCE_NEWIMAGE}"; then
3083 cp -v "$(realpath "${IMAGESTATEDIR}/default.img
")" "$IMAGE_PUBLIC"
3088 declare -f -F test_append_files
>/dev
/null
&& hook_defined
=yes || hook_defined
=no
3090 echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")"
3091 if get_bool
"$TEST_PARALLELIZE" || get_bool
"$hook_defined"; then
3092 cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
3094 ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
3099 if get_bool
"${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED}"; then
3100 dinfo
"Masking supporting services"
3101 mask_supporting_services
3104 # Send stdout/stderr of testsuite-*.service units to both journal and
3105 # console to make debugging in CIs easier
3106 # Note: we can't use a dropin for `testsuite-.service`, since that also
3107 # overrides 'sub-units' of some tests that already use a specific
3108 # value for Standard(Output|Error)=
3109 # (e.g. test/units/testsuite-66-deviceisolation.service)
3110 if ! get_bool
"$INTERACTIVE_DEBUG"; then
3111 local dropin_dir
="${initdir:?}/etc/systemd/system/testsuite-${TESTID:?}.service.d"
3112 mkdir
-p "$dropin_dir"
3113 printf '[Service]\nStandardOutput=journal+console\nStandardError=journal+console' >"$dropin_dir/99-stdout.conf"
3116 if get_bool
"$hook_defined"; then
3117 test_append_files
"${initdir:?}"
3125 local test_id
="${1:?}"
3128 if ! get_bool
"${TEST_NO_QEMU:=}"; then
3129 if run_qemu
"$test_id"; then
3130 check_result_qemu ||
{ echo "qemu test failed"; return 1; }
3132 dwarn
"can't run qemu, skipping"
3135 if ! get_bool
"${TEST_NO_NSPAWN:=}"; then
3137 if run_nspawn
"${initdir:?}" "$test_id"; then
3138 check_result_nspawn
"$initdir" ||
{ echo "nspawn-root test failed"; return 1; }
3140 dwarn
"can't run systemd-nspawn, skipping"
3143 if get_bool
"${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
3144 dir
="$TESTDIR/unprivileged-nspawn-root"
3145 if NSPAWN_ARGUMENTS
="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn
"$dir" "$test_id"; then
3146 check_result_nspawn
"$dir" ||
{ echo "unprivileged-nspawn-root test failed"; return 1; }
3148 dwarn
"can't run systemd-nspawn, skipping"
3156 if [[ $UID != "0" ]]; then
3157 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
3161 if get_bool
"${TEST_NO_QEMU:=}" && get_bool
"${TEST_NO_NSPAWN:=}"; then
3162 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both qemu and nspawn disabled" >&2
3166 if get_bool
"${TEST_QEMU_ONLY:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
3167 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: qemu-only tests requested" >&2
3171 if get_bool
"${TEST_PREFER_NSPAWN:=}" && ! get_bool
"$TEST_NO_NSPAWN"; then
3176 [[ "$libdir" ]] ||
for libdir
in /lib64
/lib
; do
3177 [[ -d $libdir ]] && libdirs
+=" $libdir" && break
3180 [[ "$usrlibdir" ]] ||
for usrlibdir
in /usr
/lib64
/usr
/lib
; do
3181 [[ -d $usrlibdir ]] && libdirs
+=" $usrlibdir" && break
3184 mkdir
-p "$STATEDIR"
3189 if [ -n "${SUDO_USER}" ]; then
3190 ddebug
"Making ${TESTDIR:?} readable for ${SUDO_USER} (acquired from sudo)"
3191 setfacl
-m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}"
3194 testname
="$(basename "$PWD")"
3196 while (($# > 0)); do
3199 echo "${testname} RUN: $TEST_DESCRIPTION"
3202 if [ $ret -eq 0 ]; then
3203 echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
3205 echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
3210 echo "${testname} SETUP: $TEST_DESCRIPTION"
3215 echo "${testname} CLEANUP: $TEST_DESCRIPTION"
3219 echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
3224 echo -n "${testname}: $TEST_DESCRIPTION "
3225 # Do not use a subshell, otherwise cleanup variables (LOOPDEV) will be lost
3226 # and loop devices will leak
3227 test_setup
</dev
/null
>"$TESTLOG" 2>&1 || ret
=$?
3228 if [ $ret -eq 0 ]; then
3229 test_setup_cleanup
</dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
3231 if [ $ret -eq 0 ]; then
3232 test_run
"$TESTID" </dev
/null
>>"$TESTLOG" 2>&1 || ret
=$?
3235 if [ $ret -eq 0 ]; then