]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-functions
nspawn: consistenly fail if parsing the environment fails
[thirdparty/systemd.git] / test / test-functions
CommitLineData
898720b7
HH
1#!/bin/bash
2# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
3# ex: ts=8 sw=4 sts=4 et filetype=sh
4PATH=/sbin:/bin:/usr/sbin:/usr/bin
5export PATH
6
65dd488f
DS
7LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && echo yes || :)
8LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && echo yes || :)
9LOOKS_LIKE_SUSE=$(source /etc/os-release && [[ " $ID_LIKE " = *" suse "* ]] && echo yes || :)
0d6e798a
HH
10KERNEL_VER=${KERNEL_VER-$(uname -r)}
11KERNEL_MODS="/lib/modules/$KERNEL_VER/"
91f9f8f1 12QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}"
43bbb8f0 13NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}"
b2ecd099 14TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
4b742c8a 15[[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
22f1f8f2 16UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
95cff2a7 17EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
3071b3ff 18QEMU_MEM="${QEMU_MEM:-512M}"
898720b7 19
501deda1
FS
20# Decide if we can (and want to) run QEMU with KVM acceleration.
21# Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
22# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
23# running under KVM. If these conditions are met, enable KVM (and possibly
24# nested KVM), otherwise disable it.
25if [[ -n "$TEST_NESTED_KVM" || ( -z "$TEST_NO_KVM" && $(systemd-detect-virt -v) != kvm ) ]]; then
26 QEMU_KVM=yes
27else
28 QEMU_KVM=no
29fi
30
3486cb6c
MP
31if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
32 echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
33 ROOTLIBDIR=/usr/lib/systemd
34fi
35
016fa3b9 36PATH_TO_INIT=$ROOTLIBDIR/systemd
84c49ad1
DS
37[ "$SYSTEMD_JOURNALD" ] || SYSTEMD_JOURNALD=$(which -a $BUILD_DIR/systemd-journald $ROOTLIBDIR/systemd-journald 2>/dev/null | grep '^/' -m1)
38[ "$SYSTEMD" ] || SYSTEMD=$(which -a $BUILD_DIR/systemd $ROOTLIBDIR/systemd 2>/dev/null | grep '^/' -m1)
39[ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1)
40[ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1)
016fa3b9 41
1e460470 42BASICTOOLS="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"
c81a46b9 43DEBUGTOOLS="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"
889a9042 44
22077c9c
MP
45STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
46STATEFILE="$STATEDIR/.testdir"
47TESTLOG="$STATEDIR/test.log"
48
ec9181d2
EV
49is_built_with_asan() {
50 if ! type -P objdump >/dev/null; then
51 ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN."
52 return 1
53 fi
54
55 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
84c49ad1 56 local _asan_calls=$(objdump -dC $SYSTEMD_JOURNALD | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
ec9181d2
EV
57 if (( $_asan_calls < 1000 )); then
58 return 1
59 else
60 return 0
61 fi
62}
63
64IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no)
65
66if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
67 STRIP_BINARIES=no
54a3790c 68 SKIP_INITRD="${SKIP_INITRD:-yes}"
016fa3b9 69 PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
038bf640 70 QEMU_MEM="2048M"
5b2172ee 71 QEMU_SMP=4
37ee8dc8
FS
72
73 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
84c49ad1 74 if ldd $SYSTEMD | grep -q libasan.so; then
37ee8dc8 75 ASAN_COMPILER=gcc
84c49ad1 76 elif ldd $SYSTEMD | grep -q libclang_rt.asan; then
37ee8dc8
FS
77 ASAN_COMPILER=clang
78
79 # As clang's ASan DSO is usually in a non-standard path, let's check if
80 # the environment is set accordingly. If not, warn the user and exit.
81 # We're not setting the LD_LIBRARY_PATH automagically here, because
82 # user should encounter (and fix) the same issue when running the unit
83 # tests (meson test)
84c49ad1
DS
84 if ldd "$SYSTEMD" | grep -q "libclang_rt.asan.*not found"; then
85 _asan_rt_name="$(ldd $SYSTEMD | awk '/libclang_rt.asan/ {print $1; exit}')"
37ee8dc8
FS
86 _asan_rt_path="$(find /usr/lib* /usr/local/lib* -type f -name "$_asan_rt_name" 2>/dev/null | sed 1q)"
87 echo >&2 "clang's ASan DSO ($_asan_rt_name) is not present in the runtime library path"
88 echo >&2 "Consider setting LD_LIBRARY_PATH=${_asan_rt_path%/*}"
89 exit 1
90 fi
91 else
92 echo >&2 "systemd is not linked against the ASan DSO"
93 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
94 exit 1
95 fi
ec9181d2
EV
96fi
97
c6a77179 98function find_qemu_bin() {
3e7aa2ed 99 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
501deda1 100 if [[ $QEMU_KVM == "yes" ]]; then
3e7aa2ed
LP
101 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
102 fi
c6a77179
RC
103
104 [ "$ARCH" ] || ARCH=$(uname -m)
105 case $ARCH in
106 x86_64)
107 # QEMU's own build system calls it qemu-system-x86_64
108 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-x86_64 2>/dev/null | grep '^/' -m1)
109 ;;
110 i*86)
111 # new i386 version of QEMU
112 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-i386 2>/dev/null | grep '^/' -m1)
113
114 # i386 version of QEMU
115 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu 2>/dev/null | grep '^/' -m1)
116 ;;
cf5f9bb8 117 ppc64*)
f364a17d 118 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-ppc64 2>/dev/null | grep '^/' -m1)
cf5f9bb8 119 ;;
c6a77179
RC
120 esac
121
122 if [ ! -e "$QEMU_BIN" ]; then
123 echo "Could not find a suitable QEMU binary" >&2
124 return 1
125 fi
126}
127
b2ecd099
MP
128# Return 0 if QEMU did run (then you must check the result state/logs for actual
129# success), or 1 if QEMU is not available.
889a9042 130run_qemu() {
b6f0c419
HH
131 if [ -f /etc/machine-id ]; then
132 read MACHINE_ID < /etc/machine-id
906bbac4
ZJS
133 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
134 && INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
135 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
136 && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
b6f0c419
HH
137 fi
138
9a2e265b
DJL
139 CONSOLE=ttyS0
140
e1a27318
EV
141 if [[ ! "$KERNEL_BIN" ]]; then
142 if [[ "$LOOKS_LIKE_ARCH" ]]; then
143 KERNEL_BIN=/boot/vmlinuz-linux
144 else
eaa602cb
DJL
145 [ "$ARCH" ] || ARCH=$(uname -m)
146 case $ARCH in
147 ppc64*)
148 KERNEL_BIN=/boot/vmlinux-$KERNEL_VER
9a2e265b 149 CONSOLE=hvc0
eaa602cb
DJL
150 ;;
151 *)
152 KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
153 ;;
154 esac
e1a27318
EV
155 fi
156 fi
157
61fea35e
EV
158 default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
159 default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
19632f6d 160 default_arch_initrd=/boot/initramfs-linux-fallback.img
85393d8f 161 default_suse_initrd=/boot/initrd-${KERNEL_VER}
e1a27318
EV
162 if [[ ! "$INITRD" ]]; then
163 if [[ -e "$default_fedora_initrd" ]]; then
164 INITRD="$default_fedora_initrd"
165 elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then
166 INITRD="$default_debian_initrd"
167 elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
168 INITRD="$default_arch_initrd"
85393d8f
TB
169 elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
170 INITRD="$default_suse_initrd"
e1a27318
EV
171 fi
172 fi
173
5bfb2a93
FS
174 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
175 # i.e. use the number of online CPUs on the host machine. If the nproc utility
176 # is not installed or there's some other error when calling it, fall back
177 # to the original value (QEMU_SMP=1).
178 if ! [ "$QEMU_SMP" ]; then
179 if ! QEMU_SMP=$(nproc); then
180 dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
181 QEMU_SMP=1
182 fi
183 fi
c6a77179
RC
184
185 find_qemu_bin || return 1
186
22f1f8f2
EV
187 local _cgroup_args
188 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
189 _cgroup_args="systemd.unified_cgroup_hierarchy=yes"
190 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
191 _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=yes"
192 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
193 _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=no"
194 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
195 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
196 exit 1
197 fi
198
cc5549ca
ZJS
199 if [[ "$LOOKS_LIKE_SUSE" ]]; then
200 PARAMS+="rd.hostonly=0"
201 elif [[ "$LOOKS_LIKE_ARCH" ]]; then
202 PARAMS+="rw"
203 else
204 PARAMS+="ro"
205 fi
85393d8f 206
cc5549ca 207 KERNEL_APPEND="$PARAMS \
85393d8f 208root=/dev/sda1 \
c6a77179
RC
209raid=noautodetect \
210loglevel=2 \
016fa3b9 211init=$PATH_TO_INIT \
9a2e265b 212console=$CONSOLE \
c6a77179 213selinux=0 \
b2ad25d3 214printk.devkmsg=on \
22f1f8f2 215$_cgroup_args \
c6a77179
RC
216$KERNEL_APPEND \
217"
218
dbf43a42 219 QEMU_OPTIONS="-smp $QEMU_SMP \
c6a77179 220-net none \
3071b3ff 221-m $QEMU_MEM \
c6a77179
RC
222-nographic \
223-kernel $KERNEL_BIN \
5b23cef0 224-drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
761ce2a4 225$QEMU_OPTIONS \
c6a77179
RC
226"
227
91f9f8f1 228 if [[ "$INITRD" && "$SKIP_INITRD" != "yes" ]]; then
c6a77179
RC
229 QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
230 fi
231
501deda1
FS
232 # Let's use KVM if possible
233 if [[ -c /dev/kvm && $QEMU_KVM == "yes" ]]; then
dbf43a42
DM
234 QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
235 fi
236
91f9f8f1
EV
237 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
238 QEMU_BIN="timeout --foreground $QEMU_TIMEOUT $QEMU_BIN"
239 fi
b2ecd099
MP
240 (set -x; $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND")
241 rc=$?
242 if [ "$rc" = 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
243 derror "test timed out after $QEMU_TIMEOUT s"
244 TIMED_OUT=1
245 else
246 [ "$rc" != 0 ] && derror "QEMU failed with exit code $rc"
247 fi
248 return 0
889a9042
RC
249}
250
7cad32bb
MP
251# Return 0 if nspawn did run (then you must check the result state/logs for actual
252# success), or 1 if nspawn is not available.
889a9042 253run_nspawn() {
7cad32bb
MP
254 [[ -d /run/systemd/system ]] || return 1
255
84c49ad1 256 local _nspawn_cmd="$SYSTEMD_NSPAWN $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
43bbb8f0
EV
257 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
258 _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
259 fi
856ca72b 260
22f1f8f2
EV
261 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
262 dwarn "nspawn doesn't support UNIFIED_CGROUP_HIERARCHY=hybrid, skipping"
263 exit
264 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
265 _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
266 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
267 _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
268 else
269 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
270 exit 1
271 fi
856ca72b 272
b2ecd099
MP
273 (set -x; $_nspawn_cmd)
274 rc=$?
275 if [ "$rc" = 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
276 derror "test timed out after $NSPAWN_TIMEOUT s"
277 TIMED_OUT=1
278 else
279 [ "$rc" != 0 ] && derror "nspawn failed with exit code $rc"
280 fi
281 return 0
889a9042
RC
282}
283
284setup_basic_environment() {
285 # create the basic filesystem layout
286 setup_basic_dirs
287
288 install_systemd
289 install_missing_libraries
290 install_config_files
291 create_rc_local
292 install_basic_tools
293 install_libnss
294 install_pam
295 install_dbus
296 install_fonts
297 install_keymaps
298 install_terminfo
299 install_execs
9974ff63 300 install_fsck
889a9042
RC
301 install_plymouth
302 install_debug_tools
303 install_ld_so_conf
e3ce42e7 304 setup_selinux
889a9042
RC
305 strip_binaries
306 install_depmod_files
307 generate_module_dependencies
ec9181d2 308 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
37ee8dc8 309 create_asan_wrapper
ec9181d2 310 fi
889a9042
RC
311}
312
e3ce42e7
EV
313setup_selinux() {
314 # don't forget KERNEL_APPEND='... selinux=1 ...'
315 if [[ "$SETUP_SELINUX" != "yes" ]]; then
316 ddebug "Don't setup SELinux"
317 return 0
318 fi
319 ddebug "Setup SELinux"
320 local _conf_dir=/etc/selinux
321 local _fixfiles_tools="bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles"
322
323 rm -rf $initdir/$_conf_dir
324 if ! cp -ar $_conf_dir $initdir/$_conf_dir; then
325 dfatal "Failed to copy $_conf_dir"
326 exit 1
327 fi
328
329 cat <<EOF >$initdir/etc/systemd/system/autorelabel.service
330[Unit]
331Description=Relabel all filesystems
332DefaultDependencies=no
333Requires=local-fs.target
334Conflicts=shutdown.target
335After=local-fs.target
336Before=sysinit.target shutdown.target
337ConditionSecurity=selinux
338ConditionPathExists=|/.autorelabel
339
340[Service]
341ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot'
342Type=oneshot
343TimeoutSec=0
344RemainAfterExit=yes
345EOF
346
347 touch $initdir/.autorelabel
348 mkdir -p $initdir/etc/systemd/system/basic.target.wants
349 ln -fs autorelabel.service $initdir/etc/systemd/system/basic.target.wants/autorelabel.service
350
351 dracut_install $_fixfiles_tools
352 dracut_install fixfiles
353 dracut_install sestatus
354}
355
a2fbff31
EV
356install_valgrind() {
357 if ! type -p valgrind; then
358 dfatal "Failed to install valgrind"
359 exit 1
360 fi
361
362 local _valgrind_bins=$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
363 dracut_install $_valgrind_bins
364
365 local _valgrind_libs=$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
366 dracut_install $_valgrind_libs
367
368 local _valgrind_dbg_and_supp=$(
369 strace -e open valgrind /bin/true 2>&1 >/dev/null |
370 perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
371 )
372 dracut_install $_valgrind_dbg_and_supp
373}
374
cb2f9d3f
EV
375create_valgrind_wrapper() {
376 local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
377 ddebug "Create $_valgrind_wrapper"
378 cat >$_valgrind_wrapper <<EOF
379#!/bin/bash
380
23cabb68 381mount -t proc proc /proc
cb2f9d3f
EV
382exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
383EOF
384 chmod 0755 $_valgrind_wrapper
385}
386
1786fae3
EV
387create_asan_wrapper() {
388 local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
37ee8dc8 389 local _asan_rt_pattern
1786fae3 390 ddebug "Create $_asan_wrapper"
37ee8dc8
FS
391
392 case "$ASAN_COMPILER" in
393 gcc)
394 _asan_rt_pattern="*libasan*"
395 ;;
396 clang)
397 _asan_rt_pattern="libclang_rt.asan-*"
398 # Install llvm-symbolizer to generate useful reports
399 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
400 dracut_install "llvm-symbolizer"
401 ;;
402 *)
403 dfail "Unsupported compiler: $ASAN_COMPILER"
404 exit 1
405 esac
406
1786fae3
EV
407 cat >$_asan_wrapper <<EOF
408#!/bin/bash
409
410set -x
411
f5f8cc7a
FS
412DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
413DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
a19f909b 414DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
1786fae3 415
37ee8dc8
FS
416# As right now bash is the PID 1, we can't expect PATH to have a sane value.
417# Let's make one to prevent unexpected "<bin> not found" issues in the future
418export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
419
1786fae3
EV
420mount -t proc proc /proc
421mount -t sysfs sysfs /sys
422mount -o remount,rw /
423
37ee8dc8 424PATH_TO_ASAN=\$(find / -name '$_asan_rt_pattern' | sed 1q)
1786fae3
EV
425if [[ "\$PATH_TO_ASAN" ]]; then
426 # A lot of services (most notably dbus) won't start without preloading libasan
427 # See https://github.com/systemd/systemd/issues/5004
428 DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
37ee8dc8
FS
429 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
430 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
431 # runtime ASan DSO is in a non-standard (library) path.
432 echo \${PATH_TO_ASAN%/*} > /etc/ld.so.conf.d/asan-path-override.conf
433 ldconfig
1786fae3
EV
434fi
435echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
9688fccc 436echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
37ee8dc8 437echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
1786fae3
EV
438
439# ASAN and syscall filters aren't compatible with each other.
440find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
441
88ed0f26
EV
442# The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
443# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
444JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
445mkdir -p "\$JOURNALD_CONF_DIR"
abf9b52c 446printf "[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"
88ed0f26 447
6141c6c9
EV
448# Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
449# Let's try to catch them by redirecting stderr (and stdout just in case) to a file
450# See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
451printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf"
452
082bcdca
EV
453# 90s isn't enough for some services to finish when literally everything is run
454# under ASan+UBSan in containers, which, in turn, are run in VMs.
d7283fc1 455# Let's limit which environments such services should be executed in.
082bcdca 456mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
d7283fc1 457printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
082bcdca 458
a5372344
EV
459# Let's override another hard-coded timeout that kicks in too early
460mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
461printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf
462
aaef1ed2
FS
463# The 'mount' utility doesn't behave well under libasan, causing unexpected
464# fails during boot and subsequent test results check:
465# bash-5.0# mount -o remount,rw -v /
466# mount: /dev/sda1 mounted on /.
467# bash-5.0# echo \$?
468# 1
469# Let's workaround this by clearing the previously set LD_PRELOAD env variable,
470# so the libasan library is not loaded for this particular service
471REMOUNTFS_CONF_DIR=/etc/systemd/system/systemd-remount-fs.service.d
472mkdir -p "\$REMOUNTFS_CONF_DIR"
473printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$REMOUNTFS_CONF_DIR/env.conf"
474
c1342d55 475export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
1786fae3
EV
476exec $ROOTLIBDIR/systemd "\$@"
477EOF
478
479 chmod 0755 $_asan_wrapper
480}
481
45dbd7b6
EV
482create_strace_wrapper() {
483 local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
484 ddebug "Create $_strace_wrapper"
485 cat >$_strace_wrapper <<EOF
486#!/bin/bash
487
488exec strace -D -o /strace.out $ROOTLIBDIR/systemd "\$@"
489EOF
490 chmod 0755 $_strace_wrapper
491}
492
9974ff63
EV
493install_fsck() {
494 dracut_install /sbin/fsck*
495 dracut_install -o /bin/fsck*
331fb4ca
EV
496
497 # fskc.reiserfs calls reiserfsck. so, install it
498 dracut_install -o reiserfsck
9974ff63
EV
499}
500
889a9042
RC
501install_dmevent() {
502 instmods dm_crypt =crypto
59279e96 503 inst_binary dmeventd
ac289ce3
EV
504 if [[ "$LOOKS_LIKE_DEBIAN" ]]; then
505 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
9c869ff6
DJL
506 # and since buster/bionic 95-dm-notify.rules
507 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
508 inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
ac289ce3
EV
509 else
510 inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
511 fi
889a9042
RC
512}
513
514install_systemd() {
515 # install compiled files
ca992ecf
EV
516 local _ninja_bin=$(type -P ninja || type -P ninja-build)
517 if [[ -z "$_ninja_bin" ]]; then
518 dfatal "ninja was not found"
519 exit 1
520 fi
521 (set -x; DESTDIR=$initdir "$_ninja_bin" -C $BUILD_DIR install)
889a9042 522 # remove unneeded documentation
23756070 523 rm -fr $initdir/usr/share/{man,doc}
889a9042
RC
524 # we strip binaries since debug symbols increase binaries size a lot
525 # and it could fill the available space
526 strip_binaries
61b480b6 527
85393d8f
TB
528 [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
529
61b480b6
ZJS
530 # enable debug logging in PID1
531 echo LogLevel=debug >> $initdir/etc/systemd/system.conf
215bffe1
FS
532 # store coredumps in journal
533 echo Storage=journal >> $initdir/etc/systemd/coredump.conf
889a9042
RC
534}
535
d7a4278d
FS
536get_ldpath() {
537 local _bin="$1"
538 objdump -p "$_bin" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :
539}
540
889a9042
RC
541install_missing_libraries() {
542 # install possible missing libraries
e3d3dada 543 for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
37ee8dc8 544 LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
889a9042
RC
545 done
546}
547
548create_empty_image() {
28c7474e
EV
549 local _size=500
550 if [[ "$STRIP_BINARIES" = "no" ]]; then
37ee8dc8 551 _size=$((4*_size))
28c7474e 552 fi
739d81dd 553 rm -f "$TESTDIR/rootdisk.img"
889a9042 554 # Create the blank file to use as a root filesystem
adcc450f 555 truncate -s "${_size}M" "$TESTDIR/rootdisk.img"
889a9042 556 LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
739d81dd 557 [ -b "$LOOPDEV" ] || return 1
889a9042 558 echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
edbced8a 559 sfdisk "$LOOPDEV" <<EOF
32983312 560,$((_size-50))M
889a9042
RC
561,
562EOF
563
053edc5b
EV
564 udevadm settle
565
331fb4ca
EV
566 local _label="-L systemd"
567 # mkfs.reiserfs doesn't know -L. so, use --label instead
568 [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd"
569 if ! mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; then
570 dfatal "Failed to mkfs -t ${FSTYPE}"
571 exit 1
572 fi
889a9042
RC
573}
574
ec4cab49
DS
575create_empty_image_rootdir() {
576 create_empty_image
0036ce06
DS
577 mkdir -p $initdir
578 mount ${LOOPDEV}p1 $initdir
ec4cab49
DS
579 TEST_SETUP_CLEANUP_ROOTDIR=1
580}
581
0d6e61d6
EV
582check_asan_reports() {
583 local ret=0
584 local root="$1"
585
7e11a95e 586 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
0d6e61d6
EV
587 ls -l "$root"
588 if [[ -e "$root/systemd.asan.log.1" ]]; then
589 cat "$root/systemd.asan.log.1"
7e11a95e
EV
590 ret=$(($ret+1))
591 fi
998445fd 592
abf9b52c 593 journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
d56db495 594 if [[ ! -z "$journald_report" ]]; then
6141c6c9 595 printf "%s\n" "$journald_report"
65dd488f 596 cat "$root/systemd-journald.out" || :
998445fd 597 ret=$(($ret+1))
d56db495 598 fi
ed4f303f 599
d56db495 600 pids=$(
84c49ad1 601 "$JOURNALCTL" -D "$root/var/log/journal" | perl -alne '
d56db495
EV
602 BEGIN {
603 %services_to_ignore = (
604 "dbus-daemon" => undef,
605 );
606 }
6d67286f 607 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
d56db495
EV
608 )
609 if [[ ! -z "$pids" ]]; then
ed4f303f
EV
610 ret=$(($ret+1))
611 for pid in $pids; do
84c49ad1 612 "$JOURNALCTL" -D "$root/var/log/journal" _PID=$pid --no-pager
ed4f303f 613 done
d56db495 614 fi
7e11a95e
EV
615 fi
616
889a9042
RC
617 return $ret
618}
619
0d6e61d6
EV
620check_result_nspawn() {
621 local ret=1
622 local journald_report=""
623 local pids=""
624 [[ -e $TESTDIR/$1/testok ]] && ret=0
625 [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
626 cp -a $TESTDIR/$1/var/log/journal $TESTDIR
627 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
628 ls -l $TESTDIR/journal/*/*.journal
629 test -s $TESTDIR/failed && ret=$(($ret+1))
630 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
631 check_asan_reports "$TESTDIR/$1" || ret=$(($ret+1))
632 return $ret
633}
634
054ee249
MP
635# can be overridden in specific test
636check_result_qemu() {
0013fac2 637 local ret=1
0036ce06
DS
638 mkdir -p $initdir
639 mount ${LOOPDEV}p1 $initdir
640 [[ -e $initdir/testok ]] && ret=0
641 [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
642 cp -a $initdir/var/log/journal $TESTDIR
643 check_asan_reports "$initdir" || ret=$(($ret+1))
644 umount $initdir
054ee249
MP
645 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
646 ls -l $TESTDIR/journal/*/*.journal
647 test -s $TESTDIR/failed && ret=$(($ret+1))
648 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
649 return $ret
650}
651
889a9042 652strip_binaries() {
5a613464
EV
653 if [[ "$STRIP_BINARIES" = "no" ]]; then
654 ddebug "Don't strip binaries"
655 return 0
656 fi
889a9042 657 ddebug "Strip binaries"
408c9a07
ZJS
658 find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
659 xargs strip --strip-unneeded |& \
d391ee10 660 grep -vi 'file format not recognized' | \
408c9a07 661 ddebug
889a9042
RC
662}
663
664create_rc_local() {
665 mkdir -p $initdir/etc/rc.d
666 cat >$initdir/etc/rc.d/rc.local <<EOF
667#!/bin/bash
668exit 0
669EOF
670 chmod 0755 $initdir/etc/rc.d/rc.local
671}
672
673install_execs() {
c7eda013
EV
674 ddebug "install any Execs from the service files"
675 (
209f4b9e 676 export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
c7eda013
EV
677 systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
678 systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
fe4bd4e5 679 sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
e180bdb5 680 | sort -u | while read i; do
818567fc 681 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
e4e039bc
FS
682 # also, plymouth is pulled in by rescue.service, but even there the exit code
683 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
71a0de36 684 dinfo "Attempting to install $i"
e4e039bc 685 inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
c7eda013
EV
686 done
687 )
889a9042
RC
688}
689
690generate_module_dependencies() {
691 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
692 ! depmod -a -b "$initdir" $KERNEL_VER; then
693 dfatal "\"depmod -a $KERNEL_VER\" failed."
694 exit 1
695 fi
696}
697
698install_depmod_files() {
699 inst /lib/modules/$KERNEL_VER/modules.order
700 inst /lib/modules/$KERNEL_VER/modules.builtin
701}
702
703install_plymouth() {
704 # install plymouth, if found... else remove plymouth service files
705 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
706 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
707 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
708 # dracut_install plymouth plymouthd
709 # else
710 rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
711 # fi
712}
713
714install_ld_so_conf() {
715 cp -a /etc/ld.so.conf* $initdir/etc
716 ldconfig -r "$initdir"
717}
718
719install_config_files() {
65dd488f 720 inst /etc/sysconfig/init || :
889a9042
RC
721 inst /etc/passwd
722 inst /etc/shadow
bf3a947c 723 inst /etc/login.defs
889a9042
RC
724 inst /etc/group
725 inst /etc/shells
726 inst /etc/nsswitch.conf
65dd488f
DS
727 inst /etc/pam.conf || :
728 inst /etc/securetty || :
889a9042
RC
729 inst /etc/os-release
730 inst /etc/localtime
731 # we want an empty environment
732 > $initdir/etc/environment
733 > $initdir/etc/machine-id
734 # set the hostname
735 echo systemd-testsuite > $initdir/etc/hostname
736 # fstab
85393d8f
TB
737 if [[ "$LOOKS_LIKE_SUSE" ]]; then
738 ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1"
739 else
740 ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1"
741 fi
742
889a9042 743 cat >$initdir/etc/fstab <<EOF
85393d8f 744$ROOTMOUNT
889a9042
RC
745EOF
746}
747
748install_basic_tools() {
749 [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
4be4833e 750 dracut_install -o sushell
7d023341
MP
751 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
752 dracut_install -o ldconfig.real
889a9042
RC
753}
754
755install_debug_tools() {
756 [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
c81a46b9
FS
757
758 if [[ $INTERACTIVE_DEBUG ]]; then
759 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
760 local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
761 mkdir -p "$_getty_override"
762 echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
763
764 cat > "$initdir/etc/motd" << EOF
765To adjust the terminal size use:
766 export COLUMNS=xx
767 export LINES=yy
768or
769 stty cols xx rows yy
770EOF
771 fi
889a9042
RC
772}
773
774install_libnss() {
775 # install libnss_files for login
cffae62b 776 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
99877b7e 777 dracut_install $NSS_LIBS
889a9042
RC
778}
779
780install_dbus() {
3486cb6c 781 inst $ROOTLIBDIR/system/dbus.socket
a978c9f2 782
908665f4
LP
783 # Newer Fedora versions use dbus-broker by default. Let's install it is available.
784 if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
785 inst $ROOTLIBDIR/system/dbus-broker.service
786 inst_symlink /etc/systemd/system/dbus.service
787 inst /usr/bin/dbus-broker
788 inst /usr/bin/dbus-broker-launch
789 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
790 # Fedora rawhide replaced dbus.service with dbus-daemon.service
a978c9f2
FS
791 inst $ROOTLIBDIR/system/dbus-daemon.service
792 # Alias symlink
793 inst_symlink /etc/systemd/system/dbus.service
794 else
795 inst $ROOTLIBDIR/system/dbus.service
796 fi
889a9042
RC
797
798 find \
e63b61be 799 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
889a9042
RC
800 | while read file; do
801 inst $file
802 done
803}
804
805install_pam() {
0fe15dc8 806 (
818567fc
MP
807 if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
808 find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
809 else
810 find /lib*/security -xtype f
811 fi
812 find /etc/pam.d /etc/security -xtype f
0fe15dc8 813 ) | while read file; do
889a9042
RC
814 inst $file
815 done
417491f1 816
d5172c79
EV
817 # pam_unix depends on unix_chkpwd.
818 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
819 dracut_install -o unix_chkpwd
820
417491f1
EV
821 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
822 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
e14b866b
ZJS
823
824 # set empty root password for easy debugging
825 sed -i 's/^root:x:/root::/' $initdir/etc/passwd
889a9042
RC
826}
827
828install_keymaps() {
83a7051e
YW
829 # The first three paths may be deprecated.
830 # It seems now the last two paths are used by many distributions.
889a9042
RC
831 for i in \
832 /usr/lib/kbd/keymaps/include/* \
833 /usr/lib/kbd/keymaps/i386/include/* \
83a7051e
YW
834 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
835 /usr/lib/kbd/keymaps/legacy/include/* \
836 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
889a9042
RC
837 [[ -f $i ]] || continue
838 inst $i
839 done
ad931fee
YW
840
841 # When it takes any argument, then install more keymaps.
842 if [[ -n $1 ]]; then
843 for i in \
844 /usr/lib/kbd/keymaps/i386/*/* \
845 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
846 [[ -f $i ]] || continue
847 inst $i
848 done
849 fi
889a9042
RC
850}
851
7d10ec1c
YW
852install_zoneinfo() {
853 for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
854 [[ -f $i ]] || continue
855 inst $i
856 done
857}
858
889a9042
RC
859install_fonts() {
860 for i in \
25b47f96 861 /usr/lib/kbd/consolefonts/eurlatgr* \
889a9042
RC
862 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
863 [[ -f $i ]] || continue
864 inst $i
865 done
866}
867
868install_terminfo() {
869 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
870 [ -f ${_terminfodir}/l/linux ] && break
871 done
872 dracut_install -o ${_terminfodir}/l/linux
873}
874
875setup_testsuite() {
53d90f95 876 cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
5c404f1a 877 cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
889a9042
RC
878
879 mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
880 ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
c81a46b9
FS
881 # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
882 [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
889a9042
RC
883
884 # make the testsuite the default target
885 ln -fs testsuite.target $initdir/etc/systemd/system/default.target
886}
887
888setup_nspawn_root() {
889 rm -fr $TESTDIR/nspawn-root
890 ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
891 cp -ar $initdir $TESTDIR/nspawn-root
892 # we don't mount in the nspawn root
893 rm -f $TESTDIR/nspawn-root/etc/fstab
746fbd9c
EV
894 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
895 cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
896 fi
889a9042
RC
897}
898
0d6e798a 899setup_basic_dirs() {
889a9042
RC
900 mkdir -p $initdir/run
901 mkdir -p $initdir/etc/systemd/system
902 mkdir -p $initdir/var/log/journal
903
0d6e798a 904 for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log dev proc sys sysroot root run run/lock run/initramfs; do
898720b7
HH
905 if [ -L "/$d" ]; then
906 inst_symlink "/$d"
907 else
0d6e798a 908 inst_dir "/$d"
898720b7
HH
909 fi
910 done
911
912 ln -sfn /run "$initdir/var/run"
913 ln -sfn /run/lock "$initdir/var/lock"
914}
915
916inst_libs() {
917 local _bin=$1
918 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
919 local _file _line
920
921 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
922 [[ $_line = 'not a dynamic executable' ]] && break
923
924 if [[ $_line =~ $_so_regex ]]; then
925 _file=${BASH_REMATCH[1]}
926 [[ -e ${initdir}/$_file ]] && continue
927 inst_library "$_file"
928 continue
929 fi
930
931 if [[ $_line =~ not\ found ]]; then
932 dfatal "Missing a shared library required by $_bin."
933 dfatal "Run \"ldd $_bin\" to find out what it is."
934 dfatal "$_line"
935 dfatal "dracut cannot create an initrd."
936 exit 1
937 fi
938 done
939}
940
941import_testdir() {
898720b7 942 [[ -e $STATEFILE ]] && . $STATEFILE
3f50fff5
FS
943 if [[ ! -d "$TESTDIR" ]]; then
944 if [[ -z "$TESTDIR" ]]; then
945 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
946 else
947 mkdir -p "$TESTDIR"
948 fi
949
898720b7
HH
950 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
951 export TESTDIR
952 fi
953}
954
889a9042
RC
955import_initdir() {
956 initdir=$TESTDIR/root
8c03df36 957 mkdir -p $initdir
889a9042
RC
958 export initdir
959}
960
898720b7
HH
961## @brief Converts numeric logging level to the first letter of level name.
962#
963# @param lvl Numeric logging level in range from 1 to 6.
964# @retval 1 if @a lvl is out of range.
965# @retval 0 if @a lvl is correct.
966# @result Echoes first letter of level name.
967_lvl2char() {
968 case "$1" in
969 1) echo F;;
970 2) echo E;;
971 3) echo W;;
972 4) echo I;;
973 5) echo D;;
974 6) echo T;;
975 *) return 1;;
976 esac
977}
978
979## @brief Internal helper function for _do_dlog()
980#
981# @param lvl Numeric logging level.
982# @param msg Message.
983# @retval 0 It's always returned, even if logging failed.
984#
985# @note This function is not supposed to be called manually. Please use
986# dtrace(), ddebug(), or others instead which wrap this one.
987#
988# This function calls _do_dlog() either with parameter msg, or if
989# none is given, it will read standard input and will use every line as
990# a message.
991#
992# This enables:
993# dwarn "This is a warning"
994# echo "This is a warning" | dwarn
20fc56c0 995LOG_LEVEL=${LOG_LEVEL:-4}
898720b7
HH
996
997dlog() {
998 [ -z "$LOG_LEVEL" ] && return 0
999 [ $1 -le $LOG_LEVEL ] || return 0
1000 local lvl="$1"; shift
1001 local lvlc=$(_lvl2char "$lvl") || return 0
1002
1003 if [ $# -ge 1 ]; then
1004 echo "$lvlc: $*"
1005 else
1006 while read line; do
1007 echo "$lvlc: " "$line"
1008 done
1009 fi
1010}
1011
1012## @brief Logs message at TRACE level (6)
1013#
1014# @param msg Message.
1015# @retval 0 It's always returned, even if logging failed.
1016dtrace() {
1017 set +x
1018 dlog 6 "$@"
1019 [ -n "$debug" ] && set -x || :
1020}
1021
1022## @brief Logs message at DEBUG level (5)
1023#
1024# @param msg Message.
1025# @retval 0 It's always returned, even if logging failed.
1026ddebug() {
0d6e798a 1027# set +x
898720b7 1028 dlog 5 "$@"
0d6e798a 1029# [ -n "$debug" ] && set -x || :
898720b7
HH
1030}
1031
1032## @brief Logs message at INFO level (4)
1033#
1034# @param msg Message.
1035# @retval 0 It's always returned, even if logging failed.
1036dinfo() {
1037 set +x
1038 dlog 4 "$@"
1039 [ -n "$debug" ] && set -x || :
1040}
1041
1042## @brief Logs message at WARN level (3)
1043#
1044# @param msg Message.
1045# @retval 0 It's always returned, even if logging failed.
1046dwarn() {
1047 set +x
1048 dlog 3 "$@"
1049 [ -n "$debug" ] && set -x || :
1050}
1051
1052## @brief Logs message at ERROR level (2)
1053#
1054# @param msg Message.
1055# @retval 0 It's always returned, even if logging failed.
1056derror() {
0d6e798a 1057# set +x
898720b7 1058 dlog 2 "$@"
0d6e798a 1059# [ -n "$debug" ] && set -x || :
898720b7
HH
1060}
1061
1062## @brief Logs message at FATAL level (1)
1063#
1064# @param msg Message.
1065# @retval 0 It's always returned, even if logging failed.
1066dfatal() {
1067 set +x
1068 dlog 1 "$@"
1069 [ -n "$debug" ] && set -x || :
1070}
1071
1072
1073# Generic substring function. If $2 is in $1, return 0.
1074strstr() { [ "${1#*$2*}" != "$1" ]; }
1075
1076# normalize_path <path>
1077# Prints the normalized path, where it removes any duplicated
1078# and trailing slashes.
1079# Example:
1080# $ normalize_path ///test/test//
1081# /test/test
1082normalize_path() {
1083 shopt -q -s extglob
1084 set -- "${1//+(\/)//}"
1085 shopt -q -u extglob
1086 echo "${1%/}"
1087}
1088
1089# convert_abs_rel <from> <to>
1090# Prints the relative path, when creating a symlink to <to> from <from>.
1091# Example:
1092# $ convert_abs_rel /usr/bin/test /bin/test-2
1093# ../../bin/test-2
1094# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
1095convert_abs_rel() {
1096 local __current __absolute __abssize __cursize __newpath
1097 local -i __i __level
1098
1099 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
1100
1101 # corner case #1 - self looping link
1102 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
1103
1104 # corner case #2 - own dir link
1105 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
1106
1107 IFS="/" __current=($1)
1108 IFS="/" __absolute=($2)
1109
1110 __abssize=${#__absolute[@]}
1111 __cursize=${#__current[@]}
1112
1113 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
1114 do
1115 (( __level++ ))
1116 if (( __level > __abssize || __level > __cursize ))
1117 then
1118 break
1119 fi
1120 done
1121
1122 for ((__i = __level; __i < __cursize-1; __i++))
1123 do
1124 if ((__i > __level))
1125 then
1126 __newpath=$__newpath"/"
1127 fi
1128 __newpath=$__newpath".."
1129 done
1130
1131 for ((__i = __level; __i < __abssize; __i++))
1132 do
1133 if [[ -n $__newpath ]]
1134 then
1135 __newpath=$__newpath"/"
1136 fi
1137 __newpath=$__newpath${__absolute[__i]}
1138 done
1139
1140 echo "$__newpath"
1141}
1142
1143
1144# Install a directory, keeping symlinks as on the original system.
1145# Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
1146# will create ${initdir}/lib64, ${initdir}/lib64/file,
1147# and a symlink ${initdir}/lib -> lib64.
1148inst_dir() {
1149 [[ -e ${initdir}/"$1" ]] && return 0 # already there
1150
1151 local _dir="$1" _part="${1%/*}" _file
1152 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
1153 _dir="$_part $_dir"
1154 _part=${_part%/*}
1155 done
1156
1157 # iterate over parent directories
1158 for _file in $_dir; do
1159 [[ -e "${initdir}/$_file" ]] && continue
1160 if [[ -L $_file ]]; then
1161 inst_symlink "$_file"
1162 else
1163 # create directory
1164 mkdir -m 0755 -p "${initdir}/$_file" || return 1
1165 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
1166 chmod u+w "${initdir}/$_file"
1167 fi
1168 done
1169}
1170
1171# $1 = file to copy to ramdisk
1172# $2 (optional) Name for the file on the ramdisk
1173# Location of the image dir is assumed to be $initdir
1174# We never overwrite the target if it exists.
1175inst_simple() {
1176 [[ -f "$1" ]] || return 1
1177 strstr "$1" "/" || return 1
1178
1179 local _src=$1 target="${2:-$1}"
1180 if ! [[ -d ${initdir}/$target ]]; then
1181 [[ -e ${initdir}/$target ]] && return 0
1182 [[ -L ${initdir}/$target ]] && return 0
1183 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
1184 fi
1185 # install checksum files also
1186 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1187 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
1188 fi
1189 ddebug "Installing $_src"
1190 cp --sparse=always -pfL "$_src" "${initdir}/$target"
1191}
1192
1193# find symlinks linked to given library file
1194# $1 = library file
1195# Function searches for symlinks by stripping version numbers appended to
1196# library filename, checks if it points to the same target and finally
1197# prints the list of symlinks to stdout.
1198#
1199# Example:
1200# rev_lib_symlinks libfoo.so.8.1
1201# output: libfoo.so.8 libfoo.so
1202# (Only if libfoo.so.8 and libfoo.so exists on host system.)
1203rev_lib_symlinks() {
1204 [[ ! $1 ]] && return 0
1205
1206 local fn="$1" orig="$(readlink -f "$1")" links=''
1207
1208 [[ ${fn} =~ .*\.so\..* ]] || return 1
1209
1210 until [[ ${fn##*.} == so ]]; do
1211 fn="${fn%.*}"
1212 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
1213 done
1214
1215 echo "${links}"
1216}
1217
1218# Same as above, but specialized to handle dynamic libraries.
1219# It handles making symlinks according to how the original library
1220# is referenced.
1221inst_library() {
1222 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
1223 strstr "$1" "/" || return 1
1224 [[ -e $initdir/$_dest ]] && return 0
1225 if [[ -L $_src ]]; then
1226 # install checksum files also
1227 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1228 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
1229 fi
1230 _reallib=$(readlink -f "$_src")
1231 inst_simple "$_reallib" "$_reallib"
1232 inst_dir "${_dest%/*}"
1233 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
1234 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
1235 else
1236 inst_simple "$_src" "$_dest"
1237 fi
1238
1239 # Create additional symlinks. See rev_symlinks description.
1240 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
818567fc 1241 [[ -e $initdir/$_symlink ]] || {
898720b7
HH
1242 ddebug "Creating extra symlink: $_symlink"
1243 inst_symlink $_symlink
1244 }
1245 done
1246}
1247
1248# find a binary. If we were not passed the full path directly,
1249# search in the usual places to find the binary.
1250find_binary() {
1251 if [[ -z ${1##/*} ]]; then
1252 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
1253 echo $1
1254 return 0
1255 fi
1256 fi
1257
1258 type -P $1
1259}
1260
1261# Same as above, but specialized to install binary executables.
1262# Install binary executable, and all shared library dependencies, if any.
1263inst_binary() {
1264 local _bin _target
3cdb93d0
FS
1265
1266 # In certain cases we might attempt to install a binary which is already
1267 # present in the test image, yet it's missing from the host system.
1268 # In such cases, let's check if the binary indeed exists in the image
1269 # before doing any other chcecks. If it does, immediately return with
1270 # success.
1271 [[ $# -eq 1 && -e $initdir/$1 ]] && return 0
1272
898720b7
HH
1273 _bin=$(find_binary "$1") || return 1
1274 _target=${2:-$_bin}
1275 [[ -e $initdir/$_target ]] && return 0
1276 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
1277 local _file _line
1278 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1279 # I love bash!
1280 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1281 [[ $_line = 'not a dynamic executable' ]] && break
1282
1283 if [[ $_line =~ $_so_regex ]]; then
1284 _file=${BASH_REMATCH[1]}
1285 [[ -e ${initdir}/$_file ]] && continue
1286 inst_library "$_file"
1287 continue
1288 fi
1289
1290 if [[ $_line =~ not\ found ]]; then
1291 dfatal "Missing a shared library required by $_bin."
1292 dfatal "Run \"ldd $_bin\" to find out what it is."
1293 dfatal "$_line"
1294 dfatal "dracut cannot create an initrd."
1295 exit 1
1296 fi
1297 done
1298 inst_simple "$_bin" "$_target"
1299}
1300
1301# same as above, except for shell scripts.
1302# If your shell script does not start with shebang, it is not a shell script.
1303inst_script() {
1304 local _bin
1305 _bin=$(find_binary "$1") || return 1
1306 shift
1307 local _line _shebang_regex
1308 read -r -n 80 _line <"$_bin"
1309 # If debug is set, clean unprintable chars to prevent messing up the term
1310 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
1311 _shebang_regex='(#! *)(/[^ ]+).*'
1312 [[ $_line =~ $_shebang_regex ]] || return 1
1313 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
1314}
1315
1316# same as above, but specialized for symlinks
1317inst_symlink() {
1318 local _src=$1 _target=${2:-$1} _realsrc
1319 strstr "$1" "/" || return 1
1320 [[ -L $1 ]] || return 1
1321 [[ -L $initdir/$_target ]] && return 0
1322 _realsrc=$(readlink -f "$_src")
1323 if ! [[ -e $initdir/$_realsrc ]]; then
1324 if [[ -d $_realsrc ]]; then
1325 inst_dir "$_realsrc"
1326 else
1327 inst "$_realsrc"
1328 fi
1329 fi
1330 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
1331 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
1332 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
1333}
1334
1335# attempt to install any programs specified in a udev rule
1336inst_rule_programs() {
1337 local _prog _bin
1338
1339 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
1340 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
1341 if [ -x /lib/udev/$_prog ]; then
1342 _bin=/lib/udev/$_prog
1343 else
1344 _bin=$(find_binary "$_prog") || {
1345 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
1346 continue;
1347 }
1348 fi
1349
1350 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
1351 dracut_install "$_bin"
1352 done
1353 fi
1354}
1355
1356# udev rules always get installed in the same place, so
1357# create a function to install them to make life simpler.
1358inst_rules() {
1359 local _target=/etc/udev/rules.d _rule _found
1360
1361 inst_dir "/lib/udev/rules.d"
1362 inst_dir "$_target"
1363 for _rule in "$@"; do
1364 if [ "${rule#/}" = "$rule" ]; then
1365 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1366 if [[ -f $r/$_rule ]]; then
1367 _found="$r/$_rule"
1368 inst_simple "$_found"
1369 inst_rule_programs "$_found"
1370 fi
1371 done
1372 fi
1373 for r in '' ./ $dracutbasedir/rules.d/; do
1374 if [[ -f ${r}$_rule ]]; then
1375 _found="${r}$_rule"
1376 inst_simple "$_found" "$_target/${_found##*/}"
1377 inst_rule_programs "$_found"
1378 fi
1379 done
1380 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
31ce89e7 1381 _found=
898720b7
HH
1382 done
1383}
1384
1385# general purpose installation function
1386# Same args as above.
1387inst() {
1388 local _x
1389
1390 case $# in
1391 1) ;;
1392 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
1393 [[ $initdir = $2 ]] && set $1;;
1394 3) [[ -z $initdir ]] && export initdir=$2
1395 set $1 $3;;
1396 *) dfatal "inst only takes 1 or 2 or 3 arguments"
1397 exit 1;;
1398 esac
1399 for _x in inst_symlink inst_script inst_binary inst_simple; do
1400 $_x "$@" && return 0
1401 done
1402 return 1
1403}
1404
1405# install any of listed files
1406#
1407# If first argument is '-d' and second some destination path, first accessible
1408# source is installed into this path, otherwise it will installed in the same
1409# path as source. If none of listed files was installed, function return 1.
1410# On first successful installation it returns with 0 status.
1411#
1412# Example:
1413#
1414# inst_any -d /bin/foo /bin/bar /bin/baz
1415#
1416# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
1417# initramfs.
1418inst_any() {
1419 local to f
1420
1421 [[ $1 = '-d' ]] && to="$2" && shift 2
1422
1423 for f in "$@"; do
1424 if [[ -e $f ]]; then
1425 [[ $to ]] && inst "$f" "$to" && return 0
1426 inst "$f" && return 0
1427 fi
1428 done
1429
1430 return 1
1431}
1432
1433# dracut_install [-o ] <file> [<file> ... ]
1434# Install <file> to the initramfs image
1435# -o optionally install the <file> and don't fail, if it is not there
1436dracut_install() {
1437 local _optional=no
1438 if [[ $1 = '-o' ]]; then
1439 _optional=yes
1440 shift
1441 fi
1442 while (($# > 0)); do
1443 if ! inst "$1" ; then
1444 if [[ $_optional = yes ]]; then
1445 dinfo "Skipping program $1 as it cannot be found and is" \
1446 "flagged to be optional"
1447 else
1448 dfatal "Failed to install $1"
1449 exit 1
1450 fi
1451 fi
1452 shift
1453 done
1454}
1455
0d6e798a
HH
1456# Install a single kernel module along with any firmware it may require.
1457# $1 = full path to kernel module to install
1458install_kmod_with_fw() {
1459 # no need to go further if the module is already installed
1460
1461 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
1462 && return 0
1463
1464 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1465
1466 if [[ $omit_drivers ]]; then
1467 local _kmod=${1##*/}
1468 _kmod=${_kmod%.ko}
1469 _kmod=${_kmod/-/_}
1470 if [[ "$_kmod" =~ $omit_drivers ]]; then
1471 dinfo "Omitting driver $_kmod"
1472 return 1
1473 fi
1474 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
1475 dinfo "Omitting driver $_kmod"
1476 return 1
1477 fi
1478 fi
1479
1480 [ -d "$initdir/.kernelmodseen" ] && \
1481 > "$initdir/.kernelmodseen/${1##*/}"
1482
1483 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
1484 || return $?
1485
1486 local _modname=${1##*/} _fwdir _found _fw
1487 _modname=${_modname%.ko*}
1488 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
1489 _found=''
1490 for _fwdir in $fw_dir; do
1491 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1492 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1493 _found=yes
1494 fi
1495 done
1496 if [[ $_found != yes ]]; then
1497 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1498 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1499 "\"${_modname}.ko\""
1500 else
1501 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1502 "\"${_modname}.ko\""
1503 fi
1504 fi
1505 done
1506 return 0
1507}
1508
1509# Do something with all the dependencies of a kernel module.
1510# Note that kernel modules depend on themselves using the technique we use
1511# $1 = function to call for each dependency we find
1512# It will be passed the full path to the found kernel module
1513# $2 = module to get dependencies for
1514# rest of args = arguments to modprobe
1515# _fderr specifies FD passed from surrounding scope
1516for_each_kmod_dep() {
1517 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1518 shift 2
1519 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1520 while read _cmd _modpath _options; do
1521 [[ $_cmd = insmod ]] || continue
1522 $_func ${_modpath} || exit $?
1523 _found=1
1524 done
1525 [[ $_found -eq 0 ]] && exit 1
1526 exit 0
1527 )
1528}
1529
1530# filter kernel modules to install certain modules that meet specific
1531# requirements.
1532# $1 = search only in subdirectory of /kernel/$1
1533# $2 = function to call with module name to filter.
1534# This function will be passed the full path to the module to test.
c5315881 1535# The behavior of this function can vary depending on whether $hostonly is set.
0d6e798a
HH
1536# If it is, we will only look at modules that are already in memory.
1537# If it is not, we will look at all kernel modules
1538# This function returns the full filenames of modules that match $1
1539filter_kernel_modules_by_path () (
1540 local _modname _filtercmd
1541 if ! [[ $hostonly ]]; then
1542 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1543 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1544 _filtercmd+=' -o -name "*.ko.xz"'
1545 _filtercmd+=' 2>/dev/null'
1546 else
1547 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1548 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1549 fi
1550 for _modname in $(eval $_filtercmd); do
1551 case $_modname in
1552 *.ko) "$2" "$_modname" && echo "$_modname";;
1553 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1554 $2 $initdir/$$.ko && echo "$_modname"
1555 rm -f $initdir/$$.ko
1556 ;;
1557 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1558 $2 $initdir/$$.ko && echo "$_modname"
1559 rm -f $initdir/$$.ko
1560 ;;
1561 esac
1562 done
1563)
1564find_kernel_modules_by_path () (
1565 if ! [[ $hostonly ]]; then
1566 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1567 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1568 else
1569 cut -d " " -f 1 </proc/modules \
1570 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1571 fi
1572)
1573
1574filter_kernel_modules () {
1575 filter_kernel_modules_by_path drivers "$1"
1576}
1577
1578find_kernel_modules () {
1579 find_kernel_modules_by_path drivers
1580}
1581
1582# instmods [-c] <kernel module> [<kernel module> ... ]
1583# instmods [-c] <kernel subsystem>
1584# install kernel modules along with all their dependencies.
1585# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1586instmods() {
1587 [[ $no_kernel = yes ]] && return
1588 # called [sub]functions inherit _fderr
1589 local _fderr=9
1590 local _check=no
1591 if [[ $1 = '-c' ]]; then
1592 _check=yes
1593 shift
1594 fi
1595
1596 function inst1mod() {
1597 local _ret=0 _mod="$1"
1598 case $_mod in
1599 =*)
1600 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1601 ( [[ "$_mpargs" ]] && echo $_mpargs
1602 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1603 | instmods
1604 else
1605 ( [[ "$_mpargs" ]] && echo $_mpargs
e9638652 1606 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -type f -printf '%f\n' ) \
0d6e798a
HH
1607 | instmods
1608 fi
1609 ;;
1610 --*) _mpargs+=" $_mod" ;;
1611 i2o_scsi) return ;; # Do not load this diagnostic-only module
1612 *)
1613 _mod=${_mod##*/}
1614 # if we are already installed, skip this module and go on
1615 # to the next one.
1616 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1617
1618 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1619 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1620 return
1621 fi
1622 # If we are building a host-specific initramfs and this
1623 # module is not already loaded, move on to the next one.
1624 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1625 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1626 && return
1627
1628 # We use '-d' option in modprobe only if modules prefix path
1629 # differs from default '/'. This allows us to use Dracut with
1630 # old version of modprobe which doesn't have '-d' option.
1631 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1632 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1633
1634 # ok, load the module, all its dependencies, and any firmware
1635 # it may require
1636 for_each_kmod_dep install_kmod_with_fw $_mod \
1637 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1638 ((_ret+=$?))
1639 ;;
1640 esac
1641 return $_ret
1642 }
1643
1644 function instmods_1() {
1645 local _mod _mpargs
1646 if (($# == 0)); then # filenames from stdin
1647 while read _mod; do
1648 inst1mod "${_mod%.ko*}" || {
1649 if [ "$_check" = "yes" ]; then
1650 dfatal "Failed to install $_mod"
1651 return 1
1652 fi
1653 }
1654 done
1655 fi
1656 while (($# > 0)); do # filenames as arguments
1657 inst1mod ${1%.ko*} || {
1658 if [ "$_check" = "yes" ]; then
1659 dfatal "Failed to install $1"
1660 return 1
1661 fi
1662 }
1663 shift
1664 done
1665 return 0
1666 }
1667
1668 local _ret _filter_not_found='FATAL: Module .* not found.'
1669 set -o pipefail
1670 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1671 # redirections, but that would make dracut require bash4 at least.
1672 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1673 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1674 _ret=$?
1675 set +o pipefail
1676 return $_ret
1677}
898720b7 1678
85393d8f 1679setup_suse() {
caced732
FB
1680 ln -fs ../usr/bin/systemctl $initdir/bin/
1681 ln -fs ../usr/lib/systemd $initdir/lib/
85393d8f
TB
1682 inst_simple "/usr/lib/systemd/system/haveged.service"
1683}
1684
9e19a8b0
DS
1685_umount_dir() {
1686 if mountpoint -q $1; then
1687 ddebug "umount $1"
1688 umount $1
1689 fi
1690}
1691
ec4cab49
DS
1692_test_setup_cleanup() {
1693 # only umount if create_empty_image_rootdir() was called to mount it
0036ce06 1694 [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
ec4cab49
DS
1695}
1696
1697# can be overridden in specific test
1698test_setup_cleanup() {
1699 _test_setup_cleanup
1700}
1701
1702_test_cleanup() {
f85bc044
DS
1703 # (post-test) cleanup should always ignore failure and cleanup as much as possible
1704 (
1705 set +e
0036ce06 1706 _umount_dir $initdir
f85bc044
DS
1707 if [[ $LOOPDEV && -b $LOOPDEV ]]; then
1708 ddebug "losetup -d $LOOPDEV"
1709 losetup -d $LOOPDEV
1710 fi
1711 rm -fr "$TESTDIR"
1712 rm -f "$STATEFILE"
65dd488f 1713 ) || :
ec4cab49
DS
1714}
1715
054ee249
MP
1716# can be overridden in specific test
1717test_cleanup() {
ec4cab49 1718 _test_cleanup
054ee249
MP
1719}
1720
1721test_run() {
1722 if [ -z "$TEST_NO_QEMU" ]; then
1723 if run_qemu; then
1724 check_result_qemu || return 1
1725 else
1726 dwarn "can't run QEMU, skipping"
1727 fi
1728 fi
1729 if [ -z "$TEST_NO_NSPAWN" ]; then
746fbd9c
EV
1730 if run_nspawn "nspawn-root"; then
1731 check_result_nspawn "nspawn-root" || return 1
054ee249
MP
1732 else
1733 dwarn "can't run systemd-nspawn, skipping"
1734 fi
746fbd9c
EV
1735
1736 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
1737 if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
1738 check_result_nspawn "unprivileged-nspawn-root" || return 1
1739 else
1740 dwarn "can't run systemd-nspawn, skipping"
1741 fi
d56db495 1742 fi
054ee249
MP
1743 fi
1744 return 0
1745}
1746
898720b7 1747do_test() {
33a5e20f
HH
1748 if [[ $UID != "0" ]]; then
1749 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
1750 exit 0
1751 fi
1752
cc5549ca 1753 # Detect lib paths
898720b7
HH
1754 [[ $libdir ]] || for libdir in /lib64 /lib; do
1755 [[ -d $libdir ]] && libdirs+=" $libdir" && break
1756 done
1757
1758 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
1759 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
1760 done
1761
22077c9c
MP
1762 mkdir -p "$STATEDIR"
1763
898720b7 1764 import_testdir
889a9042 1765 import_initdir
898720b7
HH
1766
1767 while (($# > 0)); do
1768 case $1 in
1769 --run)
1770 echo "TEST RUN: $TEST_DESCRIPTION"
0013fac2
YW
1771 test_run
1772 ret=$?
1773 if (( $ret == 0 )); then
898720b7
HH
1774 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
1775 else
1776 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
1777 fi
1778 exit $ret;;
1779 --setup)
1780 echo "TEST SETUP: $TEST_DESCRIPTION"
1781 test_setup
ec4cab49 1782 test_setup_cleanup
818567fc 1783 ;;
898720b7
HH
1784 --clean)
1785 echo "TEST CLEANUP: $TEST_DESCRIPTION"
1786 test_cleanup
818567fc 1787 ;;
898720b7 1788 --all)
818567fc 1789 ret=0
f85bc044 1790 echo -n "TEST: $TEST_DESCRIPTION "
898720b7 1791 (
ec4cab49
DS
1792 test_setup
1793 test_setup_cleanup
1794 test_run
818567fc 1795 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
f85bc044 1796 test_cleanup
898720b7 1797 if [ $ret -eq 0 ]; then
22077c9c 1798 rm "$TESTLOG"
898720b7
HH
1799 echo "[OK]"
1800 else
1801 echo "[FAILED]"
22077c9c 1802 echo "see $TESTLOG"
898720b7
HH
1803 fi
1804 exit $ret;;
1805 *) break ;;
1806 esac
1807 shift
1808 done
1809}