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