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