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