PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH
-LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || "$ID_LIKE" = "debian" ]] && echo yes)
-LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" ]] && echo yes)
+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)
KERNEL_VER=${KERNEL_VER-$(uname -r)}
KERNEL_MODS="/lib/modules/$KERNEL_VER/"
QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}"
NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}"
TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
-FSTYPE="${FSTYPE:-ext3}"
+[[ "$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)"
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
-BASICTOOLS="sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm"
+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"
+STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
+STATEFILE="$STATEDIR/.testdir"
+TESTLOG="$STATEDIR/test.log"
+
+is_built_with_asan() {
+ if ! type -P objdump >/dev/null; then
+ ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN."
+ return 1
+ 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)
+ if (( $_asan_calls < 1000 )); then
+ return 1
+ else
+ return 0
+ fi
+}
+
+IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no)
+
+if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
+ STRIP_BINARIES=no
+ SKIP_INITRD=yes
+ PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
+fi
+
function find_qemu_bin() {
- # SUSE and Red Hat call the binary qemu-kvm
- # Debian and Gentoo call it kvm
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
+ # 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
+ [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
+ fi
[ "$ARCH" ] || ARCH=$(uname -m)
case $ARCH in
run_qemu() {
if [ -f /etc/machine-id ]; then
read MACHINE_ID < /etc/machine-id
- [ -z "$INITRD" ] && [ -e "/boot/$MACHINE_ID/$KERNEL_VER/initrd" ] \
- && INITRD="/boot/$MACHINE_ID/$KERNEL_VER/initrd"
- [ -z "$KERNEL_BIN" ] && [ -e "/boot/$MACHINE_ID/$KERNEL_VER/linux" ] \
- && KERNEL_BIN="/boot/$MACHINE_ID/$KERNEL_VER/linux"
+ [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
+ && INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
+ [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
+ && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
fi
if [[ ! "$KERNEL_BIN" ]]; then
default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
default_arch_initrd=/boot/initramfs-linux.img
+ default_suse_initrd=/boot/initrd-${KERNEL_VER}
if [[ ! "$INITRD" ]]; then
if [[ -e "$default_fedora_initrd" ]]; then
INITRD="$default_fedora_initrd"
INITRD="$default_debian_initrd"
elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
INITRD="$default_arch_initrd"
+ elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
+ INITRD="$default_suse_initrd"
fi
fi
exit 1
fi
- KERNEL_APPEND="root=/dev/sda1 \
+if [[ "$LOOKS_LIKE_SUSE" ]]; then
+ PARAMS+="rd.hostonly=0"
+else
+ PARAMS+="ro"
+fi
+
+KERNEL_APPEND="$PARAMS \
+root=/dev/sda1 \
raid=noautodetect \
loglevel=2 \
-init=$ROOTLIBDIR/systemd \
-ro \
+init=$PATH_TO_INIT \
console=ttyS0 \
selinux=0 \
+printk.devkmsg=on \
$_cgroup_args \
$KERNEL_APPEND \
"
QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
fi
- if [ -c /dev/kvm ]; then
+ # 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
QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
fi
run_nspawn() {
[[ -d /run/systemd/system ]] || return 1
- local _nspawn_cmd="../../systemd-nspawn --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND"
+ local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $PATH_TO_INIT $KERNEL_APPEND"
if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
_nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
fi
strip_binaries
install_depmod_files
generate_module_dependencies
+ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
+ create_asan_wrapper
+ fi
}
setup_selinux() {
chmod 0755 $_valgrind_wrapper
}
+create_asan_wrapper() {
+ local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
+ ddebug "Create $_asan_wrapper"
+ cat >$_asan_wrapper <<EOF
+#!/bin/bash
+
+set -x
+
+DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
+DEFAULT_ENVIRONMENT=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS
+
+mount -t proc proc /proc
+mount -t sysfs sysfs /sys
+mount -o remount,rw /
+
+PATH_TO_ASAN=\$(find / -name '*libasan*' | sed 1q)
+if [[ "\$PATH_TO_ASAN" ]]; then
+ # A lot of services (most notably dbus) won't start without preloading libasan
+ # See https://github.com/systemd/systemd/issues/5004
+ DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
+fi
+echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/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/'
+
+# The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
+# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
+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"
+
+export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log
+exec $ROOTLIBDIR/systemd "\$@"
+EOF
+
+ chmod 0755 $_asan_wrapper
+}
+
create_strace_wrapper() {
local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
ddebug "Create $_strace_wrapper"
inst_libdir_file "libdevmapper-event.so*"
if [[ "$LOOKS_LIKE_DEBIAN" ]]; then
# dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
- # see https://anonscm.debian.org/cgit/pkg-lvm/lvm2.git/tree/debian/patches/0007-udev.patch
- inst_rules 55-dm.rules 60-persistent-storage-dm.rules
+ # and since buster/bionic 95-dm-notify.rules
+ # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
+ inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
else
inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
fi
install_systemd() {
# install compiled files
- (cd $TEST_BASE_DIR/..; set -x; make DESTDIR=$initdir install)
+ local _ninja_bin=$(type -P ninja || type -P ninja-build)
+ if [[ -z "$_ninja_bin" ]]; then
+ dfatal "ninja was not found"
+ exit 1
+ fi
+ (set -x; DESTDIR=$initdir "$_ninja_bin" -C $BUILD_DIR install)
# remove unneeded documentation
rm -fr $initdir/usr/share/{man,doc}
# we strip binaries since debug symbols increase binaries size a lot
# and it could fill the available space
strip_binaries
+ [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
+
# enable debug logging in PID1
echo LogLevel=debug >> $initdir/etc/systemd/system.conf
}
}
create_empty_image() {
+ local _size=500
+ if [[ "$STRIP_BINARIES" = "no" ]]; then
+ _size=$((2*_size))
+ 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=400
+ dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek="$_size"
LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
[ -b "$LOOPDEV" ] || return 1
echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
sfdisk "$LOOPDEV" <<EOF
-,390M
+,$((_size-10))M
,
EOF
+ udevadm settle
+
local _label="-L systemd"
# mkfs.reiserfs doesn't know -L. so, use --label instead
[[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd"
return $ret
}
+# can be overridden in specific test
+check_result_qemu() {
+ 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
+ umount $TESTDIR/root
+ [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
+ ls -l $TESTDIR/journal/*/*.journal
+ test -s $TESTDIR/failed && ret=$(($ret+1))
+ [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
+ return $ret
+}
+
strip_binaries() {
if [[ "$STRIP_BINARIES" = "no" ]]; then
ddebug "Don't strip binaries"
install_execs() {
ddebug "install any Execs from the service files"
(
- export PKG_CONFIG_PATH=$TEST_BASE_DIR/../src/core/
+ export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
egrep -ho '^Exec[^ ]*=[^ ]+' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
| while read i; do
- i=${i##Exec*=}; i=${i##-}
- inst $i
+ i=${i##Exec*=}; i=${i##[@+\!-]}; i=${i##\!}
+ # 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" ]
done
)
}
}
install_config_files() {
- inst /etc/sysconfig/init
+ inst /etc/sysconfig/init || true
inst /etc/passwd
inst /etc/shadow
inst /etc/login.defs
inst /etc/group
inst /etc/shells
inst /etc/nsswitch.conf
- inst /etc/pam.conf
- inst /etc/securetty
+ inst /etc/pam.conf || true
+ inst /etc/securetty || true
inst /etc/os-release
inst /etc/localtime
# we want an empty environment
# set the hostname
echo systemd-testsuite > $initdir/etc/hostname
# fstab
+ if [[ "$LOOKS_LIKE_SUSE" ]]; then
+ ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1"
+ else
+ ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1"
+ fi
+
cat >$initdir/etc/fstab <<EOF
-LABEL=systemd / ${FSTYPE} rw 0 1
+$ROOTMOUNT
EOF
}
install_pam() {
(
- [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null && find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
- find \
- /etc/pam.d \
- /etc/security \
- /lib64/security \
- /lib/security -xtype f \
+ if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
+ find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
+ else
+ find /lib*/security -xtype f
+ fi
+ find /etc/pam.d /etc/security -xtype f
) | while read file; do
inst $file
done
}
import_testdir() {
- STATEFILE=".testdir"
[[ -e $STATEFILE ]] && . $STATEFILE
if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then
TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
# Create additional symlinks. See rev_symlinks description.
for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
- [[ ! -e $initdir/$_symlink ]] && {
+ [[ -e $initdir/$_symlink ]] || {
ddebug "Creating extra symlink: $_symlink"
inst_symlink $_symlink
}
fi
done
[[ $_found ]] || dinfo "Skipping udev rule: $_rule"
+ _found=
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"
+}
+
+# can be overridden in specific test
+test_cleanup() {
+ umount $TESTDIR/root 2>/dev/null || true
+ [[ $LOOPDEV ]] && losetup -d $LOOPDEV || true
+ return 0
+}
+
+test_run() {
+ if [ -z "$TEST_NO_QEMU" ]; then
+ if run_qemu; then
+ check_result_qemu || return 1
+ else
+ dwarn "can't run QEMU, skipping"
+ fi
+ fi
+ if [ -z "$TEST_NO_NSPAWN" ]; then
+ if run_nspawn; then
+ check_result_nspawn || return 1
+ else
+ dwarn "can't run systemd-nspawn, skipping"
+ fi
+ fi
+ return 0
+}
+
do_test() {
if [[ $UID != "0" ]]; then
echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
[[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
done
+ mkdir -p "$STATEDIR"
+
import_testdir
import_initdir
case $1 in
--run)
echo "TEST RUN: $TEST_DESCRIPTION"
- test_run
- ret=$?
- if [ $ret -eq 0 ]; then
+ if test_run; then
echo "TEST RUN: $TEST_DESCRIPTION [OK]"
else
echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
--setup)
echo "TEST SETUP: $TEST_DESCRIPTION"
test_setup
- exit $?;;
+ ;;
--clean)
echo "TEST CLEANUP: $TEST_DESCRIPTION"
test_cleanup
rm -fr "$TESTDIR"
- rm -f .testdir
- exit $?;;
+ rm -f "$STATEFILE"
+ ;;
--all)
+ ret=0
echo -n "TEST: $TEST_DESCRIPTION ";
(
test_setup && test_run
ret=$?
test_cleanup
rm -fr "$TESTDIR"
- rm -f .testdir
+ rm -f "$STATEFILE"
exit $ret
- ) </dev/null >test.log 2>&1
- ret=$?
+ ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
if [ $ret -eq 0 ]; then
- rm test.log
+ rm "$TESTLOG"
echo "[OK]"
else
echo "[FAILED]"
- echo "see $(pwd)/test.log"
+ echo "see $TESTLOG"
fi
exit $ret;;
*) break ;;