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