]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-functions
tmpfiles: support --image= similar to --root=
[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
2aa5a13a
ER
636 if [[ "$LOOKS_LIKE_SUSE" ]]; then
637 inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules
638 fi
889a9042
RC
639}
640
641install_systemd() {
642 # install compiled files
ca992ecf
EV
643 local _ninja_bin=$(type -P ninja || type -P ninja-build)
644 if [[ -z "$_ninja_bin" ]]; then
645 dfatal "ninja was not found"
646 exit 1
647 fi
648 (set -x; DESTDIR=$initdir "$_ninja_bin" -C $BUILD_DIR install)
889a9042 649 # remove unneeded documentation
23756070 650 rm -fr $initdir/usr/share/{man,doc}
889a9042
RC
651 # we strip binaries since debug symbols increase binaries size a lot
652 # and it could fill the available space
653 strip_binaries
61b480b6 654
8c3534b5 655 [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
85393d8f 656
61b480b6
ZJS
657 # enable debug logging in PID1
658 echo LogLevel=debug >> $initdir/etc/systemd/system.conf
215bffe1
FS
659 # store coredumps in journal
660 echo Storage=journal >> $initdir/etc/systemd/coredump.conf
889a9042
RC
661}
662
d7a4278d
FS
663get_ldpath() {
664 local _bin="$1"
5bb4503d
LP
665 local rpath=$(objdump -p "$_bin" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :)
666
667 if [ -z "$rpath" ] ; then
668 echo $BUILD_DIR
669 else
670 echo $rpath
671 fi
d7a4278d
FS
672}
673
889a9042
RC
674install_missing_libraries() {
675 # install possible missing libraries
e3d3dada 676 for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
37ee8dc8 677 LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
889a9042
RC
678 done
679}
680
1506edca
ZJS
681cleanup_loopdev() {
682 if [ -n "${LOOPDEV}" ]; then
683 ddebug "losetup -d $LOOPDEV"
684 losetup -d "${LOOPDEV}"
2991fa41 685 unset LOOPDEV
1506edca
ZJS
686 fi
687}
688
6e5b51d9 689trap cleanup_loopdev EXIT INT QUIT PIPE
1506edca 690
889a9042 691create_empty_image() {
8c3534b5
ZJS
692 if [ -z "$IMAGE_NAME" ]; then
693 echo "create_empty_image: \$IMAGE_NAME not set"
694 exit 1
695 fi
696
28c7474e
EV
697 local _size=500
698 if [[ "$STRIP_BINARIES" = "no" ]]; then
37ee8dc8 699 _size=$((4*_size))
28c7474e 700 fi
80c53fe7 701
e8945092
FS
702 echo "Setting up $IMAGE_PUBLIC (${_size} MB)"
703 rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC"
ec43f686 704
889a9042 705 # Create the blank file to use as a root filesystem
2991fa41 706 truncate -s "${_size}M" "$IMAGE_PUBLIC"
ec43f686 707
e8945092 708 LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
739d81dd 709 [ -b "$LOOPDEV" ] || return 1
edbced8a 710 sfdisk "$LOOPDEV" <<EOF
32983312 711,$((_size-50))M
889a9042
RC
712,
713EOF
714
053edc5b
EV
715 udevadm settle
716
8a59e79c 717 local _label="-L systemd.${name}"
331fb4ca 718 # mkfs.reiserfs doesn't know -L. so, use --label instead
8a59e79c 719 [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd.${name}"
1506edca
ZJS
720 mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; ret=$?
721 if [ $ret -ne 0 ] ; then
331fb4ca
EV
722 dfatal "Failed to mkfs -t ${FSTYPE}"
723 exit 1
724 fi
889a9042
RC
725}
726
8c3534b5
ZJS
727mount_initdir() {
728 if [ -z "${LOOPDEV}" ]; then
e8945092 729 [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
8c3534b5
ZJS
730 LOOPDEV=$(losetup --show -P -f "$image")
731 [ -b "$LOOPDEV" ] || return 1
8c3534b5 732
1506edca
ZJS
733 udevadm settle
734 fi
ec4cab49 735
1506edca
ZJS
736 if ! mountpoint -q $initdir; then
737 mkdir -p $initdir
738 mount ${LOOPDEV}p1 $initdir
739 TEST_SETUP_CLEANUP_ROOTDIR=1
8c3534b5 740 fi
8c3534b5
ZJS
741}
742
ec43f686
ZJS
743cleanup_initdir() {
744 # only umount if create_empty_image_rootdir() was called to mount it
745 [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
746}
747
748umount_loopback() {
749 # unmount the loopback device from all places. Otherwise we risk file
750 # system corruption.
e8945092 751 for device in $(losetup -l | awk '$6=="'"$IMAGE_PUBLIC"'" {print $1}'); do
ec43f686
ZJS
752 ddebug "Unmounting all uses of $device"
753 mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
754 done
755}
756
8c3534b5
ZJS
757create_empty_image_rootdir() {
758 create_empty_image
759 mount_initdir
760}
761
0d6e61d6
EV
762check_asan_reports() {
763 local ret=0
764 local root="$1"
765
7e11a95e 766 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
0d6e61d6
EV
767 ls -l "$root"
768 if [[ -e "$root/systemd.asan.log.1" ]]; then
769 cat "$root/systemd.asan.log.1"
7e11a95e
EV
770 ret=$(($ret+1))
771 fi
998445fd 772
abf9b52c 773 journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
d56db495 774 if [[ ! -z "$journald_report" ]]; then
6141c6c9 775 printf "%s\n" "$journald_report"
65dd488f 776 cat "$root/systemd-journald.out" || :
998445fd 777 ret=$(($ret+1))
d56db495 778 fi
ed4f303f 779
d56db495 780 pids=$(
84c49ad1 781 "$JOURNALCTL" -D "$root/var/log/journal" | perl -alne '
d56db495
EV
782 BEGIN {
783 %services_to_ignore = (
784 "dbus-daemon" => undef,
785 );
786 }
6d67286f 787 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
d56db495
EV
788 )
789 if [[ ! -z "$pids" ]]; then
ed4f303f
EV
790 ret=$(($ret+1))
791 for pid in $pids; do
84c49ad1 792 "$JOURNALCTL" -D "$root/var/log/journal" _PID=$pid --no-pager
ed4f303f 793 done
d56db495 794 fi
7e11a95e
EV
795 fi
796
889a9042
RC
797 return $ret
798}
799
8943daf8 800save_journal() {
f9eb2d51
ZJS
801 if [ -n "${ARTIFACT_DIRECTORY}" ]; then
802 dest="${ARTIFACT_DIRECTORY}/${testname}.journal"
803 else
804 dest="$TESTDIR/system.journal"
805 fi
8943daf8 806
f9eb2d51 807 for j in $1/*; do
cad67279 808 $SYSTEMD_JOURNAL_REMOTE \
f9eb2d51 809 -o $dest \
cad67279 810 --getter="$JOURNALCTL -o export -D $j"
f1416431
ZJS
811
812 if [ -n "${TEST_SHOW_JOURNAL}" ]; then
813 echo "---- $j ----"
cad67279 814 $JOURNALCTL --no-pager -o short-monotonic --no-hostname --priority=${TEST_SHOW_JOURNAL} -D $j
f1416431
ZJS
815 fi
816
f9eb2d51
ZJS
817 rm -r $j
818 done
8943daf8
ZJS
819
820 # we want to print this sometime later, so save this in a variable
f9eb2d51 821 JOURNAL_LIST="$(ls -l $dest*)"
8943daf8
ZJS
822}
823
0d6e61d6
EV
824check_result_nspawn() {
825 local ret=1
826 local journald_report=""
827 local pids=""
ec43f686
ZJS
828 [[ -e $1/testok ]] && ret=0
829 [[ -f $1/failed ]] && cp -a $1/failed $TESTDIR
8943daf8 830 save_journal $1/var/log/journal
0d6e61d6 831 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
8943daf8 832 echo $JOURNAL_LIST
0d6e61d6
EV
833 test -s $TESTDIR/failed && ret=$(($ret+1))
834 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
ec43f686 835 check_asan_reports "$1" || ret=$(($ret+1))
1506edca 836 _umount_dir $initdir
0d6e61d6
EV
837 return $ret
838}
839
054ee249
MP
840# can be overridden in specific test
841check_result_qemu() {
0013fac2 842 local ret=1
8c3534b5 843 mount_initdir
0036ce06
DS
844 [[ -e $initdir/testok ]] && ret=0
845 [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
8943daf8 846 save_journal $initdir/var/log/journal
0036ce06 847 check_asan_reports "$initdir" || ret=$(($ret+1))
1506edca 848 _umount_dir $initdir
054ee249 849 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
8943daf8 850 echo $JOURNAL_LIST
054ee249
MP
851 test -s $TESTDIR/failed && ret=$(($ret+1))
852 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
853 return $ret
854}
855
889a9042 856strip_binaries() {
5a613464
EV
857 if [[ "$STRIP_BINARIES" = "no" ]]; then
858 ddebug "Don't strip binaries"
859 return 0
860 fi
889a9042 861 ddebug "Strip binaries"
408c9a07
ZJS
862 find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
863 xargs strip --strip-unneeded |& \
d391ee10 864 grep -vi 'file format not recognized' | \
408c9a07 865 ddebug
889a9042
RC
866}
867
868create_rc_local() {
869 mkdir -p $initdir/etc/rc.d
870 cat >$initdir/etc/rc.d/rc.local <<EOF
ff12a795 871#!/usr/bin/env bash
889a9042
RC
872exit 0
873EOF
874 chmod 0755 $initdir/etc/rc.d/rc.local
875}
876
877install_execs() {
c7eda013
EV
878 ddebug "install any Execs from the service files"
879 (
209f4b9e 880 export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
c7eda013
EV
881 systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
882 systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
fe4bd4e5 883 sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
e180bdb5 884 | sort -u | while read i; do
818567fc 885 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
e4e039bc
FS
886 # also, plymouth is pulled in by rescue.service, but even there the exit code
887 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
71a0de36 888 dinfo "Attempting to install $i"
e46736fb 889 inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "${i##*/}" == "plymouth" ]
c7eda013
EV
890 done
891 )
889a9042
RC
892}
893
894generate_module_dependencies() {
895 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
896 ! depmod -a -b "$initdir" $KERNEL_VER; then
897 dfatal "\"depmod -a $KERNEL_VER\" failed."
898 exit 1
899 fi
900}
901
902install_depmod_files() {
903 inst /lib/modules/$KERNEL_VER/modules.order
904 inst /lib/modules/$KERNEL_VER/modules.builtin
905}
906
907install_plymouth() {
908 # install plymouth, if found... else remove plymouth service files
909 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
910 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
911 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
912 # dracut_install plymouth plymouthd
913 # else
cad67279 914 rm -f $initdir/{usr/lib,lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,lib,etc}/systemd/system/*/plymouth*
889a9042
RC
915 # fi
916}
917
918install_ld_so_conf() {
919 cp -a /etc/ld.so.conf* $initdir/etc
920 ldconfig -r "$initdir"
921}
922
d0ac89a1
ZJS
923install_testuser() {
924 # create unprivileged user for user manager tests
925 mkdir -p $initdir/etc/sysusers.d
926 cat >$initdir/etc/sysusers.d/testuser.conf <<EOF
927u testuser 4711 "Test User" /home/testuser
928EOF
929
930 mkdir -p $initdir/home/testuser -m 0700
931 chown 4711:4711 $initdir/home/testuser
932}
933
889a9042 934install_config_files() {
65dd488f 935 inst /etc/sysconfig/init || :
889a9042
RC
936 inst /etc/passwd
937 inst /etc/shadow
2aa5a13a 938 inst_any /etc/login.defs /usr/etc/login.defs
889a9042
RC
939 inst /etc/group
940 inst /etc/shells
9e173292 941 inst_any /etc/nsswitch.conf /usr/etc/nsswitch.conf
65dd488f 942 inst /etc/pam.conf || :
889a9042
RC
943 inst /etc/os-release
944 inst /etc/localtime
945 # we want an empty environment
946 > $initdir/etc/environment
947 > $initdir/etc/machine-id
7eeeab20 948
889a9042
RC
949 # set the hostname
950 echo systemd-testsuite > $initdir/etc/hostname
a455e75a
ZJS
951
952 # let's set up just one image with the traditional verbose output
953 if [ ${IMAGE_NAME} != "basic" ]; then
954 mkdir -p $initdir/etc/systemd/system.conf.d
955 echo -e '[Manager]\nStatusUnitFormat=name' >$initdir/etc/systemd/system.conf.d/status.conf
956 fi
889a9042
RC
957}
958
959install_basic_tools() {
4110a6de 960 dracut_install "${BASICTOOLS[@]}"
4be4833e 961 dracut_install -o sushell
7d023341
MP
962 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
963 dracut_install -o ldconfig.real
889a9042
RC
964}
965
966install_debug_tools() {
4110a6de 967 dracut_install "${DEBUGTOOLS[@]}"
c81a46b9
FS
968
969 if [[ $INTERACTIVE_DEBUG ]]; then
970 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
971 local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
972 mkdir -p "$_getty_override"
973 echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
974
975 cat > "$initdir/etc/motd" << EOF
976To adjust the terminal size use:
977 export COLUMNS=xx
978 export LINES=yy
979or
980 stty cols xx rows yy
981EOF
982 fi
889a9042
RC
983}
984
985install_libnss() {
986 # install libnss_files for login
cffae62b 987 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
99877b7e 988 dracut_install $NSS_LIBS
889a9042
RC
989}
990
991install_dbus() {
3486cb6c 992 inst $ROOTLIBDIR/system/dbus.socket
a978c9f2 993
a49ad4c4 994 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
908665f4
LP
995 if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
996 inst $ROOTLIBDIR/system/dbus-broker.service
997 inst_symlink /etc/systemd/system/dbus.service
998 inst /usr/bin/dbus-broker
999 inst /usr/bin/dbus-broker-launch
1000 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
1001 # Fedora rawhide replaced dbus.service with dbus-daemon.service
a978c9f2
FS
1002 inst $ROOTLIBDIR/system/dbus-daemon.service
1003 # Alias symlink
1004 inst_symlink /etc/systemd/system/dbus.service
1005 else
1006 inst $ROOTLIBDIR/system/dbus.service
1007 fi
889a9042
RC
1008
1009 find \
e63b61be 1010 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
889a9042
RC
1011 | while read file; do
1012 inst $file
1013 done
bdfd515a
ZJS
1014
1015 # setup policy for Type=dbus test
1016 mkdir -p $initdir/etc/dbus-1/system.d
1017 cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
1018<?xml version="1.0"?>
1019<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
1020 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
1021<busconfig>
1022 <policy user="root">
1023 <allow own="systemd.test.ExecStopPost"/>
1024 </policy>
1025</busconfig>
1026EOF
889a9042
RC
1027}
1028
a49ad4c4
FB
1029install_user_dbus() {
1030 inst $ROOTLIBDIR/user/dbus.socket
1031 inst_symlink /usr/lib/systemd/user/sockets.target.wants/dbus.socket || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket
1032
1033 # Append the After= dependency on dbus in case it isn't already set up
1034 mkdir -p "$initdir/etc/systemd/system/user@.service.d/"
1035 cat <<EOF >"$initdir/etc/systemd/system/user@.service.d/dbus.conf"
1036[Unit]
1037After=dbus.service
1038EOF
1039
1040 # Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
1041 if [ -f $ROOTLIBDIR/user/dbus-broker.service ]; then
1042 inst $ROOTLIBDIR/user/dbus-broker.service
1043 inst_symlink /etc/systemd/user/dbus.service
1044 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
1045 # Fedora rawhide replaced dbus.service with dbus-daemon.service
1046 inst $ROOTLIBDIR/user/dbus-daemon.service
1047 # Alias symlink
1048 inst_symlink /etc/systemd/user/dbus.service
1049 else
1050 inst $ROOTLIBDIR/user/dbus.service
1051 fi
1052}
1053
889a9042 1054install_pam() {
0fe15dc8 1055 (
818567fc
MP
1056 if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
1057 find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
1058 else
1059 find /lib*/security -xtype f
1060 fi
1061 find /etc/pam.d /etc/security -xtype f
0fe15dc8 1062 ) | while read file; do
889a9042
RC
1063 inst $file
1064 done
417491f1 1065
d5172c79
EV
1066 # pam_unix depends on unix_chkpwd.
1067 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
1068 dracut_install -o unix_chkpwd
1069
417491f1
EV
1070 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
1071 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
e14b866b
ZJS
1072
1073 # set empty root password for easy debugging
1074 sed -i 's/^root:x:/root::/' $initdir/etc/passwd
889a9042
RC
1075}
1076
1077install_keymaps() {
83a7051e
YW
1078 # The first three paths may be deprecated.
1079 # It seems now the last two paths are used by many distributions.
889a9042
RC
1080 for i in \
1081 /usr/lib/kbd/keymaps/include/* \
1082 /usr/lib/kbd/keymaps/i386/include/* \
83a7051e
YW
1083 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
1084 /usr/lib/kbd/keymaps/legacy/include/* \
1085 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
889a9042
RC
1086 [[ -f $i ]] || continue
1087 inst $i
1088 done
ad931fee
YW
1089
1090 # When it takes any argument, then install more keymaps.
1091 if [[ -n $1 ]]; then
1092 for i in \
1093 /usr/lib/kbd/keymaps/i386/*/* \
1094 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
1095 [[ -f $i ]] || continue
1096 inst $i
1097 done
1098 fi
889a9042
RC
1099}
1100
7d10ec1c 1101install_zoneinfo() {
f4c40fd7
ZJS
1102 inst_any /usr/share/zoneinfo/Asia/Seoul
1103 inst_any /usr/share/zoneinfo/Asia/Vladivostok
1104 inst_any /usr/share/zoneinfo/Australia/Sydney
1105 inst_any /usr/share/zoneinfo/Europe/Berlin
1106 inst_any /usr/share/zoneinfo/Europe/Kiev
1107 inst_any /usr/share/zoneinfo/Pacific/Auckland
1108 inst_any /usr/share/zoneinfo/Pacific/Honolulu
1109 inst_any /usr/share/zoneinfo/CET
1110 inst_any /usr/share/zoneinfo/EET
1111 inst_any /usr/share/zoneinfo/UTC
7d10ec1c
YW
1112}
1113
889a9042
RC
1114install_fonts() {
1115 for i in \
25b47f96 1116 /usr/lib/kbd/consolefonts/eurlatgr* \
889a9042
RC
1117 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
1118 [[ -f $i ]] || continue
1119 inst $i
1120 done
1121}
1122
1123install_terminfo() {
1124 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
1125 [ -f ${_terminfodir}/l/linux ] && break
1126 done
1127 dracut_install -o ${_terminfodir}/l/linux
1128}
1129
a49ad4c4
FB
1130has_user_dbus_socket() {
1131 if [ -f /usr/lib/systemd/user/dbus.socket ] || [ -f /etc/systemd/user/dbus.socket ]; then
1132 return 0
1133 else
1134 echo "Per-user instances are not supported. Skipping..."
1135 return 1
1136 fi
1137}
1138
889a9042 1139setup_nspawn_root() {
8c3534b5
ZJS
1140 if [ -z "${initdir}" ]; then
1141 dfatal "\$initdir not defined"
1142 exit 1
1143 fi
ec43f686 1144
693ad298
ZJS
1145 rm -rf "$TESTDIR/unprivileged-nspawn-root"
1146
746fbd9c 1147 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
ec43f686
ZJS
1148 ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
1149 cp -ar $initdir $TESTDIR/unprivileged-nspawn-root
746fbd9c 1150 fi
889a9042
RC
1151}
1152
0d6e798a 1153setup_basic_dirs() {
889a9042
RC
1154 mkdir -p $initdir/run
1155 mkdir -p $initdir/etc/systemd/system
1156 mkdir -p $initdir/var/log/journal
1157
0d6e798a 1158 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
1159 if [ -L "/$d" ]; then
1160 inst_symlink "/$d"
1161 else
0d6e798a 1162 inst_dir "/$d"
898720b7
HH
1163 fi
1164 done
1165
1166 ln -sfn /run "$initdir/var/run"
1167 ln -sfn /run/lock "$initdir/var/lock"
1168}
1169
51fa8591
ZJS
1170mask_supporting_services() {
1171 # mask some services that we do not want to run in these tests
1172 ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
1173 ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
1174 ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
1175 ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
1176 ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
1177}
1178
898720b7
HH
1179inst_libs() {
1180 local _bin=$1
1181 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1182 local _file _line
1183
1184 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1185 [[ $_line = 'not a dynamic executable' ]] && break
1186
1187 if [[ $_line =~ $_so_regex ]]; then
1188 _file=${BASH_REMATCH[1]}
1189 [[ -e ${initdir}/$_file ]] && continue
1190 inst_library "$_file"
1191 continue
1192 fi
1193
1194 if [[ $_line =~ not\ found ]]; then
1195 dfatal "Missing a shared library required by $_bin."
1196 dfatal "Run \"ldd $_bin\" to find out what it is."
1197 dfatal "$_line"
1198 dfatal "dracut cannot create an initrd."
1199 exit 1
1200 fi
1201 done
1202}
1203
1204import_testdir() {
1506edca
ZJS
1205 # make sure we don't get a stale LOOPDEV value from old times
1206 __LOOPDEV=$LOOPDEV
898720b7 1207 [[ -e $STATEFILE ]] && . $STATEFILE
1506edca 1208 LOOPDEV=$__LOOPDEV
3f50fff5
FS
1209 if [[ ! -d "$TESTDIR" ]]; then
1210 if [[ -z "$TESTDIR" ]]; then
1211 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
1212 else
1213 mkdir -p "$TESTDIR"
1214 fi
1215
8c3534b5
ZJS
1216 cat >$STATEFILE<<EOF
1217TESTDIR="$TESTDIR"
8c3534b5 1218EOF
898720b7
HH
1219 export TESTDIR
1220 fi
e8945092
FS
1221
1222 IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME}.img"
1223 IMAGE_PUBLIC="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
898720b7
HH
1224}
1225
889a9042
RC
1226import_initdir() {
1227 initdir=$TESTDIR/root
8c03df36 1228 mkdir -p $initdir
889a9042
RC
1229 export initdir
1230}
1231
898720b7
HH
1232## @brief Converts numeric logging level to the first letter of level name.
1233#
1234# @param lvl Numeric logging level in range from 1 to 6.
1235# @retval 1 if @a lvl is out of range.
1236# @retval 0 if @a lvl is correct.
1237# @result Echoes first letter of level name.
1238_lvl2char() {
1239 case "$1" in
1240 1) echo F;;
1241 2) echo E;;
1242 3) echo W;;
1243 4) echo I;;
1244 5) echo D;;
1245 6) echo T;;
1246 *) return 1;;
1247 esac
1248}
1249
1250## @brief Internal helper function for _do_dlog()
1251#
1252# @param lvl Numeric logging level.
1253# @param msg Message.
1254# @retval 0 It's always returned, even if logging failed.
1255#
1256# @note This function is not supposed to be called manually. Please use
1257# dtrace(), ddebug(), or others instead which wrap this one.
1258#
1259# This function calls _do_dlog() either with parameter msg, or if
1260# none is given, it will read standard input and will use every line as
1261# a message.
1262#
1263# This enables:
1264# dwarn "This is a warning"
1265# echo "This is a warning" | dwarn
20fc56c0 1266LOG_LEVEL=${LOG_LEVEL:-4}
898720b7
HH
1267
1268dlog() {
1269 [ -z "$LOG_LEVEL" ] && return 0
1270 [ $1 -le $LOG_LEVEL ] || return 0
1271 local lvl="$1"; shift
1272 local lvlc=$(_lvl2char "$lvl") || return 0
1273
1274 if [ $# -ge 1 ]; then
1275 echo "$lvlc: $*"
1276 else
1277 while read line; do
1278 echo "$lvlc: " "$line"
1279 done
1280 fi
1281}
1282
1283## @brief Logs message at TRACE level (6)
1284#
1285# @param msg Message.
1286# @retval 0 It's always returned, even if logging failed.
1287dtrace() {
1288 set +x
1289 dlog 6 "$@"
1290 [ -n "$debug" ] && set -x || :
1291}
1292
1293## @brief Logs message at DEBUG level (5)
1294#
1295# @param msg Message.
1296# @retval 0 It's always returned, even if logging failed.
1297ddebug() {
0d6e798a 1298# set +x
898720b7 1299 dlog 5 "$@"
0d6e798a 1300# [ -n "$debug" ] && set -x || :
898720b7
HH
1301}
1302
1303## @brief Logs message at INFO level (4)
1304#
1305# @param msg Message.
1306# @retval 0 It's always returned, even if logging failed.
1307dinfo() {
1308 set +x
1309 dlog 4 "$@"
1310 [ -n "$debug" ] && set -x || :
1311}
1312
1313## @brief Logs message at WARN level (3)
1314#
1315# @param msg Message.
1316# @retval 0 It's always returned, even if logging failed.
1317dwarn() {
1318 set +x
1319 dlog 3 "$@"
1320 [ -n "$debug" ] && set -x || :
1321}
1322
1323## @brief Logs message at ERROR level (2)
1324#
1325# @param msg Message.
1326# @retval 0 It's always returned, even if logging failed.
1327derror() {
0d6e798a 1328# set +x
898720b7 1329 dlog 2 "$@"
0d6e798a 1330# [ -n "$debug" ] && set -x || :
898720b7
HH
1331}
1332
1333## @brief Logs message at FATAL level (1)
1334#
1335# @param msg Message.
1336# @retval 0 It's always returned, even if logging failed.
1337dfatal() {
1338 set +x
1339 dlog 1 "$@"
1340 [ -n "$debug" ] && set -x || :
1341}
1342
1343
1344# Generic substring function. If $2 is in $1, return 0.
1345strstr() { [ "${1#*$2*}" != "$1" ]; }
1346
1347# normalize_path <path>
1348# Prints the normalized path, where it removes any duplicated
1349# and trailing slashes.
1350# Example:
1351# $ normalize_path ///test/test//
1352# /test/test
1353normalize_path() {
1354 shopt -q -s extglob
1355 set -- "${1//+(\/)//}"
1356 shopt -q -u extglob
1357 echo "${1%/}"
1358}
1359
1360# convert_abs_rel <from> <to>
1361# Prints the relative path, when creating a symlink to <to> from <from>.
1362# Example:
1363# $ convert_abs_rel /usr/bin/test /bin/test-2
1364# ../../bin/test-2
1365# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
1366convert_abs_rel() {
1367 local __current __absolute __abssize __cursize __newpath
1368 local -i __i __level
1369
1370 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
1371
1372 # corner case #1 - self looping link
1373 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
1374
1375 # corner case #2 - own dir link
1376 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
1377
1378 IFS="/" __current=($1)
1379 IFS="/" __absolute=($2)
1380
1381 __abssize=${#__absolute[@]}
1382 __cursize=${#__current[@]}
1383
1384 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
1385 do
1386 (( __level++ ))
1387 if (( __level > __abssize || __level > __cursize ))
1388 then
1389 break
1390 fi
1391 done
1392
1393 for ((__i = __level; __i < __cursize-1; __i++))
1394 do
1395 if ((__i > __level))
1396 then
1397 __newpath=$__newpath"/"
1398 fi
1399 __newpath=$__newpath".."
1400 done
1401
1402 for ((__i = __level; __i < __abssize; __i++))
1403 do
1404 if [[ -n $__newpath ]]
1405 then
1406 __newpath=$__newpath"/"
1407 fi
1408 __newpath=$__newpath${__absolute[__i]}
1409 done
1410
1411 echo "$__newpath"
1412}
1413
1414
1415# Install a directory, keeping symlinks as on the original system.
1416# Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
1417# will create ${initdir}/lib64, ${initdir}/lib64/file,
1418# and a symlink ${initdir}/lib -> lib64.
1419inst_dir() {
1420 [[ -e ${initdir}/"$1" ]] && return 0 # already there
1421
1422 local _dir="$1" _part="${1%/*}" _file
1423 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
1424 _dir="$_part $_dir"
1425 _part=${_part%/*}
1426 done
1427
1428 # iterate over parent directories
1429 for _file in $_dir; do
1430 [[ -e "${initdir}/$_file" ]] && continue
1431 if [[ -L $_file ]]; then
1432 inst_symlink "$_file"
1433 else
1434 # create directory
1435 mkdir -m 0755 -p "${initdir}/$_file" || return 1
1436 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
1437 chmod u+w "${initdir}/$_file"
1438 fi
1439 done
1440}
1441
1442# $1 = file to copy to ramdisk
1443# $2 (optional) Name for the file on the ramdisk
1444# Location of the image dir is assumed to be $initdir
1445# We never overwrite the target if it exists.
1446inst_simple() {
1447 [[ -f "$1" ]] || return 1
1448 strstr "$1" "/" || return 1
1449
1450 local _src=$1 target="${2:-$1}"
1451 if ! [[ -d ${initdir}/$target ]]; then
1452 [[ -e ${initdir}/$target ]] && return 0
1453 [[ -L ${initdir}/$target ]] && return 0
1454 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
1455 fi
1456 # install checksum files also
1457 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1458 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
1459 fi
1460 ddebug "Installing $_src"
1461 cp --sparse=always -pfL "$_src" "${initdir}/$target"
1462}
1463
1464# find symlinks linked to given library file
1465# $1 = library file
1466# Function searches for symlinks by stripping version numbers appended to
1467# library filename, checks if it points to the same target and finally
1468# prints the list of symlinks to stdout.
1469#
1470# Example:
1471# rev_lib_symlinks libfoo.so.8.1
1472# output: libfoo.so.8 libfoo.so
1473# (Only if libfoo.so.8 and libfoo.so exists on host system.)
1474rev_lib_symlinks() {
1475 [[ ! $1 ]] && return 0
1476
1477 local fn="$1" orig="$(readlink -f "$1")" links=''
1478
1479 [[ ${fn} =~ .*\.so\..* ]] || return 1
1480
1481 until [[ ${fn##*.} == so ]]; do
1482 fn="${fn%.*}"
1483 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
1484 done
1485
1486 echo "${links}"
1487}
1488
1489# Same as above, but specialized to handle dynamic libraries.
1490# It handles making symlinks according to how the original library
1491# is referenced.
1492inst_library() {
1493 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
1494 strstr "$1" "/" || return 1
1495 [[ -e $initdir/$_dest ]] && return 0
1496 if [[ -L $_src ]]; then
1497 # install checksum files also
1498 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1499 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
1500 fi
1501 _reallib=$(readlink -f "$_src")
1502 inst_simple "$_reallib" "$_reallib"
1503 inst_dir "${_dest%/*}"
1504 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
1505 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
1506 else
1507 inst_simple "$_src" "$_dest"
1508 fi
1509
1510 # Create additional symlinks. See rev_symlinks description.
1511 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
818567fc 1512 [[ -e $initdir/$_symlink ]] || {
898720b7
HH
1513 ddebug "Creating extra symlink: $_symlink"
1514 inst_symlink $_symlink
1515 }
1516 done
1517}
1518
1519# find a binary. If we were not passed the full path directly,
1520# search in the usual places to find the binary.
1521find_binary() {
1522 if [[ -z ${1##/*} ]]; then
1523 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
1524 echo $1
1525 return 0
1526 fi
1527 fi
1528
1529 type -P $1
1530}
1531
1532# Same as above, but specialized to install binary executables.
1533# Install binary executable, and all shared library dependencies, if any.
1534inst_binary() {
1535 local _bin _target
3cdb93d0
FS
1536
1537 # In certain cases we might attempt to install a binary which is already
1538 # present in the test image, yet it's missing from the host system.
1539 # In such cases, let's check if the binary indeed exists in the image
1540 # before doing any other chcecks. If it does, immediately return with
1541 # success.
1542 [[ $# -eq 1 && -e $initdir/$1 ]] && return 0
1543
898720b7
HH
1544 _bin=$(find_binary "$1") || return 1
1545 _target=${2:-$_bin}
1546 [[ -e $initdir/$_target ]] && return 0
1547 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
1548 local _file _line
1549 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1550 # I love bash!
1551 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1552 [[ $_line = 'not a dynamic executable' ]] && break
1553
1554 if [[ $_line =~ $_so_regex ]]; then
1555 _file=${BASH_REMATCH[1]}
1556 [[ -e ${initdir}/$_file ]] && continue
1557 inst_library "$_file"
1558 continue
1559 fi
1560
1561 if [[ $_line =~ not\ found ]]; then
1562 dfatal "Missing a shared library required by $_bin."
1563 dfatal "Run \"ldd $_bin\" to find out what it is."
1564 dfatal "$_line"
1565 dfatal "dracut cannot create an initrd."
1566 exit 1
1567 fi
1568 done
1569 inst_simple "$_bin" "$_target"
1570}
1571
1572# same as above, except for shell scripts.
1573# If your shell script does not start with shebang, it is not a shell script.
1574inst_script() {
1575 local _bin
1576 _bin=$(find_binary "$1") || return 1
1577 shift
1578 local _line _shebang_regex
1579 read -r -n 80 _line <"$_bin"
1580 # If debug is set, clean unprintable chars to prevent messing up the term
1581 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
1582 _shebang_regex='(#! *)(/[^ ]+).*'
1583 [[ $_line =~ $_shebang_regex ]] || return 1
1584 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
1585}
1586
1587# same as above, but specialized for symlinks
1588inst_symlink() {
1589 local _src=$1 _target=${2:-$1} _realsrc
1590 strstr "$1" "/" || return 1
1591 [[ -L $1 ]] || return 1
1592 [[ -L $initdir/$_target ]] && return 0
1593 _realsrc=$(readlink -f "$_src")
1594 if ! [[ -e $initdir/$_realsrc ]]; then
1595 if [[ -d $_realsrc ]]; then
1596 inst_dir "$_realsrc"
1597 else
1598 inst "$_realsrc"
1599 fi
1600 fi
1601 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
1602 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
1603 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
1604}
1605
1606# attempt to install any programs specified in a udev rule
1607inst_rule_programs() {
1608 local _prog _bin
1609
1610 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
1611 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
1612 if [ -x /lib/udev/$_prog ]; then
1613 _bin=/lib/udev/$_prog
1614 else
1615 _bin=$(find_binary "$_prog") || {
1616 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
1617 continue;
1618 }
1619 fi
1620
1621 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
1622 dracut_install "$_bin"
1623 done
1624 fi
1625}
1626
1627# udev rules always get installed in the same place, so
1628# create a function to install them to make life simpler.
1629inst_rules() {
1630 local _target=/etc/udev/rules.d _rule _found
1631
1632 inst_dir "/lib/udev/rules.d"
1633 inst_dir "$_target"
1634 for _rule in "$@"; do
1635 if [ "${rule#/}" = "$rule" ]; then
1636 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1637 if [[ -f $r/$_rule ]]; then
1638 _found="$r/$_rule"
1639 inst_simple "$_found"
1640 inst_rule_programs "$_found"
1641 fi
1642 done
1643 fi
1644 for r in '' ./ $dracutbasedir/rules.d/; do
1645 if [[ -f ${r}$_rule ]]; then
1646 _found="${r}$_rule"
1647 inst_simple "$_found" "$_target/${_found##*/}"
1648 inst_rule_programs "$_found"
1649 fi
1650 done
1651 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
31ce89e7 1652 _found=
898720b7
HH
1653 done
1654}
1655
1656# general purpose installation function
1657# Same args as above.
1658inst() {
1659 local _x
1660
1661 case $# in
1662 1) ;;
1663 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
1664 [[ $initdir = $2 ]] && set $1;;
1665 3) [[ -z $initdir ]] && export initdir=$2
1666 set $1 $3;;
1667 *) dfatal "inst only takes 1 or 2 or 3 arguments"
1668 exit 1;;
1669 esac
1670 for _x in inst_symlink inst_script inst_binary inst_simple; do
1671 $_x "$@" && return 0
1672 done
1673 return 1
1674}
1675
1676# install any of listed files
1677#
1678# If first argument is '-d' and second some destination path, first accessible
1679# source is installed into this path, otherwise it will installed in the same
1680# path as source. If none of listed files was installed, function return 1.
1681# On first successful installation it returns with 0 status.
1682#
1683# Example:
1684#
1685# inst_any -d /bin/foo /bin/bar /bin/baz
1686#
1687# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
1688# initramfs.
1689inst_any() {
1690 local to f
1691
1692 [[ $1 = '-d' ]] && to="$2" && shift 2
1693
1694 for f in "$@"; do
1695 if [[ -e $f ]]; then
1696 [[ $to ]] && inst "$f" "$to" && return 0
1697 inst "$f" && return 0
1698 fi
1699 done
1700
1701 return 1
1702}
1703
1704# dracut_install [-o ] <file> [<file> ... ]
1705# Install <file> to the initramfs image
1706# -o optionally install the <file> and don't fail, if it is not there
1707dracut_install() {
1708 local _optional=no
1709 if [[ $1 = '-o' ]]; then
1710 _optional=yes
1711 shift
1712 fi
1713 while (($# > 0)); do
1714 if ! inst "$1" ; then
1715 if [[ $_optional = yes ]]; then
1716 dinfo "Skipping program $1 as it cannot be found and is" \
1717 "flagged to be optional"
1718 else
1719 dfatal "Failed to install $1"
1720 exit 1
1721 fi
1722 fi
1723 shift
1724 done
1725}
1726
0d6e798a
HH
1727# Install a single kernel module along with any firmware it may require.
1728# $1 = full path to kernel module to install
1729install_kmod_with_fw() {
1730 # no need to go further if the module is already installed
1731
1732 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
1733 && return 0
1734
1735 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1736
1737 if [[ $omit_drivers ]]; then
1738 local _kmod=${1##*/}
1739 _kmod=${_kmod%.ko}
1740 _kmod=${_kmod/-/_}
1741 if [[ "$_kmod" =~ $omit_drivers ]]; then
1742 dinfo "Omitting driver $_kmod"
1743 return 1
1744 fi
1745 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
1746 dinfo "Omitting driver $_kmod"
1747 return 1
1748 fi
1749 fi
1750
1751 [ -d "$initdir/.kernelmodseen" ] && \
1752 > "$initdir/.kernelmodseen/${1##*/}"
1753
1754 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
1755 || return $?
1756
1757 local _modname=${1##*/} _fwdir _found _fw
1758 _modname=${_modname%.ko*}
1759 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
1760 _found=''
1761 for _fwdir in $fw_dir; do
1762 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1763 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1764 _found=yes
1765 fi
1766 done
1767 if [[ $_found != yes ]]; then
1768 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1769 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1770 "\"${_modname}.ko\""
1771 else
1772 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1773 "\"${_modname}.ko\""
1774 fi
1775 fi
1776 done
1777 return 0
1778}
1779
1780# Do something with all the dependencies of a kernel module.
1781# Note that kernel modules depend on themselves using the technique we use
1782# $1 = function to call for each dependency we find
1783# It will be passed the full path to the found kernel module
1784# $2 = module to get dependencies for
1785# rest of args = arguments to modprobe
1786# _fderr specifies FD passed from surrounding scope
1787for_each_kmod_dep() {
1788 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1789 shift 2
1790 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1791 while read _cmd _modpath _options; do
1792 [[ $_cmd = insmod ]] || continue
1793 $_func ${_modpath} || exit $?
1794 _found=1
1795 done
1796 [[ $_found -eq 0 ]] && exit 1
1797 exit 0
1798 )
1799}
1800
1801# filter kernel modules to install certain modules that meet specific
1802# requirements.
1803# $1 = search only in subdirectory of /kernel/$1
1804# $2 = function to call with module name to filter.
1805# This function will be passed the full path to the module to test.
c5315881 1806# The behavior of this function can vary depending on whether $hostonly is set.
0d6e798a
HH
1807# If it is, we will only look at modules that are already in memory.
1808# If it is not, we will look at all kernel modules
1809# This function returns the full filenames of modules that match $1
1810filter_kernel_modules_by_path () (
1811 local _modname _filtercmd
1812 if ! [[ $hostonly ]]; then
1813 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1814 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1815 _filtercmd+=' -o -name "*.ko.xz"'
1816 _filtercmd+=' 2>/dev/null'
1817 else
1818 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1819 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1820 fi
1821 for _modname in $(eval $_filtercmd); do
1822 case $_modname in
1823 *.ko) "$2" "$_modname" && echo "$_modname";;
1824 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1825 $2 $initdir/$$.ko && echo "$_modname"
1826 rm -f $initdir/$$.ko
1827 ;;
1828 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1829 $2 $initdir/$$.ko && echo "$_modname"
1830 rm -f $initdir/$$.ko
1831 ;;
1832 esac
1833 done
1834)
1835find_kernel_modules_by_path () (
1836 if ! [[ $hostonly ]]; then
1837 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1838 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1839 else
1840 cut -d " " -f 1 </proc/modules \
1841 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1842 fi
1843)
1844
1845filter_kernel_modules () {
1846 filter_kernel_modules_by_path drivers "$1"
1847}
1848
1849find_kernel_modules () {
1850 find_kernel_modules_by_path drivers
1851}
1852
1853# instmods [-c] <kernel module> [<kernel module> ... ]
1854# instmods [-c] <kernel subsystem>
1855# install kernel modules along with all their dependencies.
1856# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1857instmods() {
1858 [[ $no_kernel = yes ]] && return
1859 # called [sub]functions inherit _fderr
1860 local _fderr=9
1861 local _check=no
1862 if [[ $1 = '-c' ]]; then
1863 _check=yes
1864 shift
1865 fi
1866
1867 function inst1mod() {
1868 local _ret=0 _mod="$1"
1869 case $_mod in
1870 =*)
1871 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1872 ( [[ "$_mpargs" ]] && echo $_mpargs
1873 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1874 | instmods
1875 else
1876 ( [[ "$_mpargs" ]] && echo $_mpargs
e9638652 1877 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -type f -printf '%f\n' ) \
0d6e798a
HH
1878 | instmods
1879 fi
1880 ;;
1881 --*) _mpargs+=" $_mod" ;;
1882 i2o_scsi) return ;; # Do not load this diagnostic-only module
1883 *)
1884 _mod=${_mod##*/}
1885 # if we are already installed, skip this module and go on
1886 # to the next one.
1887 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1888
1889 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1890 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1891 return
1892 fi
1893 # If we are building a host-specific initramfs and this
1894 # module is not already loaded, move on to the next one.
1895 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1896 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1897 && return
1898
1899 # We use '-d' option in modprobe only if modules prefix path
1900 # differs from default '/'. This allows us to use Dracut with
1901 # old version of modprobe which doesn't have '-d' option.
1902 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1903 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1904
1905 # ok, load the module, all its dependencies, and any firmware
1906 # it may require
1907 for_each_kmod_dep install_kmod_with_fw $_mod \
1908 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1909 ((_ret+=$?))
1910 ;;
1911 esac
1912 return $_ret
1913 }
1914
1915 function instmods_1() {
1916 local _mod _mpargs
1917 if (($# == 0)); then # filenames from stdin
1918 while read _mod; do
1919 inst1mod "${_mod%.ko*}" || {
1920 if [ "$_check" = "yes" ]; then
1921 dfatal "Failed to install $_mod"
1922 return 1
1923 fi
1924 }
1925 done
1926 fi
1927 while (($# > 0)); do # filenames as arguments
1928 inst1mod ${1%.ko*} || {
1929 if [ "$_check" = "yes" ]; then
1930 dfatal "Failed to install $1"
1931 return 1
1932 fi
1933 }
1934 shift
1935 done
1936 return 0
1937 }
1938
1939 local _ret _filter_not_found='FATAL: Module .* not found.'
1940 set -o pipefail
1941 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1942 # redirections, but that would make dracut require bash4 at least.
1943 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1944 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1945 _ret=$?
1946 set +o pipefail
1947 return $_ret
1948}
898720b7 1949
85393d8f 1950setup_suse() {
caced732
FB
1951 ln -fs ../usr/bin/systemctl $initdir/bin/
1952 ln -fs ../usr/lib/systemd $initdir/lib/
85393d8f 1953 inst_simple "/usr/lib/systemd/system/haveged.service"
2aa5a13a 1954 instmods ext4
85393d8f
TB
1955}
1956
9e19a8b0
DS
1957_umount_dir() {
1958 if mountpoint -q $1; then
1959 ddebug "umount $1"
1960 umount $1
1961 fi
1962}
1963
ec4cab49
DS
1964# can be overridden in specific test
1965test_setup_cleanup() {
ec43f686 1966 cleanup_initdir
ec4cab49
DS
1967}
1968
1969_test_cleanup() {
f85bc044
DS
1970 # (post-test) cleanup should always ignore failure and cleanup as much as possible
1971 (
1972 set +e
1506edca 1973 _umount_dir $initdir
e8945092 1974 rm -vf "$IMAGE_PUBLIC"
8c3534b5
ZJS
1975 rm -vfr "$TESTDIR"
1976 rm -vf "$STATEFILE"
65dd488f 1977 ) || :
ec4cab49
DS
1978}
1979
054ee249
MP
1980# can be overridden in specific test
1981test_cleanup() {
ec4cab49 1982 _test_cleanup
054ee249
MP
1983}
1984
693ad298
ZJS
1985test_cleanup_again() {
1986 [ -n "$TESTDIR" ] || return
1987 rm -rf "$TESTDIR/unprivileged-nspawn-root"
1506edca 1988 _umount_dir $initdir
693ad298
ZJS
1989}
1990
8c3534b5 1991test_create_image() {
70ce817c
ZJS
1992 create_empty_image_rootdir
1993
1994 # Create what will eventually be our root filesystem onto an overlay
1995 (
1996 LOG_LEVEL=5
70ce817c
ZJS
1997 setup_basic_environment
1998 mask_supporting_services
1999 )
8c3534b5
ZJS
2000}
2001
2002test_setup() {
9d84eb20
ZJS
2003 if [ ${TEST_REQUIRE_INSTALL_TESTS} -ne 0 ] && \
2004 type -P meson >/dev/null && \
2005 [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
20f938ff 2006 dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
8c3534b5
ZJS
2007 exit 1
2008 fi
2009
e8945092
FS
2010 if [ -e "$IMAGE_PRIVATE" ]; then
2011 echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)"
8c3534b5 2012 mount_initdir
2991fa41
FS
2013 else
2014 if [ ! -e "$IMAGE_PUBLIC" ]; then
2015 # Create the backing public image, but then completely unmount
2016 # it and drop the loopback device responsible for it, since we're
2017 # going to symlink/copy the image and mount it again from
2018 # elsewhere.
2019 test_create_image
2020 test_setup_cleanup
2021 umount_loopback
2022 cleanup_loopdev
2023 fi
2024
e8945092 2025 echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)"
7a57256c
FS
2026 if [ ${TEST_PARALLELIZE} -ne 0 ]; then
2027 cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
2028 else
2029 ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
2030 fi
2991fa41 2031
8c3534b5 2032 mount_initdir
8c3534b5
ZJS
2033 fi
2034
70ce817c
ZJS
2035 setup_nspawn_root
2036}
2037
054ee249 2038test_run() {
4962ed9f 2039 mount_initdir
4962ed9f 2040
054ee249 2041 if [ -z "$TEST_NO_QEMU" ]; then
fe85f2bb 2042 if run_qemu "$1"; then
b74a3061 2043 check_result_qemu || { echo "QEMU test failed"; return 1; }
054ee249
MP
2044 else
2045 dwarn "can't run QEMU, skipping"
2046 fi
2047 fi
2048 if [ -z "$TEST_NO_NSPAWN" ]; then
ec43f686
ZJS
2049 mount_initdir
2050 if run_nspawn "$initdir" "$1"; then
2051 check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
054ee249
MP
2052 else
2053 dwarn "can't run systemd-nspawn, skipping"
2054 fi
746fbd9c
EV
2055
2056 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
ec43f686
ZJS
2057 dir="$TESTDIR/unprivileged-nspawn-root"
2058 if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "$dir" "$1"; then
2059 check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
746fbd9c
EV
2060 else
2061 dwarn "can't run systemd-nspawn, skipping"
2062 fi
d56db495 2063 fi
054ee249
MP
2064 fi
2065 return 0
2066}
2067
898720b7 2068do_test() {
33a5e20f
HH
2069 if [[ $UID != "0" ]]; then
2070 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
2071 exit 0
2072 fi
2073
cc5549ca 2074 # Detect lib paths
898720b7
HH
2075 [[ $libdir ]] || for libdir in /lib64 /lib; do
2076 [[ -d $libdir ]] && libdirs+=" $libdir" && break
2077 done
2078
2079 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
2080 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
2081 done
2082
22077c9c
MP
2083 mkdir -p "$STATEDIR"
2084
898720b7 2085 import_testdir
889a9042 2086 import_initdir
898720b7 2087
8af10ca3
ZJS
2088 testname="$(basename $PWD)"
2089
898720b7
HH
2090 while (($# > 0)); do
2091 case $1 in
2092 --run)
8af10ca3 2093 echo "${testname} RUN: $TEST_DESCRIPTION"
fe85f2bb 2094 test_run "$2"
0013fac2
YW
2095 ret=$?
2096 if (( $ret == 0 )); then
8af10ca3 2097 echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
898720b7 2098 else
8af10ca3 2099 echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
898720b7
HH
2100 fi
2101 exit $ret;;
2102 --setup)
8af10ca3 2103 echo "${testname} SETUP: $TEST_DESCRIPTION"
898720b7 2104 test_setup
ec4cab49 2105 test_setup_cleanup
818567fc 2106 ;;
693ad298 2107 --clean)
8af10ca3 2108 echo "${testname} CLEANUP: $TEST_DESCRIPTION"
898720b7 2109 test_cleanup
818567fc 2110 ;;
693ad298 2111 --clean-again)
8af10ca3 2112 echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
693ad298
ZJS
2113 test_cleanup_again
2114 ;;
898720b7 2115 --all)
818567fc 2116 ret=0
8af10ca3 2117 echo -n "${testname}: $TEST_DESCRIPTION "
898720b7 2118 (
ec4cab49
DS
2119 test_setup
2120 test_setup_cleanup
fe85f2bb 2121 test_run "$2"
818567fc 2122 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
f85bc044 2123 test_cleanup
898720b7 2124 if [ $ret -eq 0 ]; then
22077c9c 2125 rm "$TESTLOG"
898720b7
HH
2126 echo "[OK]"
2127 else
2128 echo "[FAILED]"
22077c9c 2129 echo "see $TESTLOG"
898720b7
HH
2130 fi
2131 exit $ret;;
2132 *) break ;;
2133 esac
2134 shift
2135 done
2136}