]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - test/test-functions
tty-ask-password: copy argv[] before forking child
[thirdparty/systemd.git] / test / test-functions
index f1646b3bcffd05091386b307b59a23d8f25ad136..7179f70e46c2e63c97ddaf254fb211e10150dc32 100644 (file)
@@ -15,6 +15,7 @@ TIMED_OUT=  # will be 1 after run_* if *_TIMEOUT is set and test timed out
 [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
 UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
 EFI_MOUNT="$(bootctl -p 2>/dev/null || echo /boot)"
+QEMU_MEM="${QEMU_MEM:-512M}"
 
 if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
     echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
@@ -23,8 +24,8 @@ fi
 
 PATH_TO_INIT=$ROOTLIBDIR/systemd
 
-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
-DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"
+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"
 
 STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
 STATEFILE="$STATEDIR/.testdir"
@@ -37,7 +38,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 | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
+    local _asan_calls=$(objdump -dC $BUILD_DIR/systemd-journald | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
     if (( $_asan_calls < 1000 )); then
         return 1
     else
@@ -51,13 +52,15 @@ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
     STRIP_BINARIES=no
     SKIP_INITRD=yes
     PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
+    QEMU_MEM="1536M"
+    QEMU_SMP=4
 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 ] ; then
+    if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
         [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
     fi
 
@@ -96,17 +99,28 @@ run_qemu() {
             && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
     fi
 
+    CONSOLE=ttyS0
+
     if [[ ! "$KERNEL_BIN" ]]; then
         if [[ "$LOOKS_LIKE_ARCH" ]]; then
             KERNEL_BIN=/boot/vmlinuz-linux
         else
-            KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
+            [ "$ARCH" ] || ARCH=$(uname -m)
+            case $ARCH in
+                ppc64*)
+                KERNEL_BIN=/boot/vmlinux-$KERNEL_VER
+                CONSOLE=hvc0
+                ;;
+                *)
+                KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
+                ;;
+            esac
         fi
     fi
 
     default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
     default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
-    default_arch_initrd=/boot/initramfs-linux.img
+    default_arch_initrd=/boot/initramfs-linux-fallback.img
     default_suse_initrd=/boot/initrd-${KERNEL_VER}
     if [[ ! "$INITRD" ]]; then
         if [[ -e "$default_fedora_initrd" ]]; then
@@ -120,7 +134,16 @@ run_qemu() {
         fi
     fi
 
-    [ "$QEMU_SMP" ]   || QEMU_SMP=1
+    # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
+    # i.e. use the number of online CPUs on the host machine. If the nproc utility
+    # is not installed or there's some other error when calling it, fall back
+    # to the original value (QEMU_SMP=1).
+    if ! [ "$QEMU_SMP" ]; then
+        if ! QEMU_SMP=$(nproc); then
+            dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
+            QEMU_SMP=1
+        fi
+    fi
 
     find_qemu_bin || return 1
 
@@ -138,6 +161,8 @@ run_qemu() {
 
 if [[ "$LOOKS_LIKE_SUSE" ]]; then
     PARAMS+="rd.hostonly=0"
+elif [[ "$LOOKS_LIKE_ARCH" ]]; then
+    PARAMS+="rw"
 else
     PARAMS+="ro"
 fi
@@ -147,7 +172,7 @@ root=/dev/sda1 \
 raid=noautodetect \
 loglevel=2 \
 init=$PATH_TO_INIT \
-console=ttyS0 \
+console=$CONSOLE \
 selinux=0 \
 printk.devkmsg=on \
 $_cgroup_args \
@@ -156,7 +181,7 @@ $KERNEL_APPEND \
 
     QEMU_OPTIONS="-smp $QEMU_SMP \
 -net none \
--m 512M \
+-m $QEMU_MEM \
 -nographic \
 -kernel $KERNEL_BIN \
 -drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
@@ -167,7 +192,7 @@ $KERNEL_APPEND \
     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 -a `systemd-detect-virt -v` != kvm ]; then
+    if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
         QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
     fi
 
@@ -330,7 +355,7 @@ create_asan_wrapper() {
 set -x
 
 DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
-DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1
+DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
 DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
 
 mount -t proc proc /proc
@@ -344,6 +369,7 @@ if [[ "\$PATH_TO_ASAN" ]]; then
   DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
 fi
 echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
+echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
 
 # ASAN and syscall filters aren't compatible with each other.
 find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
@@ -354,6 +380,12 @@ JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
 mkdir -p "\$JOURNALD_CONF_DIR"
 printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log\n" >"\$JOURNALD_CONF_DIR/env.conf"
 
+# 90s isn't enough for some services to finish when literally everything is run
+# under ASan+UBSan in containers, which, in turn, are run in VMs.
+# Let's limit which environments such services should be executed in.
+mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
+printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
+
 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
 exec  $ROOTLIBDIR/systemd "\$@"
 EOF
@@ -421,7 +453,7 @@ get_ldpath() {
 
 install_missing_libraries() {
     # install possible missing libraries
-    for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/*; do
+    for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
         LD_LIBRARY_PATH=$(get_ldpath $i) inst_libs $i
     done
 }
@@ -438,7 +470,7 @@ create_empty_image() {
     [ -b "$LOOPDEV" ] || return 1
     echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
     sfdisk "$LOOPDEV" <<EOF
-,$((_size-10))M
+,$((_size-50))M
 ,
 EOF
 
@@ -453,8 +485,47 @@ EOF
     fi
 }
 
+check_asan_reports() {
+    local ret=0
+    local root="$1"
+
+    if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
+        ls -l "$root"
+        if [[ -e "$root/systemd.asan.log.1" ]]; then
+            cat "$root/systemd.asan.log.1"
+            ret=$(($ret+1))
+        fi
+
+        journald_report=$(find "$root" -name "systemd-journald.asan.log*" -exec cat {} \;)
+        if [[ ! -z "$journald_report" ]]; then
+            printf "%s" "$journald_report"
+            ret=$(($ret+1))
+        fi
+
+        pids=$(
+            "$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne '
+                 BEGIN {
+                     %services_to_ignore = (
+                         "dbus-daemon" => undef,
+                     );
+                 }
+                 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
+        )
+        if [[ ! -z "$pids" ]]; then
+            ret=$(($ret+1))
+            for pid in $pids; do
+                "$BUILD_DIR/journalctl" -D "$root/var/log/journal" _PID=$pid --no-pager
+            done
+        fi
+    fi
+
+    return $ret
+}
+
 check_result_nspawn() {
-    ret=1
+    local ret=1
+    local journald_report=""
+    local pids=""
     [[ -e $TESTDIR/$1/testok ]] && ret=0
     [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
     cp -a $TESTDIR/$1/var/log/journal $TESTDIR
@@ -462,17 +533,19 @@ check_result_nspawn() {
     ls -l $TESTDIR/journal/*/*.journal
     test -s $TESTDIR/failed && ret=$(($ret+1))
     [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
+    check_asan_reports "$TESTDIR/$1" || ret=$(($ret+1))
     return $ret
 }
 
 # can be overridden in specific test
 check_result_qemu() {
-    ret=1
+    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
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
     ls -l $TESTDIR/journal/*/*.journal
@@ -508,7 +581,9 @@ install_execs() {
     sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
          | sort -u | while read i; do
          # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
-         inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ]
+         # also, plymouth is pulled in by rescue.service, but even there the exit code
+         # is ignored; as it's not present on some distros, don't fail if it doesn't exist
+         inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
      done
     )
 }
@@ -580,6 +655,21 @@ install_basic_tools() {
 
 install_debug_tools() {
     [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
+
+    if [[ $INTERACTIVE_DEBUG ]]; then
+        # Set default TERM from vt220 to linux, so at least basic key shortcuts work
+        local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
+        mkdir -p "$_getty_override"
+        echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
+
+        cat > "$initdir/etc/motd" << EOF
+To adjust the terminal size use:
+    export COLUMNS=xx
+    export LINES=yy
+or
+    stty cols xx rows yy
+EOF
+    fi
 }
 
 install_libnss() {
@@ -590,7 +680,18 @@ install_libnss() {
 
 install_dbus() {
     inst $ROOTLIBDIR/system/dbus.socket
-    inst $ROOTLIBDIR/system/dbus.service
+
+    # Fedora rawhide replaced dbus.service with dbus-daemon.service
+    if [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+        inst $ROOTLIBDIR/system/dbus-daemon.service
+        # Alias symlink
+        inst_symlink /etc/systemd/system/dbus.service
+    else
+        inst $ROOTLIBDIR/system/dbus.service
+    fi
+    # Newer Fedora versions use dbus-broker by default. Let's install it is available.
+    [ -f /usr/bin/dbus-broker ] && inst /usr/bin/dbus-broker
+    [ -f /usr/bin/dbus-broker-launch ] && inst /usr/bin/dbus-broker-launch
 
     find \
         /etc/dbus-1 /usr/share/dbus-1 -xtype f \
@@ -623,12 +724,33 @@ install_pam() {
 }
 
 install_keymaps() {
+    # The first three paths may be deprecated.
+    # It seems now the last two paths are used by many distributions.
     for i in \
         /usr/lib/kbd/keymaps/include/* \
         /usr/lib/kbd/keymaps/i386/include/* \
-        /usr/lib/kbd/keymaps/i386/qwerty/us.*; do
+        /usr/lib/kbd/keymaps/i386/qwerty/us.* \
+        /usr/lib/kbd/keymaps/legacy/include/* \
+        /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
+            [[ -f $i ]] || continue
+            inst $i
+    done
+
+    # When it takes any argument, then install more keymaps.
+    if [[ -n $1 ]]; then
+        for i in \
+        /usr/lib/kbd/keymaps/i386/*/* \
+        /usr/lib/kbd/keymaps/legacy/i386/*/*; do
             [[ -f $i ]] || continue
             inst $i
+        done
+    fi
+}
+
+install_zoneinfo() {
+    for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
+        [[ -f $i ]] || continue
+        inst $i
     done
 }
 
@@ -654,7 +776,8 @@ setup_testsuite() {
 
     mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
     ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
-    ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
+    # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
+    [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
 
     # make the testsuite the default target
     ln -fs testsuite.target $initdir/etc/systemd/system/default.target
@@ -715,8 +838,13 @@ inst_libs() {
 
 import_testdir() {
     [[ -e $STATEFILE ]] && . $STATEFILE
-    if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then
-        TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
+    if [[ ! -d "$TESTDIR" ]]; then
+        if [[ -z "$TESTDIR" ]]; then
+            TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
+        else
+            mkdir -p "$TESTDIR"
+        fi
+
         echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
         export TESTDIR
     fi
@@ -761,7 +889,7 @@ _lvl2char() {
 # This enables:
 # dwarn "This is a warning"
 # echo "This is a warning" | dwarn
-LOG_LEVEL=4
+LOG_LEVEL=${LOG_LEVEL:-4}
 
 dlog() {
     [ -z "$LOG_LEVEL" ] && return 0
@@ -1497,7 +1625,7 @@ test_run() {
             else
                 dwarn "can't run systemd-nspawn, skipping"
             fi
-       fi
+        fi
     fi
     return 0
 }
@@ -1526,7 +1654,9 @@ do_test() {
         case $1 in
             --run)
                 echo "TEST RUN: $TEST_DESCRIPTION"
-                if test_run; then
+                test_run
+                ret=$?
+                if (( $ret == 0 )); then
                     echo "TEST RUN: $TEST_DESCRIPTION [OK]"
                 else
                     echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"