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