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