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