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