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