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