]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-functions
Merge pull request #12802 from irtimmer/fix-openssl
[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
670bec2b 55 QEMU_MEM="1536M"
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
398DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
a19f909b
EV
399DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
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
0d6e61d6
EV
560check_asan_reports() {
561 local ret=0
562 local root="$1"
563
7e11a95e 564 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
0d6e61d6
EV
565 ls -l "$root"
566 if [[ -e "$root/systemd.asan.log.1" ]]; then
567 cat "$root/systemd.asan.log.1"
7e11a95e
EV
568 ret=$(($ret+1))
569 fi
998445fd 570
abf9b52c 571 journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
d56db495 572 if [[ ! -z "$journald_report" ]]; then
6141c6c9
EV
573 printf "%s\n" "$journald_report"
574 cat "$root/systemd-journald.out" || true
998445fd 575 ret=$(($ret+1))
d56db495 576 fi
ed4f303f 577
d56db495
EV
578 pids=$(
579 "$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne '
580 BEGIN {
581 %services_to_ignore = (
582 "dbus-daemon" => undef,
583 );
584 }
6d67286f 585 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
d56db495
EV
586 )
587 if [[ ! -z "$pids" ]]; then
ed4f303f
EV
588 ret=$(($ret+1))
589 for pid in $pids; do
0d6e61d6 590 "$BUILD_DIR/journalctl" -D "$root/var/log/journal" _PID=$pid --no-pager
ed4f303f 591 done
d56db495 592 fi
7e11a95e
EV
593 fi
594
889a9042
RC
595 return $ret
596}
597
0d6e61d6
EV
598check_result_nspawn() {
599 local ret=1
600 local journald_report=""
601 local pids=""
602 [[ -e $TESTDIR/$1/testok ]] && ret=0
603 [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
604 cp -a $TESTDIR/$1/var/log/journal $TESTDIR
605 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
606 ls -l $TESTDIR/journal/*/*.journal
607 test -s $TESTDIR/failed && ret=$(($ret+1))
608 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
609 check_asan_reports "$TESTDIR/$1" || ret=$(($ret+1))
610 return $ret
611}
612
054ee249
MP
613# can be overridden in specific test
614check_result_qemu() {
0013fac2 615 local ret=1
054ee249
MP
616 mkdir -p $TESTDIR/root
617 mount ${LOOPDEV}p1 $TESTDIR/root
618 [[ -e $TESTDIR/root/testok ]] && ret=0
619 [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
620 cp -a $TESTDIR/root/var/log/journal $TESTDIR
0d6e61d6 621 check_asan_reports "$TESTDIR/root" || ret=$(($ret+1))
054ee249
MP
622 umount $TESTDIR/root
623 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
624 ls -l $TESTDIR/journal/*/*.journal
625 test -s $TESTDIR/failed && ret=$(($ret+1))
626 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
627 return $ret
628}
629
889a9042 630strip_binaries() {
5a613464
EV
631 if [[ "$STRIP_BINARIES" = "no" ]]; then
632 ddebug "Don't strip binaries"
633 return 0
634 fi
889a9042 635 ddebug "Strip binaries"
408c9a07
ZJS
636 find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
637 xargs strip --strip-unneeded |& \
638 grep -v 'file format not recognized' | \
639 ddebug
889a9042
RC
640}
641
642create_rc_local() {
643 mkdir -p $initdir/etc/rc.d
644 cat >$initdir/etc/rc.d/rc.local <<EOF
645#!/bin/bash
646exit 0
647EOF
648 chmod 0755 $initdir/etc/rc.d/rc.local
649}
650
651install_execs() {
c7eda013
EV
652 ddebug "install any Execs from the service files"
653 (
209f4b9e 654 export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
c7eda013
EV
655 systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
656 systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
fe4bd4e5 657 sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
e180bdb5 658 | sort -u | while read i; do
818567fc 659 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
e4e039bc
FS
660 # also, plymouth is pulled in by rescue.service, but even there the exit code
661 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
662 inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
c7eda013
EV
663 done
664 )
889a9042
RC
665}
666
667generate_module_dependencies() {
668 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
669 ! depmod -a -b "$initdir" $KERNEL_VER; then
670 dfatal "\"depmod -a $KERNEL_VER\" failed."
671 exit 1
672 fi
673}
674
675install_depmod_files() {
676 inst /lib/modules/$KERNEL_VER/modules.order
677 inst /lib/modules/$KERNEL_VER/modules.builtin
678}
679
680install_plymouth() {
681 # install plymouth, if found... else remove plymouth service files
682 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
683 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
684 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
685 # dracut_install plymouth plymouthd
686 # else
687 rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
688 # fi
689}
690
691install_ld_so_conf() {
692 cp -a /etc/ld.so.conf* $initdir/etc
693 ldconfig -r "$initdir"
694}
695
696install_config_files() {
818567fc 697 inst /etc/sysconfig/init || true
889a9042
RC
698 inst /etc/passwd
699 inst /etc/shadow
bf3a947c 700 inst /etc/login.defs
889a9042
RC
701 inst /etc/group
702 inst /etc/shells
703 inst /etc/nsswitch.conf
818567fc
MP
704 inst /etc/pam.conf || true
705 inst /etc/securetty || true
889a9042
RC
706 inst /etc/os-release
707 inst /etc/localtime
708 # we want an empty environment
709 > $initdir/etc/environment
710 > $initdir/etc/machine-id
711 # set the hostname
712 echo systemd-testsuite > $initdir/etc/hostname
713 # fstab
85393d8f
TB
714 if [[ "$LOOKS_LIKE_SUSE" ]]; then
715 ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1"
716 else
717 ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1"
718 fi
719
889a9042 720 cat >$initdir/etc/fstab <<EOF
85393d8f 721$ROOTMOUNT
889a9042
RC
722EOF
723}
724
725install_basic_tools() {
726 [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
4be4833e 727 dracut_install -o sushell
7d023341
MP
728 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
729 dracut_install -o ldconfig.real
889a9042
RC
730}
731
732install_debug_tools() {
733 [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
c81a46b9
FS
734
735 if [[ $INTERACTIVE_DEBUG ]]; then
736 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
737 local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
738 mkdir -p "$_getty_override"
739 echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
740
741 cat > "$initdir/etc/motd" << EOF
742To adjust the terminal size use:
743 export COLUMNS=xx
744 export LINES=yy
745or
746 stty cols xx rows yy
747EOF
748 fi
889a9042
RC
749}
750
751install_libnss() {
752 # install libnss_files for login
cffae62b 753 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
99877b7e 754 dracut_install $NSS_LIBS
889a9042
RC
755}
756
757install_dbus() {
3486cb6c 758 inst $ROOTLIBDIR/system/dbus.socket
a978c9f2 759
908665f4
LP
760 # Newer Fedora versions use dbus-broker by default. Let's install it is available.
761 if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
762 inst $ROOTLIBDIR/system/dbus-broker.service
763 inst_symlink /etc/systemd/system/dbus.service
764 inst /usr/bin/dbus-broker
765 inst /usr/bin/dbus-broker-launch
766 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
767 # Fedora rawhide replaced dbus.service with dbus-daemon.service
a978c9f2
FS
768 inst $ROOTLIBDIR/system/dbus-daemon.service
769 # Alias symlink
770 inst_symlink /etc/systemd/system/dbus.service
771 else
772 inst $ROOTLIBDIR/system/dbus.service
773 fi
889a9042
RC
774
775 find \
e63b61be 776 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
889a9042
RC
777 | while read file; do
778 inst $file
779 done
780}
781
782install_pam() {
0fe15dc8 783 (
818567fc
MP
784 if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
785 find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
786 else
787 find /lib*/security -xtype f
788 fi
789 find /etc/pam.d /etc/security -xtype f
0fe15dc8 790 ) | while read file; do
889a9042
RC
791 inst $file
792 done
417491f1 793
d5172c79
EV
794 # pam_unix depends on unix_chkpwd.
795 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
796 dracut_install -o unix_chkpwd
797
417491f1
EV
798 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
799 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
e14b866b
ZJS
800
801 # set empty root password for easy debugging
802 sed -i 's/^root:x:/root::/' $initdir/etc/passwd
889a9042
RC
803}
804
805install_keymaps() {
83a7051e
YW
806 # The first three paths may be deprecated.
807 # It seems now the last two paths are used by many distributions.
889a9042
RC
808 for i in \
809 /usr/lib/kbd/keymaps/include/* \
810 /usr/lib/kbd/keymaps/i386/include/* \
83a7051e
YW
811 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
812 /usr/lib/kbd/keymaps/legacy/include/* \
813 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
889a9042
RC
814 [[ -f $i ]] || continue
815 inst $i
816 done
ad931fee
YW
817
818 # When it takes any argument, then install more keymaps.
819 if [[ -n $1 ]]; then
820 for i in \
821 /usr/lib/kbd/keymaps/i386/*/* \
822 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
823 [[ -f $i ]] || continue
824 inst $i
825 done
826 fi
889a9042
RC
827}
828
7d10ec1c
YW
829install_zoneinfo() {
830 for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
831 [[ -f $i ]] || continue
832 inst $i
833 done
834}
835
889a9042
RC
836install_fonts() {
837 for i in \
25b47f96 838 /usr/lib/kbd/consolefonts/eurlatgr* \
889a9042
RC
839 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
840 [[ -f $i ]] || continue
841 inst $i
842 done
843}
844
845install_terminfo() {
846 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
847 [ -f ${_terminfodir}/l/linux ] && break
848 done
849 dracut_install -o ${_terminfodir}/l/linux
850}
851
852setup_testsuite() {
53d90f95 853 cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
5c404f1a 854 cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
889a9042
RC
855
856 mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
857 ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
c81a46b9
FS
858 # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
859 [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
889a9042
RC
860
861 # make the testsuite the default target
862 ln -fs testsuite.target $initdir/etc/systemd/system/default.target
863}
864
865setup_nspawn_root() {
866 rm -fr $TESTDIR/nspawn-root
867 ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
868 cp -ar $initdir $TESTDIR/nspawn-root
869 # we don't mount in the nspawn root
870 rm -f $TESTDIR/nspawn-root/etc/fstab
746fbd9c
EV
871 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
872 cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
873 fi
889a9042
RC
874}
875
0d6e798a 876setup_basic_dirs() {
889a9042
RC
877 mkdir -p $initdir/run
878 mkdir -p $initdir/etc/systemd/system
879 mkdir -p $initdir/var/log/journal
880
0d6e798a 881 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
882 if [ -L "/$d" ]; then
883 inst_symlink "/$d"
884 else
0d6e798a 885 inst_dir "/$d"
898720b7
HH
886 fi
887 done
888
889 ln -sfn /run "$initdir/var/run"
890 ln -sfn /run/lock "$initdir/var/lock"
891}
892
893inst_libs() {
894 local _bin=$1
895 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
896 local _file _line
897
898 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
899 [[ $_line = 'not a dynamic executable' ]] && break
900
901 if [[ $_line =~ $_so_regex ]]; then
902 _file=${BASH_REMATCH[1]}
903 [[ -e ${initdir}/$_file ]] && continue
904 inst_library "$_file"
905 continue
906 fi
907
908 if [[ $_line =~ not\ found ]]; then
909 dfatal "Missing a shared library required by $_bin."
910 dfatal "Run \"ldd $_bin\" to find out what it is."
911 dfatal "$_line"
912 dfatal "dracut cannot create an initrd."
913 exit 1
914 fi
915 done
916}
917
918import_testdir() {
898720b7 919 [[ -e $STATEFILE ]] && . $STATEFILE
3f50fff5
FS
920 if [[ ! -d "$TESTDIR" ]]; then
921 if [[ -z "$TESTDIR" ]]; then
922 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
923 else
924 mkdir -p "$TESTDIR"
925 fi
926
898720b7
HH
927 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
928 export TESTDIR
929 fi
930}
931
889a9042
RC
932import_initdir() {
933 initdir=$TESTDIR/root
934 export initdir
935}
936
898720b7
HH
937## @brief Converts numeric logging level to the first letter of level name.
938#
939# @param lvl Numeric logging level in range from 1 to 6.
940# @retval 1 if @a lvl is out of range.
941# @retval 0 if @a lvl is correct.
942# @result Echoes first letter of level name.
943_lvl2char() {
944 case "$1" in
945 1) echo F;;
946 2) echo E;;
947 3) echo W;;
948 4) echo I;;
949 5) echo D;;
950 6) echo T;;
951 *) return 1;;
952 esac
953}
954
955## @brief Internal helper function for _do_dlog()
956#
957# @param lvl Numeric logging level.
958# @param msg Message.
959# @retval 0 It's always returned, even if logging failed.
960#
961# @note This function is not supposed to be called manually. Please use
962# dtrace(), ddebug(), or others instead which wrap this one.
963#
964# This function calls _do_dlog() either with parameter msg, or if
965# none is given, it will read standard input and will use every line as
966# a message.
967#
968# This enables:
969# dwarn "This is a warning"
970# echo "This is a warning" | dwarn
20fc56c0 971LOG_LEVEL=${LOG_LEVEL:-4}
898720b7
HH
972
973dlog() {
974 [ -z "$LOG_LEVEL" ] && return 0
975 [ $1 -le $LOG_LEVEL ] || return 0
976 local lvl="$1"; shift
977 local lvlc=$(_lvl2char "$lvl") || return 0
978
979 if [ $# -ge 1 ]; then
980 echo "$lvlc: $*"
981 else
982 while read line; do
983 echo "$lvlc: " "$line"
984 done
985 fi
986}
987
988## @brief Logs message at TRACE level (6)
989#
990# @param msg Message.
991# @retval 0 It's always returned, even if logging failed.
992dtrace() {
993 set +x
994 dlog 6 "$@"
995 [ -n "$debug" ] && set -x || :
996}
997
998## @brief Logs message at DEBUG level (5)
999#
1000# @param msg Message.
1001# @retval 0 It's always returned, even if logging failed.
1002ddebug() {
0d6e798a 1003# set +x
898720b7 1004 dlog 5 "$@"
0d6e798a 1005# [ -n "$debug" ] && set -x || :
898720b7
HH
1006}
1007
1008## @brief Logs message at INFO level (4)
1009#
1010# @param msg Message.
1011# @retval 0 It's always returned, even if logging failed.
1012dinfo() {
1013 set +x
1014 dlog 4 "$@"
1015 [ -n "$debug" ] && set -x || :
1016}
1017
1018## @brief Logs message at WARN level (3)
1019#
1020# @param msg Message.
1021# @retval 0 It's always returned, even if logging failed.
1022dwarn() {
1023 set +x
1024 dlog 3 "$@"
1025 [ -n "$debug" ] && set -x || :
1026}
1027
1028## @brief Logs message at ERROR level (2)
1029#
1030# @param msg Message.
1031# @retval 0 It's always returned, even if logging failed.
1032derror() {
0d6e798a 1033# set +x
898720b7 1034 dlog 2 "$@"
0d6e798a 1035# [ -n "$debug" ] && set -x || :
898720b7
HH
1036}
1037
1038## @brief Logs message at FATAL level (1)
1039#
1040# @param msg Message.
1041# @retval 0 It's always returned, even if logging failed.
1042dfatal() {
1043 set +x
1044 dlog 1 "$@"
1045 [ -n "$debug" ] && set -x || :
1046}
1047
1048
1049# Generic substring function. If $2 is in $1, return 0.
1050strstr() { [ "${1#*$2*}" != "$1" ]; }
1051
1052# normalize_path <path>
1053# Prints the normalized path, where it removes any duplicated
1054# and trailing slashes.
1055# Example:
1056# $ normalize_path ///test/test//
1057# /test/test
1058normalize_path() {
1059 shopt -q -s extglob
1060 set -- "${1//+(\/)//}"
1061 shopt -q -u extglob
1062 echo "${1%/}"
1063}
1064
1065# convert_abs_rel <from> <to>
1066# Prints the relative path, when creating a symlink to <to> from <from>.
1067# Example:
1068# $ convert_abs_rel /usr/bin/test /bin/test-2
1069# ../../bin/test-2
1070# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
1071convert_abs_rel() {
1072 local __current __absolute __abssize __cursize __newpath
1073 local -i __i __level
1074
1075 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
1076
1077 # corner case #1 - self looping link
1078 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
1079
1080 # corner case #2 - own dir link
1081 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
1082
1083 IFS="/" __current=($1)
1084 IFS="/" __absolute=($2)
1085
1086 __abssize=${#__absolute[@]}
1087 __cursize=${#__current[@]}
1088
1089 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
1090 do
1091 (( __level++ ))
1092 if (( __level > __abssize || __level > __cursize ))
1093 then
1094 break
1095 fi
1096 done
1097
1098 for ((__i = __level; __i < __cursize-1; __i++))
1099 do
1100 if ((__i > __level))
1101 then
1102 __newpath=$__newpath"/"
1103 fi
1104 __newpath=$__newpath".."
1105 done
1106
1107 for ((__i = __level; __i < __abssize; __i++))
1108 do
1109 if [[ -n $__newpath ]]
1110 then
1111 __newpath=$__newpath"/"
1112 fi
1113 __newpath=$__newpath${__absolute[__i]}
1114 done
1115
1116 echo "$__newpath"
1117}
1118
1119
1120# Install a directory, keeping symlinks as on the original system.
1121# Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
1122# will create ${initdir}/lib64, ${initdir}/lib64/file,
1123# and a symlink ${initdir}/lib -> lib64.
1124inst_dir() {
1125 [[ -e ${initdir}/"$1" ]] && return 0 # already there
1126
1127 local _dir="$1" _part="${1%/*}" _file
1128 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
1129 _dir="$_part $_dir"
1130 _part=${_part%/*}
1131 done
1132
1133 # iterate over parent directories
1134 for _file in $_dir; do
1135 [[ -e "${initdir}/$_file" ]] && continue
1136 if [[ -L $_file ]]; then
1137 inst_symlink "$_file"
1138 else
1139 # create directory
1140 mkdir -m 0755 -p "${initdir}/$_file" || return 1
1141 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
1142 chmod u+w "${initdir}/$_file"
1143 fi
1144 done
1145}
1146
1147# $1 = file to copy to ramdisk
1148# $2 (optional) Name for the file on the ramdisk
1149# Location of the image dir is assumed to be $initdir
1150# We never overwrite the target if it exists.
1151inst_simple() {
1152 [[ -f "$1" ]] || return 1
1153 strstr "$1" "/" || return 1
1154
1155 local _src=$1 target="${2:-$1}"
1156 if ! [[ -d ${initdir}/$target ]]; then
1157 [[ -e ${initdir}/$target ]] && return 0
1158 [[ -L ${initdir}/$target ]] && return 0
1159 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
1160 fi
1161 # install checksum files also
1162 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1163 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
1164 fi
1165 ddebug "Installing $_src"
1166 cp --sparse=always -pfL "$_src" "${initdir}/$target"
1167}
1168
1169# find symlinks linked to given library file
1170# $1 = library file
1171# Function searches for symlinks by stripping version numbers appended to
1172# library filename, checks if it points to the same target and finally
1173# prints the list of symlinks to stdout.
1174#
1175# Example:
1176# rev_lib_symlinks libfoo.so.8.1
1177# output: libfoo.so.8 libfoo.so
1178# (Only if libfoo.so.8 and libfoo.so exists on host system.)
1179rev_lib_symlinks() {
1180 [[ ! $1 ]] && return 0
1181
1182 local fn="$1" orig="$(readlink -f "$1")" links=''
1183
1184 [[ ${fn} =~ .*\.so\..* ]] || return 1
1185
1186 until [[ ${fn##*.} == so ]]; do
1187 fn="${fn%.*}"
1188 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
1189 done
1190
1191 echo "${links}"
1192}
1193
1194# Same as above, but specialized to handle dynamic libraries.
1195# It handles making symlinks according to how the original library
1196# is referenced.
1197inst_library() {
1198 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
1199 strstr "$1" "/" || return 1
1200 [[ -e $initdir/$_dest ]] && return 0
1201 if [[ -L $_src ]]; then
1202 # install checksum files also
1203 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1204 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
1205 fi
1206 _reallib=$(readlink -f "$_src")
1207 inst_simple "$_reallib" "$_reallib"
1208 inst_dir "${_dest%/*}"
1209 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
1210 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
1211 else
1212 inst_simple "$_src" "$_dest"
1213 fi
1214
1215 # Create additional symlinks. See rev_symlinks description.
1216 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
818567fc 1217 [[ -e $initdir/$_symlink ]] || {
898720b7
HH
1218 ddebug "Creating extra symlink: $_symlink"
1219 inst_symlink $_symlink
1220 }
1221 done
1222}
1223
1224# find a binary. If we were not passed the full path directly,
1225# search in the usual places to find the binary.
1226find_binary() {
1227 if [[ -z ${1##/*} ]]; then
1228 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
1229 echo $1
1230 return 0
1231 fi
1232 fi
1233
1234 type -P $1
1235}
1236
1237# Same as above, but specialized to install binary executables.
1238# Install binary executable, and all shared library dependencies, if any.
1239inst_binary() {
1240 local _bin _target
1241 _bin=$(find_binary "$1") || return 1
1242 _target=${2:-$_bin}
1243 [[ -e $initdir/$_target ]] && return 0
1244 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
1245 local _file _line
1246 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1247 # I love bash!
1248 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1249 [[ $_line = 'not a dynamic executable' ]] && break
1250
1251 if [[ $_line =~ $_so_regex ]]; then
1252 _file=${BASH_REMATCH[1]}
1253 [[ -e ${initdir}/$_file ]] && continue
1254 inst_library "$_file"
1255 continue
1256 fi
1257
1258 if [[ $_line =~ not\ found ]]; then
1259 dfatal "Missing a shared library required by $_bin."
1260 dfatal "Run \"ldd $_bin\" to find out what it is."
1261 dfatal "$_line"
1262 dfatal "dracut cannot create an initrd."
1263 exit 1
1264 fi
1265 done
1266 inst_simple "$_bin" "$_target"
1267}
1268
1269# same as above, except for shell scripts.
1270# If your shell script does not start with shebang, it is not a shell script.
1271inst_script() {
1272 local _bin
1273 _bin=$(find_binary "$1") || return 1
1274 shift
1275 local _line _shebang_regex
1276 read -r -n 80 _line <"$_bin"
1277 # If debug is set, clean unprintable chars to prevent messing up the term
1278 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
1279 _shebang_regex='(#! *)(/[^ ]+).*'
1280 [[ $_line =~ $_shebang_regex ]] || return 1
1281 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
1282}
1283
1284# same as above, but specialized for symlinks
1285inst_symlink() {
1286 local _src=$1 _target=${2:-$1} _realsrc
1287 strstr "$1" "/" || return 1
1288 [[ -L $1 ]] || return 1
1289 [[ -L $initdir/$_target ]] && return 0
1290 _realsrc=$(readlink -f "$_src")
1291 if ! [[ -e $initdir/$_realsrc ]]; then
1292 if [[ -d $_realsrc ]]; then
1293 inst_dir "$_realsrc"
1294 else
1295 inst "$_realsrc"
1296 fi
1297 fi
1298 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
1299 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
1300 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
1301}
1302
1303# attempt to install any programs specified in a udev rule
1304inst_rule_programs() {
1305 local _prog _bin
1306
1307 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
1308 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
1309 if [ -x /lib/udev/$_prog ]; then
1310 _bin=/lib/udev/$_prog
1311 else
1312 _bin=$(find_binary "$_prog") || {
1313 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
1314 continue;
1315 }
1316 fi
1317
1318 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
1319 dracut_install "$_bin"
1320 done
1321 fi
1322}
1323
1324# udev rules always get installed in the same place, so
1325# create a function to install them to make life simpler.
1326inst_rules() {
1327 local _target=/etc/udev/rules.d _rule _found
1328
1329 inst_dir "/lib/udev/rules.d"
1330 inst_dir "$_target"
1331 for _rule in "$@"; do
1332 if [ "${rule#/}" = "$rule" ]; then
1333 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1334 if [[ -f $r/$_rule ]]; then
1335 _found="$r/$_rule"
1336 inst_simple "$_found"
1337 inst_rule_programs "$_found"
1338 fi
1339 done
1340 fi
1341 for r in '' ./ $dracutbasedir/rules.d/; do
1342 if [[ -f ${r}$_rule ]]; then
1343 _found="${r}$_rule"
1344 inst_simple "$_found" "$_target/${_found##*/}"
1345 inst_rule_programs "$_found"
1346 fi
1347 done
1348 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
31ce89e7 1349 _found=
898720b7
HH
1350 done
1351}
1352
1353# general purpose installation function
1354# Same args as above.
1355inst() {
1356 local _x
1357
1358 case $# in
1359 1) ;;
1360 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
1361 [[ $initdir = $2 ]] && set $1;;
1362 3) [[ -z $initdir ]] && export initdir=$2
1363 set $1 $3;;
1364 *) dfatal "inst only takes 1 or 2 or 3 arguments"
1365 exit 1;;
1366 esac
1367 for _x in inst_symlink inst_script inst_binary inst_simple; do
1368 $_x "$@" && return 0
1369 done
1370 return 1
1371}
1372
1373# install any of listed files
1374#
1375# If first argument is '-d' and second some destination path, first accessible
1376# source is installed into this path, otherwise it will installed in the same
1377# path as source. If none of listed files was installed, function return 1.
1378# On first successful installation it returns with 0 status.
1379#
1380# Example:
1381#
1382# inst_any -d /bin/foo /bin/bar /bin/baz
1383#
1384# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
1385# initramfs.
1386inst_any() {
1387 local to f
1388
1389 [[ $1 = '-d' ]] && to="$2" && shift 2
1390
1391 for f in "$@"; do
1392 if [[ -e $f ]]; then
1393 [[ $to ]] && inst "$f" "$to" && return 0
1394 inst "$f" && return 0
1395 fi
1396 done
1397
1398 return 1
1399}
1400
1401# dracut_install [-o ] <file> [<file> ... ]
1402# Install <file> to the initramfs image
1403# -o optionally install the <file> and don't fail, if it is not there
1404dracut_install() {
1405 local _optional=no
1406 if [[ $1 = '-o' ]]; then
1407 _optional=yes
1408 shift
1409 fi
1410 while (($# > 0)); do
1411 if ! inst "$1" ; then
1412 if [[ $_optional = yes ]]; then
1413 dinfo "Skipping program $1 as it cannot be found and is" \
1414 "flagged to be optional"
1415 else
1416 dfatal "Failed to install $1"
1417 exit 1
1418 fi
1419 fi
1420 shift
1421 done
1422}
1423
0d6e798a
HH
1424# Install a single kernel module along with any firmware it may require.
1425# $1 = full path to kernel module to install
1426install_kmod_with_fw() {
1427 # no need to go further if the module is already installed
1428
1429 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
1430 && return 0
1431
1432 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1433
1434 if [[ $omit_drivers ]]; then
1435 local _kmod=${1##*/}
1436 _kmod=${_kmod%.ko}
1437 _kmod=${_kmod/-/_}
1438 if [[ "$_kmod" =~ $omit_drivers ]]; then
1439 dinfo "Omitting driver $_kmod"
1440 return 1
1441 fi
1442 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
1443 dinfo "Omitting driver $_kmod"
1444 return 1
1445 fi
1446 fi
1447
1448 [ -d "$initdir/.kernelmodseen" ] && \
1449 > "$initdir/.kernelmodseen/${1##*/}"
1450
1451 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
1452 || return $?
1453
1454 local _modname=${1##*/} _fwdir _found _fw
1455 _modname=${_modname%.ko*}
1456 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
1457 _found=''
1458 for _fwdir in $fw_dir; do
1459 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1460 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1461 _found=yes
1462 fi
1463 done
1464 if [[ $_found != yes ]]; then
1465 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1466 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1467 "\"${_modname}.ko\""
1468 else
1469 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1470 "\"${_modname}.ko\""
1471 fi
1472 fi
1473 done
1474 return 0
1475}
1476
1477# Do something with all the dependencies of a kernel module.
1478# Note that kernel modules depend on themselves using the technique we use
1479# $1 = function to call for each dependency we find
1480# It will be passed the full path to the found kernel module
1481# $2 = module to get dependencies for
1482# rest of args = arguments to modprobe
1483# _fderr specifies FD passed from surrounding scope
1484for_each_kmod_dep() {
1485 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1486 shift 2
1487 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1488 while read _cmd _modpath _options; do
1489 [[ $_cmd = insmod ]] || continue
1490 $_func ${_modpath} || exit $?
1491 _found=1
1492 done
1493 [[ $_found -eq 0 ]] && exit 1
1494 exit 0
1495 )
1496}
1497
1498# filter kernel modules to install certain modules that meet specific
1499# requirements.
1500# $1 = search only in subdirectory of /kernel/$1
1501# $2 = function to call with module name to filter.
1502# This function will be passed the full path to the module to test.
c5315881 1503# The behavior of this function can vary depending on whether $hostonly is set.
0d6e798a
HH
1504# If it is, we will only look at modules that are already in memory.
1505# If it is not, we will look at all kernel modules
1506# This function returns the full filenames of modules that match $1
1507filter_kernel_modules_by_path () (
1508 local _modname _filtercmd
1509 if ! [[ $hostonly ]]; then
1510 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1511 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1512 _filtercmd+=' -o -name "*.ko.xz"'
1513 _filtercmd+=' 2>/dev/null'
1514 else
1515 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1516 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1517 fi
1518 for _modname in $(eval $_filtercmd); do
1519 case $_modname in
1520 *.ko) "$2" "$_modname" && echo "$_modname";;
1521 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1522 $2 $initdir/$$.ko && echo "$_modname"
1523 rm -f $initdir/$$.ko
1524 ;;
1525 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1526 $2 $initdir/$$.ko && echo "$_modname"
1527 rm -f $initdir/$$.ko
1528 ;;
1529 esac
1530 done
1531)
1532find_kernel_modules_by_path () (
1533 if ! [[ $hostonly ]]; then
1534 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1535 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1536 else
1537 cut -d " " -f 1 </proc/modules \
1538 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1539 fi
1540)
1541
1542filter_kernel_modules () {
1543 filter_kernel_modules_by_path drivers "$1"
1544}
1545
1546find_kernel_modules () {
1547 find_kernel_modules_by_path drivers
1548}
1549
1550# instmods [-c] <kernel module> [<kernel module> ... ]
1551# instmods [-c] <kernel subsystem>
1552# install kernel modules along with all their dependencies.
1553# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1554instmods() {
1555 [[ $no_kernel = yes ]] && return
1556 # called [sub]functions inherit _fderr
1557 local _fderr=9
1558 local _check=no
1559 if [[ $1 = '-c' ]]; then
1560 _check=yes
1561 shift
1562 fi
1563
1564 function inst1mod() {
1565 local _ret=0 _mod="$1"
1566 case $_mod in
1567 =*)
1568 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1569 ( [[ "$_mpargs" ]] && echo $_mpargs
1570 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1571 | instmods
1572 else
1573 ( [[ "$_mpargs" ]] && echo $_mpargs
1574 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \
1575 | instmods
1576 fi
1577 ;;
1578 --*) _mpargs+=" $_mod" ;;
1579 i2o_scsi) return ;; # Do not load this diagnostic-only module
1580 *)
1581 _mod=${_mod##*/}
1582 # if we are already installed, skip this module and go on
1583 # to the next one.
1584 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1585
1586 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1587 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1588 return
1589 fi
1590 # If we are building a host-specific initramfs and this
1591 # module is not already loaded, move on to the next one.
1592 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1593 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1594 && return
1595
1596 # We use '-d' option in modprobe only if modules prefix path
1597 # differs from default '/'. This allows us to use Dracut with
1598 # old version of modprobe which doesn't have '-d' option.
1599 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1600 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1601
1602 # ok, load the module, all its dependencies, and any firmware
1603 # it may require
1604 for_each_kmod_dep install_kmod_with_fw $_mod \
1605 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1606 ((_ret+=$?))
1607 ;;
1608 esac
1609 return $_ret
1610 }
1611
1612 function instmods_1() {
1613 local _mod _mpargs
1614 if (($# == 0)); then # filenames from stdin
1615 while read _mod; do
1616 inst1mod "${_mod%.ko*}" || {
1617 if [ "$_check" = "yes" ]; then
1618 dfatal "Failed to install $_mod"
1619 return 1
1620 fi
1621 }
1622 done
1623 fi
1624 while (($# > 0)); do # filenames as arguments
1625 inst1mod ${1%.ko*} || {
1626 if [ "$_check" = "yes" ]; then
1627 dfatal "Failed to install $1"
1628 return 1
1629 fi
1630 }
1631 shift
1632 done
1633 return 0
1634 }
1635
1636 local _ret _filter_not_found='FATAL: Module .* not found.'
1637 set -o pipefail
1638 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1639 # redirections, but that would make dracut require bash4 at least.
1640 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1641 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1642 _ret=$?
1643 set +o pipefail
1644 return $_ret
1645}
898720b7
HH
1646
1647# inst_libdir_file [-n <pattern>] <file> [<file>...]
1648# Install a <file> located on a lib directory to the initramfs image
1649# -n <pattern> install non-matching files
1650inst_libdir_file() {
1651 if [[ "$1" == "-n" ]]; then
1652 local _pattern=$1
1653 shift 2
1654 for _dir in $libdirs; do
1655 for _i in "$@"; do
1656 for _f in "$_dir"/$_i; do
1657 [[ "$_i" =~ $_pattern ]] || continue
1658 [[ -e "$_i" ]] && dracut_install "$_i"
1659 done
1660 done
1661 done
1662 else
1663 for _dir in $libdirs; do
1664 for _i in "$@"; do
1665 for _f in "$_dir"/$_i; do
1666 [[ -e "$_f" ]] && dracut_install "$_f"
1667 done
1668 done
1669 done
1670 fi
1671}
1672
85393d8f 1673setup_suse() {
caced732
FB
1674 ln -fs ../usr/bin/systemctl $initdir/bin/
1675 ln -fs ../usr/lib/systemd $initdir/lib/
85393d8f
TB
1676 inst_simple "/usr/lib/systemd/system/haveged.service"
1677}
1678
054ee249
MP
1679# can be overridden in specific test
1680test_cleanup() {
1681 umount $TESTDIR/root 2>/dev/null || true
818567fc 1682 [[ $LOOPDEV ]] && losetup -d $LOOPDEV || true
054ee249
MP
1683 return 0
1684}
1685
1686test_run() {
1687 if [ -z "$TEST_NO_QEMU" ]; then
1688 if run_qemu; then
1689 check_result_qemu || return 1
1690 else
1691 dwarn "can't run QEMU, skipping"
1692 fi
1693 fi
1694 if [ -z "$TEST_NO_NSPAWN" ]; then
746fbd9c
EV
1695 if run_nspawn "nspawn-root"; then
1696 check_result_nspawn "nspawn-root" || return 1
054ee249
MP
1697 else
1698 dwarn "can't run systemd-nspawn, skipping"
1699 fi
746fbd9c
EV
1700
1701 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
1702 if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
1703 check_result_nspawn "unprivileged-nspawn-root" || return 1
1704 else
1705 dwarn "can't run systemd-nspawn, skipping"
1706 fi
d56db495 1707 fi
054ee249
MP
1708 fi
1709 return 0
1710}
1711
898720b7 1712do_test() {
33a5e20f
HH
1713 if [[ $UID != "0" ]]; then
1714 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
1715 exit 0
1716 fi
1717
cc5549ca 1718 # Detect lib paths
898720b7
HH
1719 [[ $libdir ]] || for libdir in /lib64 /lib; do
1720 [[ -d $libdir ]] && libdirs+=" $libdir" && break
1721 done
1722
1723 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
1724 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
1725 done
1726
22077c9c
MP
1727 mkdir -p "$STATEDIR"
1728
898720b7 1729 import_testdir
889a9042 1730 import_initdir
898720b7
HH
1731
1732 while (($# > 0)); do
1733 case $1 in
1734 --run)
1735 echo "TEST RUN: $TEST_DESCRIPTION"
0013fac2
YW
1736 test_run
1737 ret=$?
1738 if (( $ret == 0 )); then
898720b7
HH
1739 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
1740 else
1741 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
1742 fi
1743 exit $ret;;
1744 --setup)
1745 echo "TEST SETUP: $TEST_DESCRIPTION"
1746 test_setup
818567fc 1747 ;;
898720b7
HH
1748 --clean)
1749 echo "TEST CLEANUP: $TEST_DESCRIPTION"
1750 test_cleanup
1751 rm -fr "$TESTDIR"
22077c9c 1752 rm -f "$STATEFILE"
818567fc 1753 ;;
898720b7 1754 --all)
818567fc 1755 ret=0
898720b7
HH
1756 echo -n "TEST: $TEST_DESCRIPTION ";
1757 (
1758 test_setup && test_run
1759 ret=$?
1760 test_cleanup
1761 rm -fr "$TESTDIR"
22077c9c 1762 rm -f "$STATEFILE"
898720b7 1763 exit $ret
818567fc 1764 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
898720b7 1765 if [ $ret -eq 0 ]; then
22077c9c 1766 rm "$TESTLOG"
898720b7
HH
1767 echo "[OK]"
1768 else
1769 echo "[FAILED]"
22077c9c 1770 echo "see $TESTLOG"
898720b7
HH
1771 fi
1772 exit $ret;;
1773 *) break ;;
1774 esac
1775 shift
1776 done
1777}