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