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