]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - test/test-functions
test: add function to reduce copied setup boilerplate
[thirdparty/systemd.git] / test / test-functions
index 5dbbff0f068a2c87f4220ca2671684e8b75a63df..3afc3241106ef8491e83d1f80586722d80d9024d 100644 (file)
@@ -4,9 +4,9 @@
 PATH=/sbin:/bin:/usr/sbin:/usr/bin
 export PATH
 
-LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && echo yes || true)
-LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && echo yes || true)
-LOOKS_LIKE_SUSE=$(source /etc/os-release && [[ " $ID_LIKE " = *" suse "* ]] && echo yes || true)
+LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && echo yes || :)
+LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && echo yes || :)
+LOOKS_LIKE_SUSE=$(source /etc/os-release && [[ " $ID_LIKE " = *" suse "* ]] && echo yes || :)
 KERNEL_VER=${KERNEL_VER-$(uname -r)}
 KERNEL_MODS="/lib/modules/$KERNEL_VER/"
 QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}"
@@ -17,12 +17,27 @@ UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
 EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
 QEMU_MEM="${QEMU_MEM:-512M}"
 
+# Decide if we can (and want to) run QEMU with KVM acceleration.
+# Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
+# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
+# running under KVM. If these conditions are met, enable KVM (and possibly
+# nested KVM), otherwise disable it.
+if [[ -n "$TEST_NESTED_KVM" || ( -z "$TEST_NO_KVM" && $(systemd-detect-virt -v) != kvm ) ]]; then
+    QEMU_KVM=yes
+else
+    QEMU_KVM=no
+fi
+
 if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
     echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
     ROOTLIBDIR=/usr/lib/systemd
 fi
 
 PATH_TO_INIT=$ROOTLIBDIR/systemd
+[ "$SYSTEMD_JOURNALD" ] || SYSTEMD_JOURNALD=$(which -a $BUILD_DIR/systemd-journald $ROOTLIBDIR/systemd-journald 2>/dev/null | grep '^/' -m1)
+[ "$SYSTEMD" ] || SYSTEMD=$(which -a $BUILD_DIR/systemd $ROOTLIBDIR/systemd 2>/dev/null | grep '^/' -m1)
+[ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1)
+[ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1)
 
 BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
 DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv"
@@ -38,7 +53,7 @@ is_built_with_asan() {
     fi
 
     # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
-    local _asan_calls=$(objdump -dC $BUILD_DIR/systemd-journald | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
+    local _asan_calls=$(objdump -dC $SYSTEMD_JOURNALD | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
     if (( $_asan_calls < 1000 )); then
         return 1
     else
@@ -52,13 +67,13 @@ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
     STRIP_BINARIES=no
     SKIP_INITRD="${SKIP_INITRD:-yes}"
     PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
-    QEMU_MEM="1536M"
+    QEMU_MEM="2048M"
     QEMU_SMP=4
 
     # We need to correctly distinguish between gcc's and clang's ASan DSOs.
-    if ldd $BUILD_DIR/systemd | grep -q libasan.so; then
+    if ldd $SYSTEMD | grep -q libasan.so; then
         ASAN_COMPILER=gcc
-    elif ldd $BUILD_DIR/systemd | grep -q libclang_rt.asan; then
+    elif ldd $SYSTEMD | grep -q libclang_rt.asan; then
         ASAN_COMPILER=clang
 
         # As clang's ASan DSO is usually in a non-standard path, let's check if
@@ -66,8 +81,8 @@ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
         # We're not setting the LD_LIBRARY_PATH automagically here, because
         # user should encounter (and fix) the same issue when running the unit
         # tests (meson test)
-        if ldd "$BUILD_DIR/systemd" | grep -q "libclang_rt.asan.*not found"; then
-            _asan_rt_name="$(ldd $BUILD_DIR/systemd | awk '/libclang_rt.asan/ {print $1; exit}')"
+        if ldd "$SYSTEMD" | grep -q "libclang_rt.asan.*not found"; then
+            _asan_rt_name="$(ldd $SYSTEMD | awk '/libclang_rt.asan/ {print $1; exit}')"
             _asan_rt_path="$(find /usr/lib* /usr/local/lib* -type f -name "$_asan_rt_name" 2>/dev/null | sed 1q)"
             echo >&2 "clang's ASan DSO ($_asan_rt_name) is not present in the runtime library path"
             echo >&2 "Consider setting LD_LIBRARY_PATH=${_asan_rt_path%/*}"
@@ -82,9 +97,7 @@ fi
 
 function find_qemu_bin() {
     # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
-    # Either way, only use this version if we aren't running in KVM, because
-    # nested KVM is flaky still.
-    if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
+    if [[ $QEMU_KVM == "yes" ]]; then
         [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
     fi
 
@@ -102,7 +115,7 @@ function find_qemu_bin() {
         [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu 2>/dev/null | grep '^/' -m1)
         ;;
     ppc64*)
-        [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-$ARCH 2>/dev/null | grep '^/' -m1)
+        [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-ppc64 2>/dev/null | grep '^/' -m1)
         ;;
     esac
 
@@ -209,14 +222,15 @@ $KERNEL_APPEND \
 -nographic \
 -kernel $KERNEL_BIN \
 -drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
+$QEMU_OPTIONS \
 "
 
     if [[ "$INITRD" && "$SKIP_INITRD" != "yes" ]]; then
         QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
     fi
 
-    # Let's use KVM if it is available, but let's avoid using nested KVM as that is still flaky
-    if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
+    # Let's use KVM if possible
+    if [[ -c /dev/kvm && $QEMU_KVM == "yes" ]]; then
         QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
     fi
 
@@ -239,18 +253,18 @@ $KERNEL_APPEND \
 run_nspawn() {
     [[ -d /run/systemd/system ]] || return 1
 
-    local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
+    local _nspawn_cmd="$SYSTEMD_NSPAWN $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
     if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
         _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
     fi
 
     if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
-        dwarn "nspawn doesn't support UNIFIED_CGROUP_HIERARCHY=hybrid, skipping"
+        dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
         exit
     elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
-        _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
+        _nspawn_cmd="env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
     elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
-        _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
+        _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY $_nspawn_cmd"
     else
         dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
         exit 1
@@ -486,8 +500,7 @@ install_fsck() {
 
 install_dmevent() {
     instmods dm_crypt =crypto
-    type -P dmeventd >/dev/null && dracut_install dmeventd
-    inst_libdir_file "libdevmapper-event.so*"
+    inst_binary dmeventd
     if [[ "$LOOKS_LIKE_DEBIAN" ]]; then
         # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
         # and since buster/bionic 95-dm-notify.rules
@@ -516,6 +529,8 @@ install_systemd() {
 
     # enable debug logging in PID1
     echo LogLevel=debug >> $initdir/etc/systemd/system.conf
+    # store coredumps in journal
+    echo Storage=journal >> $initdir/etc/systemd/coredump.conf
 }
 
 get_ldpath() {
@@ -537,7 +552,7 @@ create_empty_image() {
     fi
     rm -f "$TESTDIR/rootdisk.img"
     # Create the blank file to use as a root filesystem
-    dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek="$_size"
+    truncate -s "${_size}M" "$TESTDIR/rootdisk.img"
     LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
     [ -b "$LOOPDEV" ] || return 1
     echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
@@ -557,6 +572,13 @@ EOF
     fi
 }
 
+create_empty_image_rootdir() {
+    create_empty_image
+    mkdir -p $initdir
+    mount ${LOOPDEV}p1 $initdir
+    TEST_SETUP_CLEANUP_ROOTDIR=1
+}
+
 check_asan_reports() {
     local ret=0
     local root="$1"
@@ -571,12 +593,12 @@ check_asan_reports() {
         journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
         if [[ ! -z "$journald_report" ]]; then
             printf "%s\n" "$journald_report"
-            cat "$root/systemd-journald.out" || true
+            cat "$root/systemd-journald.out" || :
             ret=$(($ret+1))
         fi
 
         pids=$(
-            "$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne '
+            "$JOURNALCTL" -D "$root/var/log/journal" | perl -alne '
                  BEGIN {
                      %services_to_ignore = (
                          "dbus-daemon" => undef,
@@ -587,7 +609,7 @@ check_asan_reports() {
         if [[ ! -z "$pids" ]]; then
             ret=$(($ret+1))
             for pid in $pids; do
-                "$BUILD_DIR/journalctl" -D "$root/var/log/journal" _PID=$pid --no-pager
+                "$JOURNALCTL" -D "$root/var/log/journal" _PID=$pid --no-pager
             done
         fi
     fi
@@ -613,13 +635,13 @@ check_result_nspawn() {
 # can be overridden in specific test
 check_result_qemu() {
     local ret=1
-    mkdir -p $TESTDIR/root
-    mount ${LOOPDEV}p1 $TESTDIR/root
-    [[ -e $TESTDIR/root/testok ]] && ret=0
-    [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
-    cp -a $TESTDIR/root/var/log/journal $TESTDIR
-    check_asan_reports "$TESTDIR/root" || ret=$(($ret+1))
-    umount $TESTDIR/root
+    mkdir -p $initdir
+    mount ${LOOPDEV}p1 $initdir
+    [[ -e $initdir/testok ]] && ret=0
+    [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
+    cp -a $initdir/var/log/journal $TESTDIR
+    check_asan_reports "$initdir" || ret=$(($ret+1))
+    umount $initdir
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
     ls -l $TESTDIR/journal/*/*.journal
     test -s $TESTDIR/failed && ret=$(($ret+1))
@@ -635,7 +657,7 @@ strip_binaries() {
     ddebug "Strip binaries"
     find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
         xargs strip --strip-unneeded |& \
-        grep -v 'file format not recognized' | \
+        grep -vi 'file format not recognized' | \
         ddebug
 }
 
@@ -695,15 +717,15 @@ install_ld_so_conf() {
 }
 
 install_config_files() {
-    inst /etc/sysconfig/init || true
+    inst /etc/sysconfig/init || :
     inst /etc/passwd
     inst /etc/shadow
     inst /etc/login.defs
     inst /etc/group
     inst /etc/shells
     inst /etc/nsswitch.conf
-    inst /etc/pam.conf || true
-    inst /etc/securetty || true
+    inst /etc/pam.conf || :
+    inst /etc/securetty || :
     inst /etc/os-release
     inst /etc/localtime
     # we want an empty environment
@@ -891,6 +913,15 @@ setup_basic_dirs() {
     ln -sfn /run/lock "$initdir/var/lock"
 }
 
+mask_supporting_services() {
+    # mask some services that we do not want to run in these tests
+    ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+    ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+    ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+    ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+    ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+}
+
 inst_libs() {
     local _bin=$1
     local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
@@ -932,6 +963,7 @@ import_testdir() {
 
 import_initdir() {
     initdir=$TESTDIR/root
+    mkdir -p $initdir
     export initdir
 }
 
@@ -1580,7 +1612,7 @@ instmods() {
                     | instmods
                 else
                     ( [[ "$_mpargs" ]] && echo $_mpargs
-                      find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \
+                      find "$KERNEL_MODS" -path "*/${_mod#=}/*" -type f -printf '%f\n' ) \
                     | instmods
                 fi
                 ;;
@@ -1653,43 +1685,46 @@ instmods() {
     return $_ret
 }
 
-# inst_libdir_file [-n <pattern>] <file> [<file>...]
-# Install a <file> located on a lib directory to the initramfs image
-# -n <pattern> install non-matching files
-inst_libdir_file() {
-    if [[ "$1" == "-n" ]]; then
-        local _pattern=$1
-        shift 2
-        for _dir in $libdirs; do
-            for _i in "$@"; do
-                for _f in "$_dir"/$_i; do
-                    [[ "$_i" =~ $_pattern ]] || continue
-                    [[ -e "$_i" ]] && dracut_install "$_i"
-                done
-            done
-        done
-    else
-        for _dir in $libdirs; do
-            for _i in "$@"; do
-                for _f in "$_dir"/$_i; do
-                    [[ -e "$_f" ]] && dracut_install "$_f"
-                done
-            done
-        done
-    fi
-}
-
 setup_suse() {
     ln -fs ../usr/bin/systemctl $initdir/bin/
     ln -fs ../usr/lib/systemd $initdir/lib/
     inst_simple "/usr/lib/systemd/system/haveged.service"
 }
 
+_umount_dir() {
+    if mountpoint -q $1; then
+        ddebug "umount $1"
+        umount $1
+    fi
+}
+
+_test_setup_cleanup() {
+    # only umount if create_empty_image_rootdir() was called to mount it
+    [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
+}
+
+# can be overridden in specific test
+test_setup_cleanup() {
+    _test_setup_cleanup
+}
+
+_test_cleanup() {
+    # (post-test) cleanup should always ignore failure and cleanup as much as possible
+    (
+        set +e
+        _umount_dir $initdir
+        if [[ $LOOPDEV && -b $LOOPDEV ]]; then
+            ddebug "losetup -d $LOOPDEV"
+            losetup -d $LOOPDEV
+        fi
+        rm -fr "$TESTDIR"
+        rm -f "$STATEFILE"
+    ) || :
+}
+
 # can be overridden in specific test
 test_cleanup() {
-    umount $TESTDIR/root 2>/dev/null || true
-    [[ $LOOPDEV ]] && losetup -d $LOOPDEV || true
-    return 0
+    _test_cleanup
 }
 
 test_run() {
@@ -1753,24 +1788,21 @@ do_test() {
             --setup)
                 echo "TEST SETUP: $TEST_DESCRIPTION"
                 test_setup
+                test_setup_cleanup
                 ;;
             --clean)
                 echo "TEST CLEANUP: $TEST_DESCRIPTION"
                 test_cleanup
-                rm -fr "$TESTDIR"
-                rm -f "$STATEFILE"
                 ;;
             --all)
                 ret=0
-                echo -n "TEST: $TEST_DESCRIPTION ";
+                echo -n "TEST: $TEST_DESCRIPTION "
                 (
-                    test_setup && test_run
-                    ret=$?
-                    test_cleanup
-                    rm -fr "$TESTDIR"
-                    rm -f "$STATEFILE"
-                    exit $ret
+                    test_setup
+                    test_setup_cleanup
+                    test_run
                 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
+                test_cleanup
                 if [ $ret -eq 0 ]; then
                     rm "$TESTLOG"
                     echo "[OK]"