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