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