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