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