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" ]] && 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 || 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}"
ROOTLIBDIR=/usr/lib/systemd
fi
-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"
+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
root=/dev/sda1 \
raid=noautodetect \
loglevel=2 \
-init=$ROOTLIBDIR/systemd \
+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="$BUILD_DIR/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/$1 $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_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1
+DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS:halt_on_error=1"
+
+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 UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
+exec $ROOTLIBDIR/systemd "\$@"
+EOF
+
+ chmod 0755 $_asan_wrapper
+}
+
create_strace_wrapper() {
local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
ddebug "Create $_strace_wrapper"
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
}
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"
}
check_result_nspawn() {
- ret=1
- [[ -e $TESTDIR/nspawn-root/testok ]] && ret=0
- [[ -f $TESTDIR/nspawn-root/failed ]] && cp -a $TESTDIR/nspawn-root/failed $TESTDIR
- cp -a $TESTDIR/nspawn-root/var/log/journal $TESTDIR
+ local ret=1
+ [[ -e $TESTDIR/$1/testok ]] && ret=0
+ [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
+ cp -a $TESTDIR/$1/var/log/journal $TESTDIR
[[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
ls -l $TESTDIR/journal/*/*.journal
test -s $TESTDIR/failed && ret=$(($ret+1))
# 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
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##[@+\!-]}; i=${i##\!}
+ 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" ]
done
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
find \
/etc/dbus-1 /usr/share/dbus-1 -xtype f \
}
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
}
install_fonts() {
cp -ar $initdir $TESTDIR/nspawn-root
# we don't mount in the nspawn root
rm -f $TESTDIR/nspawn-root/etc/fstab
+ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
+ cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
+ fi
}
setup_basic_dirs() {
# 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
fi
fi
if [ -z "$TEST_NO_NSPAWN" ]; then
- if run_nspawn; then
- check_result_nspawn || return 1
+ if run_nspawn "nspawn-root"; then
+ check_result_nspawn "nspawn-root" || return 1
else
dwarn "can't run systemd-nspawn, skipping"
fi
+
+ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
+ if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
+ check_result_nspawn "unprivileged-nspawn-root" || return 1
+ else
+ dwarn "can't run systemd-nspawn, skipping"
+ fi
+ fi
fi
return 0
}
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]"