]>
Commit | Line | Data |
---|---|---|
ff12a795 | 1 | #!/usr/bin/env bash |
898720b7 | 2 | # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- |
8f5bcd61 ZJS |
3 | # SPDX-License-Identifier: LGPL-2.1-or-later |
4 | # | |
5 | # shellcheck disable=SC2030,SC2031 | |
96af59aa FS |
6 | # ex: ts=8 sw=4 sts=4 et filetype=sh tw=180 |
7 | # Note: the shellcheck line above disables warning for variables which were | |
8 | # modified in a subshell. In our case this behavior is expected, but | |
9 | # `shellcheck` can't distinguish this because of poor variable tracking, | |
10 | # which results in warning for every instance of such variable used | |
11 | # throughout this file. | |
12 | # See: | |
13 | # * comment in function install_verity_minimal() | |
14 | # * koalaman/shellcheck#280 | |
15 | set -o pipefail | |
16 | ||
898720b7 HH |
17 | PATH=/sbin:/bin:/usr/sbin:/usr/bin |
18 | export PATH | |
19 | ||
ae6c5987 | 20 | os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release) |
1b8fcd9c FS |
21 | # shellcheck source=/dev/null |
22 | source "$os_release" | |
23 | [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN="" | |
24 | [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH="" | |
25 | [[ " $ID_LIKE " = *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE="" | |
26 | KERNEL_VER="${KERNEL_VER-$(uname -r)}" | |
cd15f7f6 FS |
27 | QEMU_TIMEOUT="${QEMU_TIMEOUT:-1800}" |
28 | NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-1800}" | |
b2ecd099 | 29 | TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out |
4b742c8a | 30 | [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}" |
22f1f8f2 | 31 | UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}" |
5f28f3dd | 32 | EFI_MOUNT="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}" |
d9e606e8 LB |
33 | # Note that defining a different IMAGE_NAME in a test setup script will only result |
34 | # in default.img being copied and renamed. It can then be extended by defining | |
35 | # a test_append_files() function. The $1 parameter will be the root directory. | |
36 | # To force creating a new image from scratch (eg: to encrypt it), also define | |
37 | # TEST_FORCE_NEWIMAGE=1 in the test setup script. | |
8c3534b5 | 38 | IMAGE_NAME=${IMAGE_NAME:-default} |
e68e473b | 39 | STRIP_BINARIES="${STRIP_BINARIES:-yes}" |
9d84eb20 | 40 | TEST_REQUIRE_INSTALL_TESTS="${TEST_REQUIRE_INSTALL_TESTS:-1}" |
7a57256c | 41 | TEST_PARALLELIZE="${TEST_PARALLELIZE:-0}" |
508a7f04 | 42 | TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED="${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED:-1}" |
1506edca | 43 | LOOPDEV= |
898720b7 | 44 | |
23f8e019 FS |
45 | # Simple wrapper to unify boolean checks. |
46 | # Note: this function needs to stay near the top of the file, so we can use it | |
47 | # in code in the outermost scope. | |
48 | get_bool() { | |
49 | # Make the value lowercase to make the regex matching simpler | |
50 | local _bool="${1,,}" | |
51 | ||
52 | # Consider empty value as "false" | |
53 | if [[ -z "$_bool" || "$_bool" =~ ^(0|no|false)$ ]]; then | |
54 | return 1 | |
55 | elif [[ "$_bool" =~ ^(1|yes|true)$ ]]; then | |
56 | return 0 | |
57 | else | |
58 | echo >&2 "Value '$_bool' is not a valid boolean value" | |
59 | exit 1 | |
60 | fi | |
61 | } | |
62 | ||
b92c3df2 FS |
63 | # Since in Bash we can have only one handler per signal, let's overcome this |
64 | # limitation by having one global handler for the EXIT signal which executes | |
65 | # all registered handlers | |
66 | _AT_EXIT_HANDLERS=() | |
67 | _at_exit() { | |
68 | set +e | |
69 | ||
70 | # Run the EXIT handlers in reverse order | |
71 | for ((i = ${#_AT_EXIT_HANDLERS[@]} - 1; i >= 0; i--)); do | |
72 | ddebug "Running EXIT handler '${_AT_EXIT_HANDLERS[$i]}'" | |
73 | "${_AT_EXIT_HANDLERS[$i]}" | |
74 | done | |
75 | } | |
76 | ||
77 | trap _at_exit EXIT | |
78 | ||
79 | add_at_exit_handler() { | |
80 | local handler="${1?}" | |
81 | ||
82 | if [[ "$(type -t "$handler")" != "function" ]]; then | |
83 | dfatal "'$handler' is not a function" | |
84 | exit 1 | |
85 | fi | |
86 | ||
87 | _AT_EXIT_HANDLERS+=("$handler") | |
88 | } | |
89 | ||
3e8caa34 | 90 | # Decide if we can (and want to) run qemu with KVM acceleration. |
501deda1 FS |
91 | # Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not, |
92 | # check if it's not explicitly disabled (TEST_NO_KVM) and we're not already | |
93 | # running under KVM. If these conditions are met, enable KVM (and possibly | |
94 | # nested KVM), otherwise disable it. | |
12ee072d | 95 | if get_bool "${TEST_NESTED_KVM:=}" || (! get_bool "${TEST_NO_KVM:=}" && ! systemd-detect-virt -qv); then |
501deda1 FS |
96 | QEMU_KVM=yes |
97 | else | |
98 | QEMU_KVM=no | |
99 | fi | |
100 | ||
3486cb6c MP |
101 | if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then |
102 | echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2 | |
103 | ROOTLIBDIR=/usr/lib/systemd | |
104 | fi | |
105 | ||
42f3b48c | 106 | # The calling test.sh scripts have TEST_BASE_DIR set via their Makefile, but we don't need them to provide it |
1b8fcd9c | 107 | TEST_BASE_DIR=${TEST_BASE_DIR:-$(realpath "$(dirname "${BASH_SOURCE[0]}")")} |
c4cd6205 | 108 | TEST_UNITS_DIR="$(realpath "$TEST_BASE_DIR/units")" |
42f3b48c DS |
109 | SOURCE_DIR=$(realpath "$TEST_BASE_DIR/..") |
110 | TOOLS_DIR="$SOURCE_DIR/tools" | |
1b8fcd9c FS |
111 | # These variables are used by test scripts |
112 | export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR | |
42f3b48c | 113 | |
12d31e4e | 114 | # note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it |
dfd73ccb FB |
115 | if get_bool "${NO_BUILD:=}"; then |
116 | BUILD_DIR="$SOURCE_DIR" | |
117 | elif ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then | |
118 | echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2 | |
119 | exit 1 | |
12d31e4e DS |
120 | fi |
121 | ||
a33e2692 FS |
122 | PATH_TO_INIT="$ROOTLIBDIR/systemd" |
123 | SYSTEMD_JOURNALD="${SYSTEMD_JOURNALD:-$(command -v "$BUILD_DIR/systemd-journald" || command -v "$ROOTLIBDIR/systemd-journald")}" | |
cca3050b | 124 | SYSTEMD_JOURNAL_REMOTE="${SYSTEMD_JOURNAL_REMOTE:-$(command -v "$BUILD_DIR/systemd-journal-remote" || command -v "$ROOTLIBDIR/systemd-journal-remote" || echo "")}" |
a33e2692 FS |
125 | SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR/systemd")}" |
126 | SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}" | |
127 | JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}" | |
354b3364 | 128 | SYSTEMCTL="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}" |
016fa3b9 | 129 | |
1b8fcd9c | 130 | TESTFILE="${BASH_SOURCE[1]}" |
42f3b48c DS |
131 | if [ -z "$TESTFILE" ]; then |
132 | echo "ERROR: test-functions must be sourced from one of the TEST-*/test.sh scripts" >&2 | |
133 | exit 1 | |
134 | fi | |
1b8fcd9c | 135 | TESTNAME="$(basename "$(dirname "$(realpath "$TESTFILE")")")" |
42f3b48c | 136 | STATEDIR="$BUILD_DIR/test/$TESTNAME" |
19184069 DS |
137 | STATEFILE="$STATEDIR/.testdir" |
138 | IMAGESTATEDIR="$STATEDIR/.." | |
139 | TESTLOG="$STATEDIR/test.log" | |
140 | ||
c4cd6205 FS |
141 | if ! [[ "$TESTNAME" =~ ^TEST\-([0-9]+)\-.+$ ]]; then |
142 | echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2 | |
143 | exit 1 | |
144 | fi | |
145 | TESTID="${BASH_REMATCH[1]:?}" | |
146 | ||
147 | if [[ ! -f "$TEST_UNITS_DIR/testsuite-$TESTID.service" ]]; then | |
148 | echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/testsuite-$TESTID.service" >&2 | |
149 | exit 1 | |
150 | fi | |
151 | ||
4110a6de | 152 | BASICTOOLS=( |
f4c40fd7 | 153 | awk |
e7848266 | 154 | base64 |
f4c40fd7 | 155 | basename |
4110a6de | 156 | bash |
f4c40fd7 | 157 | capsh |
4110a6de ZJS |
158 | cat |
159 | chmod | |
160 | chown | |
161 | cmp | |
162 | cryptsetup | |
f4c40fd7 | 163 | cut |
4110a6de | 164 | date |
f4c40fd7 ZJS |
165 | dd |
166 | diff | |
167 | dirname | |
4110a6de ZJS |
168 | dmsetup |
169 | echo | |
170 | env | |
171 | false | |
41d1aaea | 172 | flock |
e4ff8040 | 173 | getconf |
f4c40fd7 ZJS |
174 | getent |
175 | getfacl | |
e29e4d57 | 176 | grep |
f4c40fd7 | 177 | gunzip |
4110a6de ZJS |
178 | gzip |
179 | head | |
f4c40fd7 | 180 | ionice |
e5badaf3 | 181 | ip |
7906b790 | 182 | jq |
5c08efee | 183 | killall |
42867dfe | 184 | ldd |
4110a6de ZJS |
185 | ln |
186 | loadkeys | |
187 | login | |
92ecc875 | 188 | losetup |
f4c40fd7 | 189 | lz4cat |
c0b97b0f ZJS |
190 | mkfifo |
191 | mktemp | |
4110a6de ZJS |
192 | modprobe |
193 | mount | |
e47add9e | 194 | mountpoint |
d10029bb ZJS |
195 | mv |
196 | nc | |
f4c40fd7 | 197 | nproc |
9c94ab0f | 198 | pkill |
c0b97b0f | 199 | readlink |
bf9b5691 | 200 | realpath |
f4c40fd7 | 201 | rev |
4110a6de | 202 | rm |
e5badaf3 | 203 | rmdir |
9c94ab0f | 204 | rmmod |
4110a6de | 205 | sed |
f4c40fd7 | 206 | seq |
59331b8e | 207 | setfattr |
4110a6de ZJS |
208 | setfont |
209 | setsid | |
e29e4d57 | 210 | sfdisk |
4110a6de ZJS |
211 | sh |
212 | sleep | |
d10029bb | 213 | stat |
d0ac89a1 | 214 | su |
4110a6de | 215 | sulogin |
e5badaf3 | 216 | sysctl |
4110a6de | 217 | tail |
6e796683 | 218 | tar |
4110a6de ZJS |
219 | tee |
220 | test | |
d446ae89 | 221 | timeout |
3ac189d8 | 222 | touch |
f4c40fd7 | 223 | tr |
4110a6de | 224 | true |
e29e4d57 | 225 | truncate |
4110a6de | 226 | umount |
f4c40fd7 | 227 | uname |
e5badaf3 | 228 | unshare |
9c94ab0f YW |
229 | useradd |
230 | userdel | |
9fbb13ac | 231 | wc |
4110a6de | 232 | xargs |
f4c40fd7 | 233 | xzcat |
4110a6de ZJS |
234 | ) |
235 | ||
236 | DEBUGTOOLS=( | |
237 | cp | |
238 | df | |
239 | dhclient | |
240 | dmesg | |
241 | du | |
242 | find | |
243 | free | |
244 | grep | |
245 | hostname | |
246 | id | |
4110a6de ZJS |
247 | less |
248 | ln | |
249 | ls | |
250 | mkdir | |
4110a6de ZJS |
251 | ping |
252 | ps | |
253 | route | |
254 | sort | |
255 | strace | |
256 | stty | |
4110a6de ZJS |
257 | tty |
258 | vi | |
8afe2f53 | 259 | /usr/libexec/vi |
4110a6de | 260 | ) |
889a9042 | 261 | |
ec9181d2 | 262 | is_built_with_asan() { |
b727d7e0 FS |
263 | local _bin="${1:?}" |
264 | ||
ec9181d2 EV |
265 | if ! type -P objdump >/dev/null; then |
266 | ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN." | |
267 | return 1 | |
268 | fi | |
269 | ||
270 | # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182 | |
1b8fcd9c | 271 | local _asan_calls |
b727d7e0 | 272 | _asan_calls="$(objdump -dC "$_bin" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)" |
1b8fcd9c | 273 | if ((_asan_calls < 1000)); then |
ec9181d2 EV |
274 | return 1 |
275 | else | |
276 | return 0 | |
277 | fi | |
278 | } | |
279 | ||
7a6c5b6a FS |
280 | is_built_with_coverage() { |
281 | if get_bool "${NO_BUILD:=}" || ! command -v meson >/dev/null; then | |
282 | return 1 | |
283 | fi | |
284 | ||
285 | meson configure "${BUILD_DIR:?}" | grep 'b_coverage' | awk '{ print $2 }' | grep -q 'true' | |
286 | } | |
287 | ||
b727d7e0 | 288 | IS_BUILT_WITH_ASAN=$(is_built_with_asan "$SYSTEMD_JOURNALD" && echo yes || echo no) |
7a6c5b6a | 289 | IS_BUILT_WITH_COVERAGE=$(is_built_with_coverage && echo yes || echo no) |
ec9181d2 | 290 | |
23f8e019 | 291 | if get_bool "$IS_BUILT_WITH_ASAN"; then |
ec9181d2 | 292 | STRIP_BINARIES=no |
54a3790c | 293 | SKIP_INITRD="${SKIP_INITRD:-yes}" |
016fa3b9 | 294 | PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan |
dc350e78 | 295 | QEMU_MEM="${QEMU_MEM:-2G}" |
648fd189 | 296 | QEMU_SMP="${QEMU_SMP:-4}" |
37ee8dc8 FS |
297 | |
298 | # We need to correctly distinguish between gcc's and clang's ASan DSOs. | |
d49b881e | 299 | if ASAN_RT_NAME="$(awk '/libasan.so/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then |
37ee8dc8 | 300 | ASAN_COMPILER=gcc |
648fd189 | 301 | ASAN_RT_PATH="$(readlink -f "$(${CC:-gcc} --print-file-name "$ASAN_RT_NAME")")" |
d49b881e | 302 | elif ASAN_RT_NAME="$(awk '/libclang_rt.asan/ {x=$1; exit} END {print x; exit x==""}' < <(ldd "$SYSTEMD"))"; then |
37ee8dc8 | 303 | ASAN_COMPILER=clang |
648fd189 | 304 | ASAN_RT_PATH="$(readlink -f "$(${CC:-clang} --print-file-name "$ASAN_RT_NAME")")" |
37ee8dc8 FS |
305 | |
306 | # As clang's ASan DSO is usually in a non-standard path, let's check if | |
307 | # the environment is set accordingly. If not, warn the user and exit. | |
308 | # We're not setting the LD_LIBRARY_PATH automagically here, because | |
309 | # user should encounter (and fix) the same issue when running the unit | |
310 | # tests (meson test) | |
84c49ad1 | 311 | if ldd "$SYSTEMD" | grep -q "libclang_rt.asan.*not found"; then |
648fd189 FS |
312 | echo >&2 "clang's ASan DSO ($ASAN_RT_NAME) is not present in the runtime library path" |
313 | echo >&2 "Consider setting LD_LIBRARY_PATH=${ASAN_RT_PATH%/*}" | |
37ee8dc8 FS |
314 | exit 1 |
315 | fi | |
316 | else | |
317 | echo >&2 "systemd is not linked against the ASan DSO" | |
318 | echo >&2 "gcc does this by default, for clang compile with -shared-libasan" | |
319 | exit 1 | |
320 | fi | |
648fd189 FS |
321 | |
322 | echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'" | |
ec9181d2 EV |
323 | fi |
324 | ||
1b8fcd9c FS |
325 | find_qemu_bin() { |
326 | QEMU_BIN="${QEMU_BIN:-""}" | |
3e7aa2ed | 327 | # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm. |
23f8e019 | 328 | if get_bool "$QEMU_KVM"; then |
1b8fcd9c | 329 | [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)" |
3e7aa2ed | 330 | fi |
c6a77179 | 331 | |
1b8fcd9c | 332 | [[ -n "$ARCH" ]] || ARCH="$(uname -m)" |
c6a77179 RC |
333 | case $ARCH in |
334 | x86_64) | |
335 | # QEMU's own build system calls it qemu-system-x86_64 | |
1b8fcd9c | 336 | [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)" |
c6a77179 RC |
337 | ;; |
338 | i*86) | |
339 | # new i386 version of QEMU | |
1b8fcd9c | 340 | [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)" |
c6a77179 RC |
341 | |
342 | # i386 version of QEMU | |
1b8fcd9c | 343 | [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu 2>/dev/null | grep '^/' -m1)" |
c6a77179 | 344 | ;; |
cf5f9bb8 | 345 | ppc64*) |
1b8fcd9c | 346 | [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)" |
cf5f9bb8 | 347 | ;; |
c6a77179 RC |
348 | esac |
349 | ||
1b8fcd9c | 350 | if [[ ! -e "$QEMU_BIN" ]]; then |
3e8caa34 | 351 | echo "Could not find a suitable qemu binary" >&2 |
c6a77179 RC |
352 | return 1 |
353 | fi | |
354 | } | |
355 | ||
43b49470 CE |
356 | # Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu |
357 | # returns 0 if newer or equal | |
358 | # returns 1 if older | |
359 | # returns 2 if failing | |
1b8fcd9c | 360 | qemu_min_version() { |
43b49470 CE |
361 | find_qemu_bin || return 2 |
362 | ||
363 | # get version from binary | |
1b8fcd9c FS |
364 | local qemu_ver |
365 | qemu_ver="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')" | |
43b49470 CE |
366 | |
367 | # Check version string format | |
368 | echo "$qemu_ver" | grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' || return 2 | |
369 | echo "$1" | grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' || return 2 | |
370 | ||
371 | # compare as last command to return that value | |
372 | printf "%s\n%s\n" "$1" "$qemu_ver" | sort -V -C | |
373 | } | |
374 | ||
3e8caa34 ZJS |
375 | # Return 0 if qemu did run (then you must check the result state/logs for actual |
376 | # success), or 1 if qemu is not available. | |
889a9042 | 377 | run_qemu() { |
b22d90e5 FS |
378 | # If the test provided its own initrd, use it (e.g. TEST-24) |
379 | if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then | |
380 | INITRD="$TESTDIR/initrd.img" | |
381 | fi | |
382 | ||
b6f0c419 | 383 | if [ -f /etc/machine-id ]; then |
1b8fcd9c | 384 | read -r MACHINE_ID </etc/machine-id |
906bbac4 ZJS |
385 | [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \ |
386 | && INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" | |
387 | [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \ | |
388 | && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" | |
b6f0c419 HH |
389 | fi |
390 | ||
1b8fcd9c | 391 | local CONSOLE=ttyS0 |
9a2e265b | 392 | |
80b44b38 | 393 | rm -f "$initdir"/{testok,failed,skipped} |
ec43f686 ZJS |
394 | # make sure the initdir is not mounted to avoid concurrent access |
395 | cleanup_initdir | |
396 | umount_loopback | |
397 | ||
e1a27318 | 398 | if [[ ! "$KERNEL_BIN" ]]; then |
23f8e019 | 399 | if get_bool "$LOOKS_LIKE_ARCH"; then |
e1a27318 EV |
400 | KERNEL_BIN=/boot/vmlinuz-linux |
401 | else | |
eaa602cb DJL |
402 | [ "$ARCH" ] || ARCH=$(uname -m) |
403 | case $ARCH in | |
404 | ppc64*) | |
46db176f FS |
405 | # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS |
406 | # uses the "standard" vmlinuz- prefix | |
407 | [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER" | |
408 | CONSOLE=hvc0 | |
409 | ;; | |
eaa602cb | 410 | *) |
46db176f FS |
411 | KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER" |
412 | ;; | |
eaa602cb | 413 | esac |
e1a27318 EV |
414 | fi |
415 | fi | |
416 | ||
1b8fcd9c FS |
417 | local default_fedora_initrd="/boot/initramfs-${KERNEL_VER}.img" |
418 | local default_debian_initrd="/boot/initrd.img-${KERNEL_VER}" | |
419 | local default_arch_initrd="/boot/initramfs-linux-fallback.img" | |
420 | local default_suse_initrd="/boot/initrd-${KERNEL_VER}" | |
e1a27318 EV |
421 | if [[ ! "$INITRD" ]]; then |
422 | if [[ -e "$default_fedora_initrd" ]]; then | |
423 | INITRD="$default_fedora_initrd" | |
424 | elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then | |
425 | INITRD="$default_debian_initrd" | |
426 | elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then | |
427 | INITRD="$default_arch_initrd" | |
85393d8f TB |
428 | elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then |
429 | INITRD="$default_suse_initrd" | |
e1a27318 EV |
430 | fi |
431 | fi | |
432 | ||
5bfb2a93 FS |
433 | # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically' |
434 | # i.e. use the number of online CPUs on the host machine. If the nproc utility | |
435 | # is not installed or there's some other error when calling it, fall back | |
436 | # to the original value (QEMU_SMP=1). | |
1b8fcd9c | 437 | if [[ -z "${QEMU_SMP:=}" ]]; then |
5bfb2a93 FS |
438 | if ! QEMU_SMP=$(nproc); then |
439 | dwarn "nproc utility is not installed, falling back to QEMU_SMP=1" | |
440 | QEMU_SMP=1 | |
441 | fi | |
442 | fi | |
c6a77179 RC |
443 | |
444 | find_qemu_bin || return 1 | |
445 | ||
8c3534b5 | 446 | # Umount initdir to avoid concurrent access to the filesystem |
0f194705 FS |
447 | _umount_dir "$initdir" |
448 | ||
449 | local kernel_params=() | |
450 | local qemu_options=() | |
451 | local qemu_cmd=("$QEMU_BIN") | |
8c3534b5 | 452 | |
22f1f8f2 | 453 | if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then |
0f194705 | 454 | kernel_params+=("systemd.unified_cgroup_hierarchy=yes") |
22f1f8f2 | 455 | elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then |
0f194705 | 456 | kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes") |
22f1f8f2 | 457 | elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then |
0f194705 | 458 | kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no") |
22f1f8f2 EV |
459 | elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then |
460 | dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]" | |
461 | exit 1 | |
462 | fi | |
463 | ||
23f8e019 | 464 | if get_bool "$LOOKS_LIKE_SUSE"; then |
0f194705 | 465 | kernel_params+=("rd.hostonly=0") |
cc5549ca | 466 | fi |
85393d8f | 467 | |
0f194705 | 468 | kernel_params+=( |
bac05644 | 469 | "root=LABEL=systemd_boot" |
0f194705 FS |
470 | "rw" |
471 | "raid=noautodetect" | |
472 | "rd.luks=0" | |
473 | "loglevel=2" | |
474 | "init=$PATH_TO_INIT" | |
475 | "console=$CONSOLE" | |
0f194705 FS |
476 | "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:" |
477 | "systemd.unit=testsuite.target" | |
478 | "systemd.wants=testsuite-$1.service" | |
479 | ) | |
480 | ||
23f8e019 | 481 | if ! get_bool "$INTERACTIVE_DEBUG"; then |
842a9d5f FS |
482 | kernel_params+=( |
483 | "oops=panic" | |
484 | "panic=1" | |
485 | "softlockup_panic=1" | |
486 | "systemd.wants=end.service" | |
487 | ) | |
fe85f2bb ZJS |
488 | fi |
489 | ||
e8945092 | 490 | [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC" |
0f194705 FS |
491 | qemu_options+=( |
492 | -smp "$QEMU_SMP" | |
493 | -net none | |
6a9c4977 | 494 | -m "${QEMU_MEM:-768M}" |
0f194705 FS |
495 | -nographic |
496 | -kernel "$KERNEL_BIN" | |
497 | -drive "format=raw,cache=unsafe,file=$image" | |
4d5d28e9 | 498 | -device "virtio-rng-pci,max-bytes=1024,period=1000" |
0f194705 FS |
499 | ) |
500 | ||
501 | if [[ -n "${QEMU_OPTIONS:=}" ]]; then | |
502 | local user_qemu_options | |
503 | read -ra user_qemu_options <<< "$QEMU_OPTIONS" | |
504 | qemu_options+=("${user_qemu_options[@]}") | |
505 | fi | |
506 | ||
507 | if [[ -n "${KERNEL_APPEND:=}" ]]; then | |
508 | local user_kernel_append | |
bea9d62b | 509 | readarray user_kernel_append <<< "$KERNEL_APPEND" |
0f194705 FS |
510 | kernel_params+=("${user_kernel_append[@]}") |
511 | fi | |
c6a77179 | 512 | |
23f8e019 | 513 | if [[ "$INITRD" ]] && ! get_bool "$SKIP_INITRD"; then |
0f194705 | 514 | qemu_options+=(-initrd "$INITRD") |
c6a77179 RC |
515 | fi |
516 | ||
501deda1 | 517 | # Let's use KVM if possible |
23f8e019 | 518 | if [[ -c /dev/kvm ]] && get_bool $QEMU_KVM; then |
0f194705 | 519 | qemu_options+=(-machine "accel=kvm" -enable-kvm -cpu host) |
dbf43a42 DM |
520 | fi |
521 | ||
91f9f8f1 | 522 | if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then |
0f194705 | 523 | qemu_cmd=(timeout --foreground "$QEMU_TIMEOUT" "$QEMU_BIN") |
91f9f8f1 | 524 | fi |
0f194705 FS |
525 | |
526 | (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]}") | |
b2ecd099 | 527 | rc=$? |
23f8e019 FS |
528 | if [ "$rc" -eq 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then |
529 | derror "Test timed out after ${QEMU_TIMEOUT}s" | |
b2ecd099 MP |
530 | TIMED_OUT=1 |
531 | else | |
3e8caa34 | 532 | [ "$rc" != 0 ] && derror "qemu failed with exit code $rc" |
b2ecd099 MP |
533 | fi |
534 | return 0 | |
889a9042 RC |
535 | } |
536 | ||
7cad32bb MP |
537 | # Return 0 if nspawn did run (then you must check the result state/logs for actual |
538 | # success), or 1 if nspawn is not available. | |
889a9042 | 539 | run_nspawn() { |
7cad32bb | 540 | [[ -d /run/systemd/system ]] || return 1 |
0f194705 FS |
541 | rm -f "${initdir:?}"/{testok,failed,skipped} |
542 | ||
543 | local nspawn_cmd=() | |
544 | local nspawn_options=( | |
545 | "--register=no" | |
546 | "--kill-signal=SIGKILL" | |
547 | "--directory=${1:?}" | |
548 | "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:" | |
10d7ed12 | 549 | "--machine=TEST-$TESTID" |
0f194705 FS |
550 | ) |
551 | local kernel_params=( | |
552 | "$PATH_TO_INIT" | |
553 | "systemd.unit=testsuite.target" | |
554 | "systemd.wants=testsuite-$2.service" | |
fe85f2bb ZJS |
555 | ) |
556 | ||
23f8e019 | 557 | if ! get_bool "$INTERACTIVE_DEBUG"; then |
0f194705 | 558 | kernel_params+=("systemd.wants=end.service") |
fe85f2bb ZJS |
559 | fi |
560 | ||
0f194705 FS |
561 | if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then |
562 | local user_nspawn_arguments | |
563 | read -ra user_nspawn_arguments <<< "$NSPAWN_ARGUMENTS" | |
564 | nspawn_options+=("${user_nspawn_arguments[@]}") | |
565 | fi | |
566 | ||
567 | if [[ -n "${KERNEL_APPEND:=}" ]]; then | |
568 | local user_kernel_append | |
bea9d62b | 569 | readarray user_kernel_append <<< "$KERNEL_APPEND" |
0f194705 | 570 | kernel_params+=("${user_kernel_append[@]}") |
43bbb8f0 | 571 | fi |
856ca72b | 572 | |
22f1f8f2 | 573 | if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then |
c78c095b | 574 | dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping" |
22f1f8f2 EV |
575 | exit |
576 | elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then | |
0f194705 | 577 | nspawn_cmd+=(env "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY") |
22f1f8f2 | 578 | elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then |
0f194705 | 579 | nspawn_cmd+=(env "--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY") |
22f1f8f2 EV |
580 | else |
581 | dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]" | |
582 | exit 1 | |
583 | fi | |
856ca72b | 584 | |
0f194705 FS |
585 | if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then |
586 | nspawn_cmd+=(timeout --foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN") | |
587 | else | |
588 | nspawn_cmd+=("$SYSTEMD_NSPAWN") | |
589 | fi | |
590 | ||
591 | (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}") | |
b2ecd099 | 592 | rc=$? |
23f8e019 FS |
593 | if [ "$rc" -eq 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then |
594 | derror "Test timed out after ${NSPAWN_TIMEOUT}s" | |
b2ecd099 MP |
595 | TIMED_OUT=1 |
596 | else | |
597 | [ "$rc" != 0 ] && derror "nspawn failed with exit code $rc" | |
598 | fi | |
599 | return 0 | |
889a9042 RC |
600 | } |
601 | ||
9785c44d LB |
602 | # Build two very minimal root images, with two units, one is the same and one is different across them |
603 | install_verity_minimal() { | |
1b8fcd9c FS |
604 | dinfo "Set up a set of minimal images for verity verification" |
605 | if [ -e "$initdir/usr/share/minimal.raw" ]; then | |
9785c44d LB |
606 | return |
607 | fi | |
608 | if ! command -v mksquashfs >/dev/null 2>&1; then | |
609 | dfatal "mksquashfs not found" | |
610 | exit 1 | |
611 | fi | |
612 | if ! command -v veritysetup >/dev/null 2>&1; then | |
613 | dfatal "veritysetup not found" | |
614 | exit 1 | |
615 | fi | |
1b8fcd9c FS |
616 | # Local modifications of some global variables is intentional in this |
617 | # subshell (SC2030) | |
618 | # shellcheck disable=SC2030 | |
9785c44d LB |
619 | ( |
620 | BASICTOOLS=( | |
621 | bash | |
622 | cat | |
93f59701 | 623 | grep |
9785c44d LB |
624 | mount |
625 | sleep | |
626 | ) | |
1b8fcd9c FS |
627 | oldinitdir="$initdir" |
628 | rm -rfv "$TESTDIR/minimal" | |
629 | export initdir="$TESTDIR/minimal" | |
3fa80e5e LB |
630 | # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image |
631 | mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1" | |
9785c44d LB |
632 | setup_basic_dirs |
633 | install_basic_tools | |
4163c877 | 634 | install_ld_so_conf |
1b8fcd9c FS |
635 | # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different |
636 | # issue, thus falsely triggering SC2030 in this case | |
637 | # See: koalaman/shellcheck#1409 | |
9f6235e1 FS |
638 | if [[ -v ASAN_RT_PATH ]]; then |
639 | # If we're compiled with ASan, install the ASan RT (and its dependencies) | |
640 | # into the verity images to get rid of the annoying errors about | |
641 | # missing $LD_PRELOAD libraries. | |
642 | inst_libs "$ASAN_RT_PATH" | |
643 | inst_library "$ASAN_RT_PATH" | |
644 | fi | |
1b8fcd9c FS |
645 | cp "$os_release" "$initdir/usr/lib/os-release" |
646 | ln -s ../usr/lib/os-release "$initdir/etc/os-release" | |
647 | touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf" | |
648 | touch "$initdir/opt/some_file" | |
649 | echo MARKER=1 >>"$initdir/usr/lib/os-release" | |
d76f0de7 LB |
650 | echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release" |
651 | cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF | |
652 | [Service] | |
653 | ExecStartPre=cat /usr/lib/os-release | |
654 | ExecStart=sleep 120 | |
655 | EOF | |
656 | cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service" | |
1b8fcd9c | 657 | |
4f2dba98 | 658 | mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend |
1b8fcd9c FS |
659 | veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \ |
660 | grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash" | |
661 | ||
662 | sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release" | |
d76f0de7 LB |
663 | rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service" |
664 | cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service" | |
1b8fcd9c | 665 | |
4f2dba98 | 666 | mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend |
1b8fcd9c FS |
667 | veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \ |
668 | grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash" | |
93f59701 LB |
669 | |
670 | # Rolling distros like Arch do not set VERSION_ID | |
671 | local version_id="" | |
1b8fcd9c FS |
672 | if grep -q "^VERSION_ID=" "$os_release"; then |
673 | version_id="$(grep "^VERSION_ID=" "$os_release")" | |
93f59701 LB |
674 | fi |
675 | ||
1b8fcd9c FS |
676 | export initdir="$TESTDIR/app0" |
677 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" | |
678 | grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0" | |
679 | echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0" | |
680 | cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF | |
93f59701 LB |
681 | [Service] |
682 | Type=oneshot | |
683 | RemainAfterExit=yes | |
684 | ExecStart=/opt/script0.sh | |
3fa80e5e LB |
685 | TemporaryFileSystem=/var/lib |
686 | StateDirectory=app0 | |
687 | RuntimeDirectory=app0 | |
93f59701 | 688 | EOF |
1b8fcd9c | 689 | cat >"$initdir/opt/script0.sh" <<EOF |
93f59701 LB |
690 | #!/bin/bash |
691 | set -e | |
692 | test -e /usr/lib/os-release | |
3fa80e5e | 693 | echo bar > \${STATE_DIRECTORY}/foo |
93f59701 LB |
694 | cat /usr/lib/extension-release.d/extension-release.app0 |
695 | EOF | |
1b8fcd9c FS |
696 | chmod +x "$initdir/opt/script0.sh" |
697 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" | |
4f2dba98 | 698 | mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend |
1b8fcd9c FS |
699 | |
700 | export initdir="$TESTDIR/app1" | |
701 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt" | |
9a4b883b | 702 | grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2" |
9ead4184 LP |
703 | ( echo "${version_id}" |
704 | echo "SYSEXT_SCOPE=portable" | |
705 | echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2" | |
9a4b883b | 706 | setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2" |
1b8fcd9c | 707 | cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF |
93f59701 LB |
708 | [Service] |
709 | Type=oneshot | |
710 | RemainAfterExit=yes | |
711 | ExecStart=/opt/script1.sh | |
3fa80e5e LB |
712 | StateDirectory=app1 |
713 | RuntimeDirectory=app1 | |
93f59701 | 714 | EOF |
1b8fcd9c | 715 | cat >"$initdir/opt/script1.sh" <<EOF |
93f59701 LB |
716 | #!/bin/bash |
717 | set -e | |
718 | test -e /usr/lib/os-release | |
3fa80e5e | 719 | echo baz > \${STATE_DIRECTORY}/foo |
9a4b883b | 720 | cat /usr/lib/extension-release.d/extension-release.app2 |
93f59701 | 721 | EOF |
1b8fcd9c FS |
722 | chmod +x "$initdir/opt/script1.sh" |
723 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file" | |
4f2dba98 | 724 | mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend |
ab4d43c5 KL |
725 | |
726 | export initdir="$TESTDIR/app-nodistro" | |
727 | mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" | |
16c1ca0d KL |
728 | ( echo "ID=_any" |
729 | echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" | |
ab4d43c5 KL |
730 | echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" |
731 | mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend | |
9785c44d LB |
732 | ) |
733 | } | |
734 | ||
889a9042 RC |
735 | setup_basic_environment() { |
736 | # create the basic filesystem layout | |
737 | setup_basic_dirs | |
738 | ||
739 | install_systemd | |
740 | install_missing_libraries | |
741 | install_config_files | |
f4c40fd7 | 742 | install_zoneinfo |
889a9042 RC |
743 | create_rc_local |
744 | install_basic_tools | |
745 | install_libnss | |
746 | install_pam | |
747 | install_dbus | |
748 | install_fonts | |
4ce68ea9 | 749 | install_locales |
889a9042 | 750 | install_keymaps |
1136175c | 751 | install_x11_keymaps |
889a9042 RC |
752 | install_terminfo |
753 | install_execs | |
0dd77c15 ZJS |
754 | install_fs_tools |
755 | install_modules | |
889a9042 | 756 | install_plymouth |
d93857ae | 757 | install_haveged |
889a9042 RC |
758 | install_debug_tools |
759 | install_ld_so_conf | |
d0ac89a1 ZJS |
760 | install_testuser |
761 | has_user_dbus_socket && install_user_dbus | |
e3ce42e7 | 762 | setup_selinux |
889a9042 | 763 | strip_binaries |
00d6fcee | 764 | instmods veth |
889a9042 RC |
765 | install_depmod_files |
766 | generate_module_dependencies | |
23f8e019 | 767 | if get_bool "$IS_BUILT_WITH_ASAN"; then |
37ee8dc8 | 768 | create_asan_wrapper |
ec9181d2 | 769 | fi |
23f8e019 | 770 | if get_bool "$TEST_INSTALL_VERITY_MINIMAL"; then |
9785c44d LB |
771 | install_verity_minimal |
772 | fi | |
889a9042 RC |
773 | } |
774 | ||
e3ce42e7 | 775 | setup_selinux() { |
1b8fcd9c | 776 | dinfo "Setup SELinux" |
e3ce42e7 | 777 | # don't forget KERNEL_APPEND='... selinux=1 ...' |
23f8e019 | 778 | if ! get_bool "$SETUP_SELINUX"; then |
1b8fcd9c | 779 | dinfo "SETUP_SELINUX != yes, skipping SELinux configuration" |
e3ce42e7 EV |
780 | return 0 |
781 | fi | |
e3ce42e7 | 782 | |
1b8fcd9c FS |
783 | local conf_dir=/etc/selinux |
784 | local fixfiles_tools=(bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles) | |
785 | ||
786 | # Make sure the following statement can't expand to "/" to prevent | |
787 | # a potential where-are-my-backups situation | |
788 | rm -rf "${initdir:?}/$conf_dir" | |
789 | if ! cp -ar "$conf_dir" "$initdir/$conf_dir"; then | |
790 | dfatal "Failed to copy $conf_dir" | |
e3ce42e7 EV |
791 | exit 1 |
792 | fi | |
793 | ||
1b8fcd9c FS |
794 | touch "$initdir/.autorelabel" |
795 | mkdir -p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants" | |
796 | ln -sf ../autorelabel.service "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/" | |
e3ce42e7 | 797 | |
39e17536 FS |
798 | image_install "${fixfiles_tools[@]}" |
799 | image_install fixfiles | |
800 | image_install sestatus | |
e3ce42e7 EV |
801 | } |
802 | ||
a2fbff31 EV |
803 | install_valgrind() { |
804 | if ! type -p valgrind; then | |
805 | dfatal "Failed to install valgrind" | |
806 | exit 1 | |
807 | fi | |
808 | ||
1b8fcd9c FS |
809 | local valgrind_bins valgrind_libs valgrind_dbg_and_supp |
810 | ||
c66e2f6c EV |
811 | readarray -t valgrind_bins < <(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/') |
812 | image_install "${valgrind_bins[@]}" | |
a2fbff31 | 813 | |
c66e2f6c EV |
814 | readarray -t valgrind_libs < <(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}') |
815 | image_install "${valgrind_libs[@]}" | |
a2fbff31 | 816 | |
c66e2f6c | 817 | readarray -t valgrind_dbg_and_supp < <( |
a2fbff31 EV |
818 | strace -e open valgrind /bin/true 2>&1 >/dev/null | |
819 | perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }' | |
c66e2f6c EV |
820 | ) |
821 | image_install "${valgrind_dbg_and_supp[@]}" | |
a2fbff31 EV |
822 | } |
823 | ||
cb2f9d3f | 824 | create_valgrind_wrapper() { |
1b8fcd9c FS |
825 | local valgrind_wrapper="$initdir/$ROOTLIBDIR/systemd-under-valgrind" |
826 | ddebug "Create $valgrind_wrapper" | |
827 | cat >"$valgrind_wrapper" <<EOF | |
ff12a795 | 828 | #!/usr/bin/env bash |
cb2f9d3f | 829 | |
23cabb68 | 830 | mount -t proc proc /proc |
8e78dca9 | 831 | exec valgrind --leak-check=full --track-fds=yes --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@" |
cb2f9d3f | 832 | EOF |
1b8fcd9c | 833 | chmod 0755 "$valgrind_wrapper" |
cb2f9d3f EV |
834 | } |
835 | ||
1786fae3 | 836 | create_asan_wrapper() { |
1b8fcd9c FS |
837 | local asan_wrapper="$initdir/$ROOTLIBDIR/systemd-under-asan" |
838 | dinfo "Create ASan wrapper as '$asan_wrapper'" | |
37ee8dc8 | 839 | |
648fd189 FS |
840 | [[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be" |
841 | ||
842 | # clang: install llvm-symbolizer to generate useful reports | |
843 | # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports | |
39e17536 | 844 | [[ "$ASAN_COMPILER" == "clang" ]] && image_install "llvm-symbolizer" |
37ee8dc8 | 845 | |
1b8fcd9c | 846 | cat >"$asan_wrapper" <<EOF |
ff12a795 | 847 | #!/usr/bin/env bash |
1786fae3 EV |
848 | |
849 | set -x | |
850 | ||
648fd189 FS |
851 | echo "ASan RT: $ASAN_RT_PATH" |
852 | if [[ ! -e "$ASAN_RT_PATH" ]]; then | |
853 | echo >&2 "Couldn't find ASan RT at '$ASAN_RT_PATH', can't continue" | |
854 | exit 1 | |
855 | fi | |
856 | ||
f5f8cc7a FS |
857 | DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1} |
858 | DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1} | |
25213e16 FS |
859 | DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS" |
860 | ||
861 | # Create a simple environment file which can be included by systemd services | |
862 | # that need it (i.e. services that utilize DynamicUser=true and bash, etc.) | |
863 | cat >/usr/lib/systemd/systemd-asan-env <<INNER_EOF | |
864 | LD_PRELOAD=$ASAN_RT_PATH | |
865 | ASAN_OPTIONS=$DEFAULT_ASAN_OPTIONS | |
b8dd2766 | 866 | LSAN_OPTIONS=detect_leaks=0 |
25213e16 FS |
867 | UBSAN_OPTIONS=$DEFAULT_UBSAN_OPTIONS |
868 | INNER_EOF | |
1786fae3 | 869 | |
37ee8dc8 FS |
870 | # As right now bash is the PID 1, we can't expect PATH to have a sane value. |
871 | # Let's make one to prevent unexpected "<bin> not found" issues in the future | |
872 | export PATH="/sbin:/bin:/usr/sbin:/usr/bin" | |
873 | ||
1786fae3 EV |
874 | mount -t proc proc /proc |
875 | mount -t sysfs sysfs /sys | |
876 | mount -o remount,rw / | |
877 | ||
fa65ba6b | 878 | DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT ASAN_RT_PATH=$ASAN_RT_PATH" |
648fd189 FS |
879 | |
880 | if [[ "$ASAN_COMPILER" == "clang" ]]; then | |
37ee8dc8 FS |
881 | # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty |
882 | # unnecessary for gcc & libasan, however, for clang this is crucial, as its | |
883 | # runtime ASan DSO is in a non-standard (library) path. | |
0ee99483 | 884 | echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf |
37ee8dc8 | 885 | ldconfig |
1786fae3 EV |
886 | fi |
887 | echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf | |
9688fccc | 888 | echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf |
37ee8dc8 | 889 | echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf |
1786fae3 EV |
890 | |
891 | # ASAN and syscall filters aren't compatible with each other. | |
892 | find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/' | |
893 | ||
88ed0f26 EV |
894 | # The redirection of ASAN reports to a file prevents them from ending up in /dev/null. |
895 | # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886. | |
896 | JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d | |
897 | mkdir -p "\$JOURNALD_CONF_DIR" | |
abf9b52c | 898 | printf "[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 | 899 | |
6141c6c9 EV |
900 | # Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path |
901 | # Let's try to catch them by redirecting stderr (and stdout just in case) to a file | |
902 | # See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821 | |
903 | printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf" | |
904 | ||
082bcdca EV |
905 | # 90s isn't enough for some services to finish when literally everything is run |
906 | # under ASan+UBSan in containers, which, in turn, are run in VMs. | |
d7283fc1 | 907 | # Let's limit which environments such services should be executed in. |
082bcdca | 908 | mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d |
8f843190 | 909 | printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf |
082bcdca | 910 | |
a5372344 EV |
911 | # Let's override another hard-coded timeout that kicks in too early |
912 | mkdir -p /etc/systemd/system/systemd-journal-flush.service.d | |
913 | printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf | |
914 | ||
c1342d55 | 915 | export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS |
1b8fcd9c | 916 | exec "$ROOTLIBDIR/systemd" "\$@" |
1786fae3 EV |
917 | EOF |
918 | ||
1b8fcd9c | 919 | chmod 0755 "$asan_wrapper" |
1786fae3 EV |
920 | } |
921 | ||
45dbd7b6 | 922 | create_strace_wrapper() { |
1b8fcd9c FS |
923 | local strace_wrapper="$initdir/$ROOTLIBDIR/systemd-under-strace" |
924 | ddebug "Create $strace_wrapper" | |
925 | cat >"$strace_wrapper" <<EOF | |
ff12a795 | 926 | #!/usr/bin/env bash |
45dbd7b6 | 927 | |
1b8fcd9c | 928 | exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd" "\$@" |
45dbd7b6 | 929 | EOF |
1b8fcd9c | 930 | chmod 0755 "$strace_wrapper" |
45dbd7b6 EV |
931 | } |
932 | ||
0dd77c15 | 933 | install_fs_tools() { |
1b8fcd9c | 934 | dinfo "Install fsck" |
39e17536 FS |
935 | image_install /sbin/fsck* |
936 | image_install -o /bin/fsck* | |
331fb4ca EV |
937 | |
938 | # fskc.reiserfs calls reiserfsck. so, install it | |
39e17536 | 939 | image_install -o reiserfsck |
0dd77c15 ZJS |
940 | |
941 | # we use mkfs in system-repart tests | |
39e17536 FS |
942 | image_install /sbin/mkfs.ext4 |
943 | image_install /sbin/mkfs.vfat | |
0dd77c15 ZJS |
944 | } |
945 | ||
946 | install_modules() { | |
947 | dinfo "Install modules" | |
948 | ||
949 | instmods loop | |
950 | instmods vfat | |
35cde9e9 | 951 | instmods nls_ascii =nls |
dce95d0b | 952 | instmods dummy |
9c94ab0f YW |
953 | # for TEST-35-LOGIN |
954 | instmods scsi_debug uinput | |
0dd77c15 | 955 | |
23f8e019 | 956 | if get_bool "$LOOKS_LIKE_SUSE"; then |
0dd77c15 ZJS |
957 | instmods ext4 |
958 | fi | |
9974ff63 EV |
959 | } |
960 | ||
889a9042 RC |
961 | install_dmevent() { |
962 | instmods dm_crypt =crypto | |
59279e96 | 963 | inst_binary dmeventd |
e3d9a2e7 | 964 | image_install "${ROOTLIBDIR:?}"/system/dm-event.{service,socket} |
23f8e019 | 965 | if get_bool "$LOOKS_LIKE_DEBIAN"; then |
ac289ce3 | 966 | # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu |
9c869ff6 DJL |
967 | # and since buster/bionic 95-dm-notify.rules |
968 | # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch | |
969 | inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules | |
ac289ce3 EV |
970 | else |
971 | inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules | |
972 | fi | |
23f8e019 | 973 | if get_bool "$LOOKS_LIKE_SUSE"; then |
2aa5a13a ER |
974 | inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules |
975 | fi | |
889a9042 RC |
976 | } |
977 | ||
9e7c3bd4 FS |
978 | install_multipath() { |
979 | instmods "=md" multipath | |
980 | image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx | |
981 | image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket} | |
982 | if get_bool "$LOOKS_LIKE_DEBIAN"; then | |
983 | inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules | |
984 | else | |
985 | inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules | |
986 | fi | |
987 | mkdir -p "${initdir:?}/etc/multipath" | |
988 | ||
989 | local file | |
990 | while read -r file; do | |
991 | # Install libraries required by the given library | |
992 | inst_libs "$file" | |
993 | # Install the library itself and create necessary symlinks | |
994 | inst_library "$file" | |
995 | done < <(find /lib*/multipath -type f) | |
9e7c3bd4 FS |
996 | } |
997 | ||
4999f368 FS |
998 | install_lvm() { |
999 | image_install lvm | |
1000 | image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket} | |
e50d743f | 1001 | image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service |
4999f368 FS |
1002 | image_install -o "/lib/tmpfiles.d/lvm2.conf" |
1003 | if get_bool "$LOOKS_LIKE_DEBIAN"; then | |
1004 | inst_rules 56-lvm.rules 69-lvm-metad.rules | |
1005 | else | |
e50d743f FS |
1006 | # Support the new udev autoactivation introduced in lvm 2.03.14 |
1007 | # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6 | |
d10d562b FS |
1008 | # Static autoactivation (via lvm2-activation-generator) was dropped |
1009 | # in lvm 2.03.15 | |
1010 | # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c | |
e50d743f FS |
1011 | if [[ -f /lib/udev/rules.d/69-dm-lvm.rules ]]; then |
1012 | inst_rules 11-dm-lvm.rules 69-dm-lvm.rules | |
1013 | else | |
d10d562b | 1014 | image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator |
e50d743f FS |
1015 | image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service |
1016 | inst_rules 11-dm-lvm.rules 69-dm-lvm-metad.rules | |
1017 | fi | |
4999f368 FS |
1018 | fi |
1019 | mkdir -p "${initdir:?}/etc/lvm" | |
1020 | } | |
1021 | ||
babe9355 FS |
1022 | install_btrfs() { |
1023 | instmods btrfs | |
1024 | # Not all utilities provided by btrfs-progs are listed here; extend the list | |
1025 | # if necessary | |
1026 | image_install btrfs btrfstune mkfs.btrfs | |
1027 | inst_rules 64-btrfs-dm.rules | |
1028 | } | |
1029 | ||
f4e64b6e FS |
1030 | install_iscsi() { |
1031 | # Install both client and server side stuff by default | |
1032 | local inst="${1:-}" | |
1033 | local file | |
1034 | ||
1035 | # Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case | |
1036 | # (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.) | |
1037 | if [[ -z "$inst" || "$inst" =~ (client|initiator) ]]; then | |
1038 | image_install iscsi-iname iscsiadm iscsid iscsistart | |
1039 | image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service | |
1040 | image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket} | |
1041 | image_install "${ROOTLIBDIR:?}"/system/iscsi.service | |
1042 | mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static} | |
1043 | mkdir -p "${initdir:?}/etc/iscsi" | |
1044 | echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf" | |
d9e1cb28 FS |
1045 | # Since open-iscsi 2.1.2 [0] the initiator name should be generated via |
1046 | # a one-time service instead of distro package's post-install scripts. | |
1047 | # However, some distros still use this approach even after this patch, | |
1048 | # so prefer the already existing initiatorname.iscsi file if it exists. | |
1049 | # | |
1050 | # [0] https://github.com/open-iscsi/open-iscsi/commit/f37d5b653f9f251845db3f29b1a3dcb90ec89731 | |
1051 | if [[ ! -e /etc/iscsi/initiatorname.iscsi ]]; then | |
1052 | image_install "${ROOTLIBDIR:?}"/system/iscsi-init.service | |
326425fb FS |
1053 | if get_bool "$IS_BUILT_WITH_ASAN"; then |
1054 | # The iscsi-init.service calls `sh` which might, in certain circumstances, | |
1055 | # pull in instrumented systemd NSS modules causing `sh` to fail. Let's mitigate | |
1056 | # this by pulling in an env file crafted by `create_asan_wrapper()` that | |
1057 | # (among others) pre-loads ASan's DSO. | |
1058 | mkdir -p "${initdir:?}/etc/systemd/system/iscsi-init.service.d/" | |
1059 | printf "[Service]\nEnvironmentFile=/usr/lib/systemd/systemd-asan-env" >"${initdir:?}/etc/systemd/system/iscsi-init.service.d/asan-env.conf" | |
1060 | fi | |
d9e1cb28 FS |
1061 | else |
1062 | inst_simple "/etc/iscsi/initiatorname.iscsi" | |
1063 | fi | |
f4e64b6e FS |
1064 | fi |
1065 | ||
1066 | # Install server-side stuff ("target" in iSCSI jargon) - TGT in this case | |
1067 | # (tgt on Debian, scsi-target-utils on Fedora, etc.) | |
1068 | if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then | |
1069 | image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg | |
1070 | image_install -o /etc/sysconfig/tgtd | |
1071 | image_install "${ROOTLIBDIR:?}"/system/tgtd.service | |
1072 | mkdir -p "${initdir:?}/etc/tgt" | |
1073 | touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf | |
1074 | # Install perl modules required by tgt-admin | |
1075 | # | |
1076 | # Forgive me father for I have sinned. The monstrosity below appends | |
1077 | # a perl snippet to the `tgt-admin` perl script on the fly, which | |
1078 | # dumps a list of files (perl modules) required by `tgt-admin` at | |
1079 | # the runtime plus any DSOs loaded via DynaLoader. This list is then | |
1080 | # passed to `inst_simple` which installs the necessary files into the image | |
1c3f490f FS |
1081 | # |
1082 | # shellcheck disable=SC2016 | |
f4e64b6e FS |
1083 | while read -r file; do |
1084 | inst_simple "$file" | |
1c3f490f | 1085 | done < <(perl -- <(cat "$(command -v tgt-admin)" <(echo -e 'use DynaLoader; print map { "$_\n" } values %INC; print join("\n", @DynaLoader::dl_shared_objects)')) -p | awk '/^\// { print $1 }') |
f4e64b6e FS |
1086 | fi |
1087 | } | |
1088 | ||
3c9af05c FS |
1089 | install_mdadm() { |
1090 | local unit | |
1091 | local mdadm_units=( | |
1092 | system/mdadm-grow-continue@.service | |
1093 | system/mdadm-last-resort@.service | |
1094 | system/mdadm-last-resort@.timer | |
1095 | system/mdmon@.service | |
1096 | system/mdmonitor-oneshot.service | |
1097 | system/mdmonitor-oneshot.timer | |
1098 | system/mdmonitor.service | |
1099 | system-shutdown/mdadm.shutdown | |
1100 | ) | |
1101 | ||
1102 | image_install mdadm mdmon | |
1103 | inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules | |
1104 | # Fedora/CentOS/RHEL ships this rule file | |
1105 | [[ -f /lib/udev/rules.d/65-md-incremental.rules ]] && inst_rules 65-md-incremental.rules | |
1106 | ||
1107 | for unit in "${mdadm_units[@]}"; do | |
1108 | image_install "${ROOTLIBDIR:?}/$unit" | |
1109 | done | |
1110 | } | |
1111 | ||
8fa03808 | 1112 | install_compiled_systemd() { |
1b8fcd9c | 1113 | dinfo "Install compiled systemd" |
9f927e46 | 1114 | |
1b8fcd9c FS |
1115 | local ninja_bin |
1116 | ninja_bin="$(type -P ninja || type -P ninja-build)" | |
1117 | if [[ -z "$ninja_bin" ]]; then | |
ca992ecf EV |
1118 | dfatal "ninja was not found" |
1119 | exit 1 | |
1120 | fi | |
1b8fcd9c | 1121 | (set -x; DESTDIR="$initdir" "$ninja_bin" -C "$BUILD_DIR" install) |
c82dc15b LB |
1122 | |
1123 | # If we are doing coverage runs, copy over the binary notes files, as lcov expects to | |
1124 | # find them in the same directory as the runtime data counts | |
02d7e730 | 1125 | if get_bool "$IS_BUILT_WITH_COVERAGE"; then |
c82dc15b LB |
1126 | mkdir -p "${initdir}/${BUILD_DIR:?}/" |
1127 | rsync -am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/" | |
e4c822e9 FS |
1128 | # Set effective & default ACLs for the build dir so unprivileged |
1129 | # processes can write gcda files with coverage stats | |
1130 | setfacl -R -m 'd:o:rwX' -m 'o:rwX' "${initdir}/${BUILD_DIR:?}/" | |
c82dc15b | 1131 | fi |
8fa03808 DS |
1132 | } |
1133 | ||
1134 | install_debian_systemd() { | |
1b8fcd9c | 1135 | dinfo "Install debian systemd" |
8fa03808 | 1136 | |
96af59aa FS |
1137 | local files |
1138 | ||
1139 | while read -r deb; do | |
1140 | files="$(dpkg-query -L "$deb" 2>/dev/null)" || continue | |
8fa03808 | 1141 | ddebug "Install debian files from package $deb" |
96af59aa | 1142 | for file in $files; do |
8fa03808 DS |
1143 | [ -e "$file" ] || continue |
1144 | [ -d "$file" ] && continue | |
96af59aa | 1145 | inst "$file" |
8fa03808 | 1146 | done |
96af59aa | 1147 | done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2) |
8fa03808 DS |
1148 | } |
1149 | ||
abf06267 FB |
1150 | install_suse_systemd() { |
1151 | local testsdir=/usr/lib/systemd/tests | |
1152 | local pkgs | |
1153 | ||
1154 | dinfo "Install SUSE systemd" | |
1155 | ||
1156 | pkgs=( | |
1157 | systemd | |
1158 | systemd-container | |
1159 | systemd-coredump | |
1160 | systemd-experimental | |
1161 | systemd-journal-remote | |
1162 | systemd-portable | |
1163 | udev | |
1164 | ) | |
1165 | ||
1166 | for p in "${pkgs[@]}"; do | |
1167 | rpm -q "$p" &>/dev/null || continue | |
1168 | ||
1169 | ddebug "Install files from package $p" | |
1170 | while read -r f; do | |
1171 | [ -e "$f" ] || continue | |
1172 | [ -d "$f" ] && continue | |
1173 | inst "$f" | |
1174 | done < <(rpm -ql "$p") | |
1175 | done | |
1176 | ||
1177 | # we only need testsdata dir as well as the unit tests (for | |
1178 | # TEST-02-UNITTESTS) in the image. | |
1179 | dinfo "Install unit tests and testdata directory" | |
1180 | ||
1181 | mkdir -p "$initdir/$testsdir" | |
1182 | cp "$testsdir"/test-* "$initdir/$testsdir/" | |
1183 | cp -a "$testsdir/testdata" "$initdir/$testsdir/" | |
1184 | ||
1185 | # On openSUSE, these dirs are not created at package install for now on. | |
1186 | mkdir -p "$initdir/var/log/journal/remote" | |
1187 | } | |
1188 | ||
8fa03808 | 1189 | install_distro_systemd() { |
1b8fcd9c | 1190 | dinfo "Install distro systemd" |
8fa03808 | 1191 | |
23f8e019 | 1192 | if get_bool "$LOOKS_LIKE_DEBIAN"; then |
8fa03808 | 1193 | install_debian_systemd |
abf06267 FB |
1194 | elif get_bool "$LOOKS_LIKE_SUSE"; then |
1195 | install_suse_systemd | |
8fa03808 DS |
1196 | else |
1197 | dfatal "NO_BUILD not supported for this distro" | |
1198 | exit 1 | |
1199 | fi | |
1200 | } | |
1201 | ||
1202 | install_systemd() { | |
1b8fcd9c | 1203 | dinfo "Install systemd" |
23f8e019 | 1204 | if get_bool "$NO_BUILD"; then |
8fa03808 DS |
1205 | install_distro_systemd |
1206 | else | |
1207 | install_compiled_systemd | |
1208 | fi | |
1209 | ||
889a9042 | 1210 | # remove unneeded documentation |
02d7e730 | 1211 | rm -fr "${initdir:?}"/usr/share/{man,doc} |
61b480b6 ZJS |
1212 | |
1213 | # enable debug logging in PID1 | |
1b8fcd9c | 1214 | echo LogLevel=debug >>"$initdir/etc/systemd/system.conf" |
bf6ef6b6 EV |
1215 | if [[ -n "$TEST_SYSTEMD_LOG_LEVEL" ]]; then |
1216 | echo DefaultEnvironment=SYSTEMD_LOG_LEVEL="$TEST_SYSTEMD_LOG_LEVEL" >>"$initdir/etc/systemd/system.conf" | |
1217 | fi | |
215bffe1 | 1218 | # store coredumps in journal |
1b8fcd9c | 1219 | echo Storage=journal >>"$initdir/etc/systemd/coredump.conf" |
064a5c14 DDM |
1220 | # Propagate SYSTEMD_UNIT_PATH to user systemd managers |
1221 | mkdir "$initdir/etc/systemd/system/user@.service.d/" | |
1222 | echo -e "[Service]\nPassEnvironment=SYSTEMD_UNIT_PATH\n" >"$initdir/etc/systemd/system/user@.service.d/override.conf" | |
02d7e730 | 1223 | |
b6fc5240 FS |
1224 | # When built with gcov, disable ProtectSystem= and ProtectHome= in the test |
1225 | # images, since it prevents gcov to write the coverage reports (*.gcda | |
93c3b698 | 1226 | # files) |
02d7e730 FS |
1227 | if get_bool "$IS_BUILT_WITH_COVERAGE"; then |
1228 | mkdir -p "$initdir/etc/systemd/system/service.d/" | |
93c3b698 FS |
1229 | echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/99-gcov-override.conf" |
1230 | # Similarly, set ReadWritePaths= to the $BUILD_DIR in the test image | |
1231 | # to make the coverage work with units utilizing DynamicUser=yes. Do | |
1232 | # this only for services from TEST-20, as setting this system-wide | |
1233 | # has many undesirable side-effects | |
1234 | mkdir -p "$initdir/etc/systemd/system/test20-.service.d/" | |
1235 | echo -e "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/test20-.service.d/99-gcov-rwpaths-override.conf" | |
02d7e730 | 1236 | fi |
6f73ef8b FS |
1237 | |
1238 | # If we're built with -Dportabled=false, tests with systemd-analyze | |
1239 | # --profile will fail. Since we need just the profile (text) files, let's | |
1240 | # copy them into the image if they don't exist there. | |
1241 | local portable_dir="${initdir:?}${ROOTLIBDIR:?}/portable" | |
1242 | if [[ ! -d "$portable_dir/profile/strict" ]]; then | |
1243 | dinfo "Couldn't find portable profiles in the test image" | |
1244 | dinfo "Copying them directly from the source tree" | |
1245 | mkdir -p "$portable_dir" | |
1246 | cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir" | |
1247 | fi | |
889a9042 RC |
1248 | } |
1249 | ||
d7a4278d | 1250 | get_ldpath() { |
1b8fcd9c FS |
1251 | local rpath |
1252 | rpath="$(objdump -p "${1:?}" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :)" | |
5bb4503d LP |
1253 | |
1254 | if [ -z "$rpath" ] ; then | |
1b8fcd9c | 1255 | echo "$BUILD_DIR" |
5bb4503d | 1256 | else |
1b8fcd9c | 1257 | echo "$rpath" |
5bb4503d | 1258 | fi |
d7a4278d FS |
1259 | } |
1260 | ||
889a9042 | 1261 | install_missing_libraries() { |
1b8fcd9c | 1262 | dinfo "Install missing libraries" |
889a9042 | 1263 | # install possible missing libraries |
1b8fcd9c FS |
1264 | for i in "${initdir:?}"{,/usr}/{sbin,bin}/* "$initdir"{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do |
1265 | LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs "$i" | |
889a9042 | 1266 | done |
b7fca1b0 | 1267 | |
5f347d31 FS |
1268 | # Install libgcc_s.so if available, since it's dlopen()ed by libpthread |
1269 | # and might cause unexpected failures during pthread_exit()/pthread_cancel() | |
1270 | # if not present | |
1271 | # See: https://github.com/systemd/systemd/pull/23858 | |
1272 | while read -r libgcc_s; do | |
1273 | [[ -e "$libgcc_s" ]] && inst_library "$libgcc_s" | |
1274 | done < <(ldconfig -p | awk '/\/libgcc_s.so.1$/ { print $4 }') | |
1275 | ||
1b8fcd9c | 1276 | local lib path |
b7fca1b0 LB |
1277 | # A number of dependencies is now optional via dlopen, so the install |
1278 | # script will not pick them up, since it looks at linkage. | |
1136175c | 1279 | for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon; do |
a9d34376 | 1280 | ddebug "Searching for $lib via pkg-config" |
1b8fcd9c FS |
1281 | if pkg-config --exists "$lib"; then |
1282 | path="$(pkg-config --variable=libdir "$lib")" | |
a9d34376 LB |
1283 | if [ -z "${path}" ]; then |
1284 | ddebug "$lib.pc does not contain a libdir variable, skipping" | |
1285 | continue | |
1286 | fi | |
1287 | ||
1288 | if ! [[ ${lib} =~ ^lib ]]; then | |
1289 | lib="lib${lib}" | |
1290 | fi | |
1291 | # Some pkg-config files are broken and give out the wrong paths | |
1292 | # (eg: libcryptsetup), so just ignore them | |
1293 | inst_libs "${path}/${lib}.so" || true | |
1294 | inst_library "${path}/${lib}.so" || true | |
1136175c YW |
1295 | |
1296 | if [[ "$lib" == "libxkbcommon" ]]; then | |
1297 | install_x11_keymaps full | |
1298 | fi | |
a9d34376 LB |
1299 | else |
1300 | ddebug "$lib.pc not found, skipping" | |
1301 | continue | |
1302 | fi | |
b7fca1b0 | 1303 | done |
3c5f7ec4 DDM |
1304 | |
1305 | # Install extra openssl 3 stuff | |
1306 | path="$(pkg-config --variable=libdir libcrypto)" | |
1307 | inst_simple "${path}/ossl-modules/legacy.so" || true | |
1308 | inst_simple "${path}/ossl-modules/fips.so" || true | |
1309 | inst_simple "${path}/engines-3/afalg.so" || true | |
1310 | inst_simple "${path}/engines-3/capi.so" || true | |
1311 | inst_simple "${path}/engines-3/loader_attic.so" || true | |
1312 | inst_simple "${path}/engines-3/padlock.so" || true | |
889a9042 RC |
1313 | } |
1314 | ||
1506edca | 1315 | cleanup_loopdev() { |
1b8fcd9c | 1316 | if [ -n "${LOOPDEV:=}" ]; then |
1506edca ZJS |
1317 | ddebug "losetup -d $LOOPDEV" |
1318 | losetup -d "${LOOPDEV}" | |
2991fa41 | 1319 | unset LOOPDEV |
1506edca ZJS |
1320 | fi |
1321 | } | |
1322 | ||
b92c3df2 | 1323 | add_at_exit_handler cleanup_loopdev |
1506edca | 1324 | |
889a9042 | 1325 | create_empty_image() { |
1b8fcd9c | 1326 | if [ -z "${IMAGE_NAME:=}" ]; then |
8c3534b5 ZJS |
1327 | echo "create_empty_image: \$IMAGE_NAME not set" |
1328 | exit 1 | |
1329 | fi | |
1330 | ||
98b27937 FS |
1331 | # Partition sizes are in MiBs |
1332 | local root_size=500 | |
1333 | local data_size=50 | |
23f8e019 | 1334 | if ! get_bool "$NO_BUILD"; then |
63878c52 | 1335 | if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then |
98b27937 | 1336 | root_size=$((root_size+=200)) |
63878c52 LB |
1337 | fi |
1338 | if meson configure "${BUILD_DIR:?}" | grep 'link-.*-shared' | awk '{ print $2 }' | grep -q 'false'; then | |
98b27937 | 1339 | root_size=$((root_size+=200)) |
63878c52 | 1340 | fi |
02d7e730 | 1341 | if get_bool "$IS_BUILT_WITH_COVERAGE"; then |
98b27937 | 1342 | root_size=$((root_size+=250)) |
c82dc15b | 1343 | fi |
853401a6 | 1344 | fi |
23f8e019 | 1345 | if ! get_bool "$STRIP_BINARIES"; then |
98b27937 FS |
1346 | root_size=$((4 * root_size)) |
1347 | data_size=$((2 * data_size)) | |
28c7474e | 1348 | fi |
14697c41 DDM |
1349 | if [ "$IMAGE_NAME" = "repart" ]; then |
1350 | root_size=$((root_size+=1000)) | |
1351 | fi | |
80c53fe7 | 1352 | |
98b27937 | 1353 | echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)" |
1b8fcd9c | 1354 | rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC" |
ec43f686 | 1355 | |
889a9042 | 1356 | # Create the blank file to use as a root filesystem |
98b27937 | 1357 | truncate -s "${root_size}M" "$IMAGE_PUBLIC" |
ec43f686 | 1358 | |
e8945092 | 1359 | LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC") |
739d81dd | 1360 | [ -b "$LOOPDEV" ] || return 1 |
98b27937 | 1361 | # Create two partitions - a root one and a data one (utilized by some tests) |
edbced8a | 1362 | sfdisk "$LOOPDEV" <<EOF |
98b27937 FS |
1363 | label: gpt |
1364 | type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=$((root_size - data_size))M bootable | |
1365 | type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data | |
889a9042 RC |
1366 | EOF |
1367 | ||
053edc5b EV |
1368 | udevadm settle |
1369 | ||
bac05644 | 1370 | local label=(-L systemd_boot) |
331fb4ca | 1371 | # mkfs.reiserfs doesn't know -L. so, use --label instead |
bac05644 | 1372 | [[ "$FSTYPE" == "reiserfs" ]] && label=(--label systemd_boot) |
1b8fcd9c | 1373 | if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then |
331fb4ca EV |
1374 | dfatal "Failed to mkfs -t ${FSTYPE}" |
1375 | exit 1 | |
1376 | fi | |
889a9042 RC |
1377 | } |
1378 | ||
8c3534b5 | 1379 | mount_initdir() { |
1b8fcd9c FS |
1380 | if [ -z "${LOOPDEV:=}" ]; then |
1381 | [ -e "${IMAGE_PRIVATE:?}" ] && image="$IMAGE_PRIVATE" || image="${IMAGE_PUBLIC:?}" | |
1382 | LOOPDEV="$(losetup --show -P -f "$image")" | |
8c3534b5 | 1383 | [ -b "$LOOPDEV" ] || return 1 |
8c3534b5 | 1384 | |
1506edca ZJS |
1385 | udevadm settle |
1386 | fi | |
ec4cab49 | 1387 | |
1b8fcd9c FS |
1388 | if ! mountpoint -q "${initdir:?}"; then |
1389 | mkdir -p "$initdir" | |
1390 | mount "${LOOPDEV}p1" "$initdir" | |
1506edca | 1391 | TEST_SETUP_CLEANUP_ROOTDIR=1 |
8c3534b5 | 1392 | fi |
8c3534b5 ZJS |
1393 | } |
1394 | ||
ec43f686 ZJS |
1395 | cleanup_initdir() { |
1396 | # only umount if create_empty_image_rootdir() was called to mount it | |
9caab7b5 FS |
1397 | if get_bool "$TEST_SETUP_CLEANUP_ROOTDIR"; then |
1398 | _umount_dir "${initdir:?}" | |
1399 | fi | |
ec43f686 ZJS |
1400 | } |
1401 | ||
1402 | umount_loopback() { | |
1403 | # unmount the loopback device from all places. Otherwise we risk file | |
1404 | # system corruption. | |
1b8fcd9c | 1405 | for device in $(losetup -l | awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do |
ec43f686 ZJS |
1406 | ddebug "Unmounting all uses of $device" |
1407 | mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v | |
1408 | done | |
1409 | } | |
1410 | ||
8c3534b5 ZJS |
1411 | create_empty_image_rootdir() { |
1412 | create_empty_image | |
1413 | mount_initdir | |
1414 | } | |
1415 | ||
0d6e61d6 EV |
1416 | check_asan_reports() { |
1417 | local ret=0 | |
1b8fcd9c | 1418 | local root="${1:?}" |
0d6e61d6 | 1419 | |
23f8e019 | 1420 | if get_bool "$IS_BUILT_WITH_ASAN"; then |
0d6e61d6 EV |
1421 | ls -l "$root" |
1422 | if [[ -e "$root/systemd.asan.log.1" ]]; then | |
1423 | cat "$root/systemd.asan.log.1" | |
1b8fcd9c | 1424 | ret=$((ret+1)) |
7e11a95e | 1425 | fi |
998445fd | 1426 | |
1b8fcd9c FS |
1427 | journald_report="$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)" |
1428 | if [[ -n "$journald_report" ]]; then | |
6141c6c9 | 1429 | printf "%s\n" "$journald_report" |
65dd488f | 1430 | cat "$root/systemd-journald.out" || : |
1b8fcd9c | 1431 | ret=$((ret+1)) |
d56db495 | 1432 | fi |
ed4f303f | 1433 | |
1b8fcd9c | 1434 | pids="$( |
84c49ad1 | 1435 | "$JOURNALCTL" -D "$root/var/log/journal" | perl -alne ' |
d56db495 EV |
1436 | BEGIN { |
1437 | %services_to_ignore = ( | |
1438 | "dbus-daemon" => undef, | |
d0880faa | 1439 | "dbus-broker-launch" => undef, |
d56db495 EV |
1440 | ); |
1441 | } | |
6d67286f | 1442 | print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}' |
1b8fcd9c FS |
1443 | )" |
1444 | if [[ -n "$pids" ]]; then | |
1445 | ret=$((ret+1)) | |
ed4f303f | 1446 | for pid in $pids; do |
1b8fcd9c | 1447 | "$JOURNALCTL" -D "$root/var/log/journal" _PID="$pid" --no-pager |
ed4f303f | 1448 | done |
d56db495 | 1449 | fi |
7e11a95e EV |
1450 | fi |
1451 | ||
889a9042 RC |
1452 | return $ret |
1453 | } | |
1454 | ||
c82dc15b LB |
1455 | check_coverage_reports() { |
1456 | local root="${1:?}" | |
1457 | ||
1458 | if get_bool "$NO_BUILD"; then | |
1459 | return 0 | |
1460 | fi | |
02d7e730 | 1461 | if ! get_bool "$IS_BUILT_WITH_COVERAGE"; then |
c82dc15b LB |
1462 | return 0 |
1463 | fi | |
1464 | ||
1465 | if [ -n "${ARTIFACT_DIRECTORY}" ]; then | |
1466 | dest="${ARTIFACT_DIRECTORY}/${testname:?}.coverage-info" | |
1467 | else | |
1468 | dest="${TESTDIR:?}/coverage-info" | |
1469 | fi | |
1470 | ||
1471 | # Create a coverage report that will later be uploaded. Remove info about | |
1472 | # system libraries/headers, as we don't really care about them. | |
35382a9d FS |
1473 | if [[ -f "$dest" ]]; then |
1474 | # If the destination report file already exists, don't overwrite it, but | |
1475 | # dump the new report in a temporary file and then merge it with the already | |
1476 | # present one - this usually happens when running both "parts" of a test | |
1477 | # in one run (the qemu and the nspawn part). | |
1478 | lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new" | |
1479 | lcov --remove "${dest}.new" -o "${dest}.new" '/usr/include/*' '/usr/lib/*' | |
1480 | lcov --add-tracefile "${dest}" --add-tracefile "${dest}.new" -o "${dest}" | |
1481 | rm -f "${dest}.new" | |
1482 | else | |
1483 | lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}" | |
1484 | lcov --remove "${dest}" -o "${dest}" '/usr/include/*' '/usr/lib/*' | |
1485 | fi | |
c82dc15b | 1486 | |
d282e57e FS |
1487 | # If the test logs contain lines like: |
1488 | # | |
1489 | # ...systemd-resolved[735885]: profiling:/systemd-meson-build/src/shared/libsystemd-shared-250.a.p/base-filesystem.c.gcda:Cannot open | |
1490 | # | |
1491 | # it means we're possibly missing some coverage since gcov can't write the stats, | |
1492 | # usually due to the sandbox being too restrictive (e.g. ProtectSystem=yes, | |
1493 | # ProtectHome=yes) or the $BUILD_DIR being inaccessible to non-root users - see | |
1494 | # `setfacl` stuff in install_compiled_systemd(). | |
1b2e3b8b FS |
1495 | if ! get_bool "${IGNORE_MISSING_COVERAGE:=}" && \ |
1496 | "${JOURNALCTL:?}" -q --no-pager -D "${root:?}/var/log/journal" --grep "profiling:.+?gcda:[Cc]annot open"; then | |
d282e57e FS |
1497 | derror "Detected possibly missing coverage, check the journal" |
1498 | return 1 | |
1499 | fi | |
1500 | ||
c82dc15b LB |
1501 | return 0 |
1502 | } | |
1503 | ||
8943daf8 | 1504 | save_journal() { |
d3b8e384 DS |
1505 | # Default to always saving journal |
1506 | local save="yes" | |
1507 | ||
1508 | if [ "${TEST_SAVE_JOURNAL}" = "no" ]; then | |
1509 | save="no" | |
1510 | elif [ "${TEST_SAVE_JOURNAL}" = "fail" ] && [ "$2" = "0" ]; then | |
1511 | save="no" | |
1512 | fi | |
1513 | ||
f9eb2d51 | 1514 | if [ -n "${ARTIFACT_DIRECTORY}" ]; then |
1b8fcd9c | 1515 | dest="${ARTIFACT_DIRECTORY}/${testname:?}.journal" |
f9eb2d51 | 1516 | else |
1b8fcd9c | 1517 | dest="${TESTDIR:?}/system.journal" |
f9eb2d51 | 1518 | fi |
8943daf8 | 1519 | |
1b8fcd9c | 1520 | for j in "${1:?}"/*; do |
23f8e019 | 1521 | if get_bool "$save"; then |
cca3050b | 1522 | if [ "$SYSTEMD_JOURNAL_REMOTE" = "" ]; then |
1523 | cp -a "$j" "$dest" | |
1524 | else | |
1525 | "$SYSTEMD_JOURNAL_REMOTE" -o "$dest" --getter="$JOURNALCTL -o export -D $j" | |
1526 | fi | |
d3b8e384 | 1527 | fi |
f1416431 ZJS |
1528 | |
1529 | if [ -n "${TEST_SHOW_JOURNAL}" ]; then | |
1530 | echo "---- $j ----" | |
1b8fcd9c | 1531 | "$JOURNALCTL" --no-pager -o short-monotonic --no-hostname --priority="${TEST_SHOW_JOURNAL}" -D "$j" |
f1416431 ZJS |
1532 | fi |
1533 | ||
1b8fcd9c | 1534 | rm -r "$j" |
f9eb2d51 | 1535 | done |
8943daf8 | 1536 | |
23f8e019 | 1537 | if ! get_bool "$save"; then |
d3b8e384 DS |
1538 | return 0 |
1539 | fi | |
1540 | ||
a83a7d1e YW |
1541 | if [ -n "${SUDO_USER}" ]; then |
1542 | setfacl -m "user:${SUDO_USER:?}:r-X" "$dest"* | |
1543 | fi | |
1544 | ||
8943daf8 | 1545 | # we want to print this sometime later, so save this in a variable |
1b8fcd9c | 1546 | JOURNAL_LIST="$(ls -l "$dest"*)" |
8943daf8 ZJS |
1547 | } |
1548 | ||
7bf20e48 | 1549 | check_result_common() { |
1b8fcd9c | 1550 | local workspace="${1:?}" |
7bf20e48 ZJS |
1551 | local ret |
1552 | ||
1553 | if [ -s "$workspace/failed" ]; then | |
c0d44092 | 1554 | # Non-empty …/failed has highest priority |
7bf20e48 | 1555 | cp -a "$workspace/failed" "${TESTDIR:?}/" |
a83a7d1e YW |
1556 | if [ -n "${SUDO_USER}" ]; then |
1557 | setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed | |
1558 | fi | |
7bf20e48 | 1559 | ret=1 |
faca95e1 FS |
1560 | elif get_bool "$TIMED_OUT"; then |
1561 | echo "(timeout)" >"${TESTDIR:?}/failed" | |
1562 | ret=2 | |
7bf20e48 ZJS |
1563 | elif [ -e "$workspace/testok" ]; then |
1564 | # …/testok always counts (but with lower priority than …/failed) | |
1565 | ret=0 | |
1566 | elif [ -e "$workspace/skipped" ]; then | |
1567 | # …/skipped always counts (a message is expected) | |
1568 | echo "${TESTNAME:?} was skipped:" | |
1569 | cat "$workspace/skipped" | |
1570 | ret=0 | |
7bf20e48 | 1571 | else |
c0d44092 ZJS |
1572 | echo "(failed; see logs)" >"${TESTDIR:?}/failed" |
1573 | ret=3 | |
7bf20e48 ZJS |
1574 | fi |
1575 | ||
c0d44092 | 1576 | check_asan_reports "$workspace" || ret=4 |
7bf20e48 | 1577 | |
c82dc15b LB |
1578 | check_coverage_reports "$workspace" || ret=5 |
1579 | ||
d3b8e384 DS |
1580 | save_journal "$workspace/var/log/journal" $ret |
1581 | ||
1b8fcd9c FS |
1582 | if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out" ]; then |
1583 | cp "$workspace/strace.out" "${ARTIFACT_DIRECTORY}/" | |
1584 | fi | |
7bf20e48 | 1585 | |
c0d44092 ZJS |
1586 | if [ ${ret:?} != 0 ] && [ -f "$TESTDIR/failed" ]; then |
1587 | echo -n "${TESTNAME:?}: " | |
1588 | cat "$TESTDIR/failed" | |
1589 | fi | |
7bf20e48 ZJS |
1590 | echo "${JOURNAL_LIST:-"No journals were saved"}" |
1591 | ||
c0d44092 | 1592 | return ${ret:?} |
7bf20e48 ZJS |
1593 | } |
1594 | ||
1595 | check_result_nspawn() { | |
1596 | local workspace="${1:?}" | |
1597 | local ret | |
1598 | ||
35d2d2e6 | 1599 | # Run a test-specific checks if defined by check_result_nspawn_hook() |
6695c41c | 1600 | if declare -F check_result_nspawn_hook >/dev/null; then |
4b9a0c3a | 1601 | if ! check_result_nspawn_hook "${workspace}"; then |
6695c41c FS |
1602 | derror "check_result_nspawn_hook() returned with EC > 0" |
1603 | ret=4 | |
1604 | fi | |
1605 | fi | |
1606 | ||
35d2d2e6 FS |
1607 | check_result_common "${workspace}" |
1608 | ret=$? | |
1609 | ||
1b8fcd9c | 1610 | _umount_dir "${initdir:?}" |
7bf20e48 | 1611 | |
0d6e61d6 EV |
1612 | return $ret |
1613 | } | |
1614 | ||
054ee249 MP |
1615 | # can be overridden in specific test |
1616 | check_result_qemu() { | |
7bf20e48 | 1617 | local ret |
8c3534b5 | 1618 | mount_initdir |
7bf20e48 | 1619 | |
35d2d2e6 | 1620 | # Run a test-specific checks if defined by check_result_qemu_hook() |
6695c41c | 1621 | if declare -F check_result_qemu_hook >/dev/null; then |
4b9a0c3a | 1622 | if ! check_result_qemu_hook "${initdir:?}"; then |
6695c41c FS |
1623 | derror "check_result_qemu_hook() returned with EC > 0" |
1624 | ret=4 | |
1625 | fi | |
1626 | fi | |
1627 | ||
35d2d2e6 FS |
1628 | check_result_common "${initdir:?}" |
1629 | ret=$? | |
1630 | ||
4b9a0c3a FS |
1631 | _umount_dir "${initdir:?}" |
1632 | ||
054ee249 MP |
1633 | return $ret |
1634 | } | |
1635 | ||
fa1fdd30 LB |
1636 | check_result_nspawn_unittests() { |
1637 | local workspace="${1:?}" | |
1638 | local ret=1 | |
1639 | ||
1640 | [[ -e "$workspace/testok" ]] && ret=0 | |
1641 | ||
1642 | if [[ -s "$workspace/failed" ]]; then | |
1643 | ret=$((ret + 1)) | |
1644 | echo "=== Failed test log ===" | |
1645 | cat "$workspace/failed" | |
1646 | else | |
1647 | if [[ -s "$workspace/skipped" ]]; then | |
1648 | echo "=== Skipped test log ==" | |
1649 | cat "$workspace/skipped" | |
1650 | # We might have only skipped tests - that should not fail the job | |
1651 | ret=0 | |
1652 | fi | |
1653 | if [[ -s "$workspace/testok" ]]; then | |
1654 | echo "=== Passed tests ===" | |
1655 | cat "$workspace/testok" | |
1656 | fi | |
1657 | fi | |
1658 | ||
23f8e019 | 1659 | get_bool "${TIMED_OUT:=}" && ret=1 |
0b5fe54f | 1660 | check_coverage_reports "$workspace" || ret=5 |
d3b8e384 DS |
1661 | |
1662 | save_journal "$workspace/var/log/journal" $ret | |
1663 | ||
fa1fdd30 LB |
1664 | _umount_dir "${initdir:?}" |
1665 | ||
fa1fdd30 LB |
1666 | return $ret |
1667 | } | |
1668 | ||
1669 | check_result_qemu_unittests() { | |
1670 | local ret=1 | |
1671 | ||
1672 | mount_initdir | |
1673 | [[ -e "${initdir:?}/testok" ]] && ret=0 | |
1674 | ||
1675 | if [[ -s "$initdir/failed" ]]; then | |
1676 | ret=$((ret + 1)) | |
1677 | echo "=== Failed test log ===" | |
1678 | cat "$initdir/failed" | |
1679 | else | |
1680 | if [[ -s "$initdir/skipped" ]]; then | |
1681 | echo "=== Skipped test log ==" | |
1682 | cat "$initdir/skipped" | |
1683 | # We might have only skipped tests - that should not fail the job | |
1684 | ret=0 | |
1685 | fi | |
1686 | if [[ -s "$initdir/testok" ]]; then | |
1687 | echo "=== Passed tests ===" | |
1688 | cat "$initdir/testok" | |
1689 | fi | |
1690 | fi | |
1691 | ||
23f8e019 | 1692 | get_bool "${TIMED_OUT:=}" && ret=1 |
0b5fe54f | 1693 | check_coverage_reports "$initdir" || ret=5 |
d3b8e384 DS |
1694 | |
1695 | save_journal "$initdir/var/log/journal" $ret | |
1696 | ||
fa1fdd30 LB |
1697 | _umount_dir "$initdir" |
1698 | ||
fa1fdd30 LB |
1699 | return $ret |
1700 | } | |
1701 | ||
889a9042 | 1702 | strip_binaries() { |
1b8fcd9c | 1703 | dinfo "Strip binaries" |
23f8e019 | 1704 | if ! get_bool "$STRIP_BINARIES"; then |
1b8fcd9c | 1705 | dinfo "STRIP_BINARIES == no, keeping binaries unstripped" |
5a613464 EV |
1706 | return 0 |
1707 | fi | |
1b8fcd9c FS |
1708 | while read -r bin; do |
1709 | strip --strip-unneeded "$bin" |& grep -vi 'file format not recognized' | ddebug || : | |
1710 | done < <(find "${initdir:?}" -executable -not -path '*/lib/modules/*.ko' -type f) | |
889a9042 RC |
1711 | } |
1712 | ||
1713 | create_rc_local() { | |
1b8fcd9c FS |
1714 | dinfo "Create rc.local" |
1715 | mkdir -p "${initdir:?}/etc/rc.d" | |
1716 | cat >"$initdir/etc/rc.d/rc.local" <<EOF | |
ff12a795 | 1717 | #!/usr/bin/env bash |
889a9042 RC |
1718 | exit 0 |
1719 | EOF | |
1b8fcd9c | 1720 | chmod 0755 "$initdir/etc/rc.d/rc.local" |
889a9042 RC |
1721 | } |
1722 | ||
1723 | install_execs() { | |
1b8fcd9c FS |
1724 | ddebug "Install executables from the service files" |
1725 | ||
1726 | local pkg_config_path="${BUILD_DIR:?}/src/core/" | |
1727 | local systemunitdir userunitdir exe | |
1728 | systemunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemdsystemunitdir systemd)" | |
1729 | userunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemduserunitdir systemd)" | |
1730 | while read -r exe; do | |
1731 | # some {rc,halt}.local scripts and programs are okay to not exist, the rest should | |
1732 | # also, plymouth is pulled in by rescue.service, but even there the exit code | |
1733 | # is ignored; as it's not present on some distros, don't fail if it doesn't exist | |
1734 | dinfo "Attempting to install $exe (based on unit file reference)" | |
1735 | inst "$exe" || [ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth" ] | |
1736 | done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service | sort -u) | |
889a9042 RC |
1737 | } |
1738 | ||
1739 | generate_module_dependencies() { | |
1b8fcd9c FS |
1740 | dinfo "Generate modules dependencies" |
1741 | if [[ -d "${initdir:?}/lib/modules/${KERNEL_VER:?}" ]] && \ | |
1742 | ! depmod -a -b "$initdir" "$KERNEL_VER"; then | |
889a9042 RC |
1743 | dfatal "\"depmod -a $KERNEL_VER\" failed." |
1744 | exit 1 | |
1745 | fi | |
1746 | } | |
1747 | ||
1748 | install_depmod_files() { | |
1b8fcd9c FS |
1749 | dinfo "Install depmod files" |
1750 | inst "/lib/modules/${KERNEL_VER:?}/modules.order" | |
1751 | inst "/lib/modules/$KERNEL_VER/modules.builtin" | |
889a9042 RC |
1752 | } |
1753 | ||
1754 | install_plymouth() { | |
1b8fcd9c | 1755 | dinfo "Install plymouth" |
889a9042 RC |
1756 | # install plymouth, if found... else remove plymouth service files |
1757 | # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then | |
1758 | # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \ | |
1759 | # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir | |
39e17536 | 1760 | # image_install plymouth plymouthd |
889a9042 | 1761 | # else |
1b8fcd9c | 1762 | rm -f "${initdir:?}"/{usr/lib,lib,etc}/systemd/system/plymouth* "$initdir"/{usr/lib,lib,etc}/systemd/system/*/plymouth* |
889a9042 RC |
1763 | # fi |
1764 | } | |
1765 | ||
d93857ae | 1766 | install_haveged() { |
ce380c2f | 1767 | # If haveged is installed, it's probably included in initrd and needs to be |
d93857ae FB |
1768 | # installed in the image too. |
1769 | if [ -x /usr/sbin/haveged ]; then | |
1770 | dinfo "Install haveged files" | |
1771 | inst /usr/sbin/haveged | |
ce380c2f | 1772 | for u in /usr/lib/systemd/system/haveged*; do |
1c3f490f | 1773 | inst "$u" |
ce380c2f | 1774 | done |
d93857ae FB |
1775 | fi |
1776 | } | |
1777 | ||
889a9042 | 1778 | install_ld_so_conf() { |
1b8fcd9c FS |
1779 | dinfo "Install /etc/ld.so.conf*" |
1780 | cp -a /etc/ld.so.conf* "${initdir:?}/etc" | |
889a9042 RC |
1781 | ldconfig -r "$initdir" |
1782 | } | |
1783 | ||
d0ac89a1 | 1784 | install_testuser() { |
1b8fcd9c | 1785 | dinfo "Set up a test user" |
d0ac89a1 | 1786 | # create unprivileged user for user manager tests |
1b8fcd9c FS |
1787 | mkdir -p "${initdir:?}/etc/sysusers.d" |
1788 | cat >"$initdir/etc/sysusers.d/testuser.conf" <<EOF | |
d0ac89a1 ZJS |
1789 | u testuser 4711 "Test User" /home/testuser |
1790 | EOF | |
1791 | ||
1b8fcd9c FS |
1792 | mkdir -p "$initdir/home/testuser" |
1793 | chmod 0700 "$initdir/home/testuser" | |
1794 | chown 4711:4711 "$initdir/home/testuser" | |
d0ac89a1 ZJS |
1795 | } |
1796 | ||
889a9042 | 1797 | install_config_files() { |
1b8fcd9c | 1798 | dinfo "Install config files" |
65dd488f | 1799 | inst /etc/sysconfig/init || : |
889a9042 RC |
1800 | inst /etc/passwd |
1801 | inst /etc/shadow | |
2aa5a13a | 1802 | inst_any /etc/login.defs /usr/etc/login.defs |
889a9042 RC |
1803 | inst /etc/group |
1804 | inst /etc/shells | |
9e173292 | 1805 | inst_any /etc/nsswitch.conf /usr/etc/nsswitch.conf |
65dd488f | 1806 | inst /etc/pam.conf || : |
ae6c5987 | 1807 | inst_any /etc/os-release /usr/lib/os-release |
889a9042 RC |
1808 | inst /etc/localtime |
1809 | # we want an empty environment | |
1b8fcd9c FS |
1810 | : >"${initdir:?}/etc/environment" |
1811 | : >"$initdir/etc/machine-id" | |
1812 | : >"$initdir/etc/resolv.conf" | |
7eeeab20 | 1813 | |
889a9042 | 1814 | # set the hostname |
ea0d33e2 | 1815 | echo 'H' >"$initdir/etc/hostname" |
a455e75a ZJS |
1816 | |
1817 | # let's set up just one image with the traditional verbose output | |
1b8fcd9c FS |
1818 | if [ "${IMAGE_NAME:?}" != "basic" ]; then |
1819 | mkdir -p "$initdir/etc/systemd/system.conf.d" | |
1820 | echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc/systemd/system.conf.d/status.conf" | |
a455e75a | 1821 | fi |
889a9042 RC |
1822 | } |
1823 | ||
1824 | install_basic_tools() { | |
1b8fcd9c | 1825 | dinfo "Install basic tools" |
39e17536 FS |
1826 | image_install "${BASICTOOLS[@]}" |
1827 | image_install -o sushell | |
7d023341 | 1828 | # in Debian ldconfig is just a shell script wrapper around ldconfig.real |
39e17536 | 1829 | image_install -o ldconfig.real |
889a9042 RC |
1830 | } |
1831 | ||
1832 | install_debug_tools() { | |
1b8fcd9c | 1833 | dinfo "Install debug tools" |
f7d47cc8 | 1834 | image_install -o "${DEBUGTOOLS[@]}" |
c81a46b9 | 1835 | |
23f8e019 | 1836 | if get_bool "$INTERACTIVE_DEBUG"; then |
c81a46b9 | 1837 | # Set default TERM from vt220 to linux, so at least basic key shortcuts work |
1b8fcd9c FS |
1838 | local getty_override="${initdir:?}/etc/systemd/system/serial-getty@.service.d" |
1839 | mkdir -p "$getty_override" | |
1840 | echo -e "[Service]\nEnvironment=TERM=linux" >"$getty_override/default-TERM.conf" | |
17082e8a | 1841 | echo 'export TERM=linux' >>"$initdir/etc/profile" |
c81a46b9 | 1842 | |
17082e8a FS |
1843 | if command -v resize >/dev/null; then |
1844 | image_install resize | |
1845 | echo "resize" >>"$initdir/etc/profile" | |
1846 | fi | |
5c08efee FS |
1847 | |
1848 | # Sometimes we might end up with plymouthd still running (especially | |
1849 | # with the initrd -> asan_wrapper -> systemd transition), which will eat | |
1850 | # our inputs and make debugging via tty impossible. Let's fix this by | |
1851 | # killing plymouthd explicitly for the interactive sessions. | |
1852 | # Note: we can't use pkill/pidof/etc. here due to a bug in libasan, see: | |
1853 | # - https://github.com/llvm/llvm-project/issues/49223 | |
1854 | # - https://bugzilla.redhat.com/show_bug.cgi?id=2098125 | |
1855 | local plymouth_unit="${initdir:?}/etc/systemd/system/kill-plymouth.service" | |
1856 | cat >"$plymouth_unit" <<EOF | |
1857 | [Unit] | |
1858 | After=multi-user.target | |
1859 | ||
1860 | [Service] | |
1861 | ExecStart=sh -c 'killall --verbose plymouthd || :' | |
1862 | ||
1863 | [Install] | |
1864 | WantedBy=multi-user.target | |
1865 | EOF | |
1866 | "${SYSTEMCTL:?}" enable --root "${initdir:?}" kill-plymouth.service | |
c81a46b9 | 1867 | fi |
889a9042 RC |
1868 | } |
1869 | ||
1870 | install_libnss() { | |
1b8fcd9c | 1871 | dinfo "Install libnss" |
889a9042 | 1872 | # install libnss_files for login |
0f194705 FS |
1873 | local NSS_LIBS |
1874 | mapfile -t NSS_LIBS < <(LD_DEBUG=files getent passwd 2>&1 >/dev/null | sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}') | |
90782fde FS |
1875 | if [[ ${#NSS_LIBS[@]} -gt 0 ]]; then |
1876 | image_install "${NSS_LIBS[@]}" | |
1877 | fi | |
889a9042 RC |
1878 | } |
1879 | ||
1880 | install_dbus() { | |
1b8fcd9c FS |
1881 | dinfo "Install dbus" |
1882 | inst "${ROOTLIBDIR:?}/system/dbus.socket" | |
a978c9f2 | 1883 | |
a49ad4c4 | 1884 | # Newer Fedora versions use dbus-broker by default. Let's install it if it's available. |
1b8fcd9c FS |
1885 | if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then |
1886 | inst "$ROOTLIBDIR/system/dbus-broker.service" | |
908665f4 LP |
1887 | inst_symlink /etc/systemd/system/dbus.service |
1888 | inst /usr/bin/dbus-broker | |
1889 | inst /usr/bin/dbus-broker-launch | |
1b8fcd9c | 1890 | elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then |
908665f4 | 1891 | # Fedora rawhide replaced dbus.service with dbus-daemon.service |
1b8fcd9c | 1892 | inst "$ROOTLIBDIR/system/dbus-daemon.service" |
a978c9f2 FS |
1893 | # Alias symlink |
1894 | inst_symlink /etc/systemd/system/dbus.service | |
1895 | else | |
1b8fcd9c | 1896 | inst "$ROOTLIBDIR/system/dbus.service" |
a978c9f2 | 1897 | fi |
889a9042 | 1898 | |
96af59aa FS |
1899 | while read -r file; do |
1900 | inst "$file" | |
1901 | done < <(find /etc/dbus-1 /usr/share/dbus-1 -xtype f 2>/dev/null) | |
bdfd515a ZJS |
1902 | |
1903 | # setup policy for Type=dbus test | |
1b8fcd9c FS |
1904 | mkdir -p "${initdir:?}/etc/dbus-1/system.d" |
1905 | cat >"$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf" <<EOF | |
bdfd515a ZJS |
1906 | <?xml version="1.0"?> |
1907 | <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" | |
41d6f3bf | 1908 | "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> |
bdfd515a ZJS |
1909 | <busconfig> |
1910 | <policy user="root"> | |
1911 | <allow own="systemd.test.ExecStopPost"/> | |
1912 | </policy> | |
1913 | </busconfig> | |
1914 | EOF | |
889a9042 RC |
1915 | } |
1916 | ||
a49ad4c4 | 1917 | install_user_dbus() { |
1b8fcd9c | 1918 | dinfo "Install user dbus" |
53a1c944 | 1919 | local userunitdir |
1b8fcd9c FS |
1920 | if ! userunitdir="$(pkg-config --variable=systemduserunitdir systemd)"; then |
1921 | dwarn "WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user" | |
1922 | userunitdir=/usr/lib/systemd/user | |
53a1c944 LB |
1923 | fi |
1924 | ||
1b8fcd9c FS |
1925 | inst "$userunitdir/dbus.socket" |
1926 | inst_symlink "$userunitdir/sockets.target.wants/dbus.socket" || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket | |
a49ad4c4 FB |
1927 | |
1928 | # Append the After= dependency on dbus in case it isn't already set up | |
1b8fcd9c FS |
1929 | mkdir -p "${initdir:?}/etc/systemd/system/user@.service.d/" |
1930 | cat >"$initdir/etc/systemd/system/user@.service.d/dbus.conf" <<EOF | |
a49ad4c4 FB |
1931 | [Unit] |
1932 | After=dbus.service | |
1933 | EOF | |
1934 | ||
1935 | # Newer Fedora versions use dbus-broker by default. Let's install it if it's available. | |
1b8fcd9c FS |
1936 | if [ -f "$userunitdir/dbus-broker.service" ]; then |
1937 | inst "$userunitdir/dbus-broker.service" | |
a49ad4c4 | 1938 | inst_symlink /etc/systemd/user/dbus.service |
1b8fcd9c | 1939 | elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then |
a49ad4c4 | 1940 | # Fedora rawhide replaced dbus.service with dbus-daemon.service |
1b8fcd9c | 1941 | inst "$userunitdir/dbus-daemon.service" |
a49ad4c4 FB |
1942 | # Alias symlink |
1943 | inst_symlink /etc/systemd/user/dbus.service | |
1944 | else | |
1b8fcd9c | 1945 | inst "$userunitdir/dbus.service" |
a49ad4c4 FB |
1946 | fi |
1947 | } | |
1948 | ||
889a9042 | 1949 | install_pam() { |
96af59aa FS |
1950 | dinfo "Install PAM" |
1951 | local paths=() | |
1952 | ||
23f8e019 | 1953 | if get_bool "$LOOKS_LIKE_DEBIAN" && type -p dpkg-architecture &>/dev/null; then |
96af59aa | 1954 | paths+=("/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security") |
818567fc | 1955 | else |
96af59aa | 1956 | paths+=(/lib*/security) |
818567fc | 1957 | fi |
96af59aa | 1958 | |
eef72224 | 1959 | for d in /etc/pam.d /{usr/,}etc/security /usr/{etc,lib}/pam.d; do |
96af59aa | 1960 | [ -d "$d" ] && paths+=("$d") |
889a9042 | 1961 | done |
417491f1 | 1962 | |
96af59aa FS |
1963 | while read -r file; do |
1964 | inst "$file" | |
1965 | done < <(find "${paths[@]}" -xtype f) | |
1966 | ||
d5172c79 EV |
1967 | # pam_unix depends on unix_chkpwd. |
1968 | # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html | |
39e17536 | 1969 | image_install -o unix_chkpwd |
d5172c79 | 1970 | |
e14b866b | 1971 | # set empty root password for easy debugging |
1b8fcd9c | 1972 | sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd" |
138f7619 FB |
1973 | |
1974 | # And make sure pam_unix will accept it by making sure that | |
1975 | # the PAM module has the nullok option. | |
1976 | for d in /etc/pam.d /usr/{etc,lib}/pam.d; do | |
1977 | [ -d "$initdir/$d" ] || continue | |
1978 | sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/* | |
1979 | done | |
889a9042 RC |
1980 | } |
1981 | ||
4ce68ea9 YW |
1982 | install_locales() { |
1983 | # install only C.UTF-8 and English locales | |
1984 | dinfo "Install locales" | |
1985 | ||
1986 | if command -v meson >/dev/null \ | |
1987 | && (meson configure "${BUILD_DIR:?}" | grep 'localegen-path */') \ | |
1988 | || get_bool "$LOOKS_LIKE_DEBIAN"; then | |
1989 | # locale-gen support | |
1990 | image_install -o locale-gen localedef | |
1991 | inst /etc/locale.gen || : | |
1992 | inst /usr/share/i18n/SUPPORTED || : | |
1993 | inst_recursive /usr/share/i18n/charmaps | |
1994 | inst_recursive /usr/share/i18n/locales | |
0c416ea0 FS |
1995 | inst_recursive /usr/share/locale/en* |
1996 | inst_recursive /usr/share/locale/de* | |
1997 | image_install /usr/share/locale/locale.alias | |
1998 | # locale-gen might either generate each locale separately or merge them | |
1999 | # into a single archive | |
2000 | if ! (inst_recursive /usr/lib/locale/C.*8 /usr/lib/locale/en_*8 || | |
2001 | image_install /usr/lib/locale/locale-archive); then | |
2002 | dfatal "Failed to install required locales" | |
2003 | exit 1 | |
2004 | fi | |
2005 | else | |
2006 | inst_recursive /usr/lib/locale/C.*8 /usr/lib/locale/en_*8 | |
4ce68ea9 | 2007 | fi |
4ce68ea9 YW |
2008 | } |
2009 | ||
1c3f490f | 2010 | # shellcheck disable=SC2120 |
889a9042 | 2011 | install_keymaps() { |
569c6fd1 YW |
2012 | local i p |
2013 | local -a prefix=( | |
1edad893 FS |
2014 | "/usr/lib" |
2015 | "/usr/share" | |
569c6fd1 | 2016 | ) |
ad931fee | 2017 | |
569c6fd1 YW |
2018 | dinfo "Install console keymaps" |
2019 | ||
2020 | if command -v meson >/dev/null \ | |
2021 | && [[ "$(meson configure "${BUILD_DIR:?}" | grep 'split-usr' | awk '{ print $2 }')" == "true" ]] \ | |
2022 | || [[ ! -L /lib ]]; then | |
2023 | prefix+=( | |
1edad893 | 2024 | "/lib" |
569c6fd1 YW |
2025 | ) |
2026 | fi | |
2027 | ||
2028 | if (( $# == 0 )); then | |
2029 | for p in "${prefix[@]}"; do | |
2030 | # The first three paths may be deprecated. | |
2031 | # It seems now the last three paths are used by many distributions. | |
2032 | for i in \ | |
1edad893 FS |
2033 | "$p"/kbd/keymaps/include/* \ |
2034 | "$p"/kbd/keymaps/i386/include/* \ | |
2035 | "$p"/kbd/keymaps/i386/qwerty/us.* \ | |
2036 | "$p"/kbd/keymaps/legacy/include/* \ | |
2037 | "$p"/kbd/keymaps/legacy/i386/qwerty/us.* \ | |
2038 | "$p"/kbd/keymaps/xkb/us*; do | |
569c6fd1 YW |
2039 | [[ -f "$i" ]] || continue |
2040 | inst "$i" | |
2041 | done | |
2042 | done | |
2043 | else | |
2044 | # When it takes any argument, then install more keymaps. | |
2045 | for p in "${prefix[@]}"; do | |
2046 | for i in \ | |
1edad893 FS |
2047 | "$p"/kbd/keymaps/include/* \ |
2048 | "$p"/kbd/keymaps/i386/*/* \ | |
2049 | "$p"/kbd/keymaps/legacy/i386/*/* \ | |
2050 | "$p"/kbd/keymaps/xkb/*; do | |
569c6fd1 YW |
2051 | [[ -f "$i" ]] || continue |
2052 | inst "$i" | |
2053 | done | |
ad931fee YW |
2054 | done |
2055 | fi | |
889a9042 RC |
2056 | } |
2057 | ||
1136175c YW |
2058 | install_x11_keymaps() { |
2059 | dinfo "Install x11 keymaps" | |
2060 | ||
2061 | if (( $# == 0 )); then | |
2062 | # Install only keymap list. | |
2063 | inst /usr/share/X11/xkb/rules/base.lst | |
2064 | else | |
2065 | # When it takes any argument, then install all keymaps. | |
2066 | inst_recursive /usr/share/X11/xkb | |
2067 | fi | |
2068 | } | |
2069 | ||
7d10ec1c | 2070 | install_zoneinfo() { |
1b8fcd9c | 2071 | dinfo "Install time zones" |
f4c40fd7 ZJS |
2072 | inst_any /usr/share/zoneinfo/Asia/Seoul |
2073 | inst_any /usr/share/zoneinfo/Asia/Vladivostok | |
2074 | inst_any /usr/share/zoneinfo/Australia/Sydney | |
2075 | inst_any /usr/share/zoneinfo/Europe/Berlin | |
129cb6e2 | 2076 | inst_any /usr/share/zoneinfo/Europe/Dublin |
f4c40fd7 ZJS |
2077 | inst_any /usr/share/zoneinfo/Europe/Kiev |
2078 | inst_any /usr/share/zoneinfo/Pacific/Auckland | |
2079 | inst_any /usr/share/zoneinfo/Pacific/Honolulu | |
2080 | inst_any /usr/share/zoneinfo/CET | |
2081 | inst_any /usr/share/zoneinfo/EET | |
2082 | inst_any /usr/share/zoneinfo/UTC | |
7d10ec1c YW |
2083 | } |
2084 | ||
889a9042 | 2085 | install_fonts() { |
1b8fcd9c | 2086 | dinfo "Install system fonts" |
889a9042 | 2087 | for i in \ |
25b47f96 | 2088 | /usr/lib/kbd/consolefonts/eurlatgr* \ |
889a9042 | 2089 | /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do |
1b8fcd9c FS |
2090 | [[ -f "$i" ]] || continue |
2091 | inst "$i" | |
889a9042 RC |
2092 | done |
2093 | } | |
2094 | ||
2095 | install_terminfo() { | |
1b8fcd9c FS |
2096 | dinfo "Install terminfo files" |
2097 | local terminfodir | |
2098 | for terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do | |
2099 | [ -f "${terminfodir}/l/linux" ] && break | |
889a9042 | 2100 | done |
39e17536 | 2101 | image_install -o "${terminfodir}/l/linux" |
889a9042 RC |
2102 | } |
2103 | ||
a49ad4c4 FB |
2104 | has_user_dbus_socket() { |
2105 | if [ -f /usr/lib/systemd/user/dbus.socket ] || [ -f /etc/systemd/user/dbus.socket ]; then | |
2106 | return 0 | |
2107 | else | |
2108 | echo "Per-user instances are not supported. Skipping..." | |
2109 | return 1 | |
2110 | fi | |
2111 | } | |
2112 | ||
48f3bc5c LN |
2113 | setup_nspawn_root_hook() { :;} |
2114 | ||
889a9042 | 2115 | setup_nspawn_root() { |
8c3534b5 ZJS |
2116 | if [ -z "${initdir}" ]; then |
2117 | dfatal "\$initdir not defined" | |
2118 | exit 1 | |
2119 | fi | |
ec43f686 | 2120 | |
1b8fcd9c | 2121 | rm -rf "${TESTDIR:?}/unprivileged-nspawn-root" |
693ad298 | 2122 | |
23f8e019 | 2123 | if get_bool "$RUN_IN_UNPRIVILEGED_CONTAINER"; then |
ec43f686 | 2124 | ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root" |
1b8fcd9c | 2125 | cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root" |
746fbd9c | 2126 | fi |
48f3bc5c LN |
2127 | |
2128 | setup_nspawn_root_hook | |
889a9042 RC |
2129 | } |
2130 | ||
0d6e798a | 2131 | setup_basic_dirs() { |
1b8fcd9c FS |
2132 | mkdir -p "${initdir:?}/run" |
2133 | mkdir -p "$initdir/etc/systemd/system" | |
2134 | mkdir -p "$initdir/var/log/journal" | |
2135 | ||
889a9042 | 2136 | |
1b8fcd9c | 2137 | for d in usr/bin usr/sbin bin etc lib "${libdir:?}" sbin tmp usr var var/log var/tmp dev proc sys sysroot root run run/lock run/initramfs; do |
898720b7 HH |
2138 | if [ -L "/$d" ]; then |
2139 | inst_symlink "/$d" | |
2140 | else | |
0d6e798a | 2141 | inst_dir "/$d" |
898720b7 HH |
2142 | fi |
2143 | done | |
2144 | ||
2145 | ln -sfn /run "$initdir/var/run" | |
2146 | ln -sfn /run/lock "$initdir/var/lock" | |
2147 | } | |
2148 | ||
51fa8591 ZJS |
2149 | mask_supporting_services() { |
2150 | # mask some services that we do not want to run in these tests | |
7776b225 FS |
2151 | ln -fsv /dev/null "${initdir:?}/etc/systemd/system/systemd-hwdb-update.service" |
2152 | ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-journal-catalog-update.service" | |
2153 | ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-networkd.service" | |
2154 | ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-networkd.socket" | |
2155 | ln -fsv /dev/null "$initdir/etc/systemd/system/systemd-resolved.service" | |
51fa8591 ZJS |
2156 | } |
2157 | ||
898720b7 | 2158 | inst_libs() { |
96af59aa FS |
2159 | local bin="${1:?}" |
2160 | local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)' | |
2161 | local file line | |
898720b7 | 2162 | |
96af59aa FS |
2163 | while read -r line; do |
2164 | [[ "$line" = 'not a dynamic executable' ]] && break | |
ff254eea ZJS |
2165 | # Ignore errors about our own stuff missing. This is most likely caused |
2166 | # by ldd attempting to use the unprefixed RPATH. | |
2167 | [[ "$line" =~ libsystemd.*\ not\ found ]] && continue | |
898720b7 | 2168 | |
96af59aa FS |
2169 | if [[ "$line" =~ $so_regex ]]; then |
2170 | file="${BASH_REMATCH[1]}" | |
2171 | [[ -e "${initdir:?}/$file" ]] && continue | |
2172 | inst_library "$file" | |
898720b7 HH |
2173 | continue |
2174 | fi | |
2175 | ||
96af59aa FS |
2176 | if [[ "$line" =~ not\ found ]]; then |
2177 | dfatal "Missing a shared library required by $bin." | |
2178 | dfatal "Run \"ldd $bin\" to find out what it is." | |
2179 | dfatal "$line" | |
39e17536 | 2180 | dfatal "Cannot create a test image." |
898720b7 HH |
2181 | exit 1 |
2182 | fi | |
96af59aa | 2183 | done < <(LC_ALL=C ldd "$bin" 2>/dev/null) |
898720b7 HH |
2184 | } |
2185 | ||
2186 | import_testdir() { | |
1506edca | 2187 | # make sure we don't get a stale LOOPDEV value from old times |
1b8fcd9c FS |
2188 | local _LOOPDEV="${LOOPDEV:=}" |
2189 | # We don't want shellcheck to follow & check the $STATEFILE | |
2190 | # shellcheck source=/dev/null | |
2191 | [[ -e "$STATEFILE" ]] && . "$STATEFILE" | |
2192 | LOOPDEV="$_LOOPDEV" | |
3f50fff5 FS |
2193 | if [[ ! -d "$TESTDIR" ]]; then |
2194 | if [[ -z "$TESTDIR" ]]; then | |
1b8fcd9c | 2195 | TESTDIR="$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)" |
3f50fff5 FS |
2196 | else |
2197 | mkdir -p "$TESTDIR" | |
2198 | fi | |
2199 | ||
1b8fcd9c | 2200 | cat >"$STATEFILE" <<EOF |
8c3534b5 | 2201 | TESTDIR="$TESTDIR" |
8c3534b5 | 2202 | EOF |
898720b7 HH |
2203 | export TESTDIR |
2204 | fi | |
e8945092 | 2205 | |
1b8fcd9c FS |
2206 | IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME:?}.img" |
2207 | IMAGE_PUBLIC="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img" | |
898720b7 HH |
2208 | } |
2209 | ||
889a9042 | 2210 | import_initdir() { |
1b8fcd9c FS |
2211 | initdir="${TESTDIR:?}/root" |
2212 | mkdir -p "$initdir" | |
889a9042 RC |
2213 | export initdir |
2214 | } | |
2215 | ||
f7237408 FS |
2216 | get_cgroup_hierarchy() { |
2217 | case "$(stat -c '%T' -f /sys/fs/cgroup)" in | |
2218 | cgroup2fs) | |
2219 | echo "unified" | |
2220 | ;; | |
2221 | tmpfs) | |
2222 | if [[ -d /sys/fs/cgroup/unified && "$(stat -c '%T' -f /sys/fs/cgroup/unified)" == cgroup2fs ]]; then | |
2223 | echo "hybrid" | |
2224 | else | |
2225 | echo "legacy" | |
2226 | fi | |
2227 | ;; | |
2228 | *) | |
2229 | dfatal "Failed to determine host's cgroup hierarchy" | |
2230 | exit 1 | |
2231 | esac | |
2232 | } | |
2233 | ||
898720b7 HH |
2234 | ## @brief Converts numeric logging level to the first letter of level name. |
2235 | # | |
2236 | # @param lvl Numeric logging level in range from 1 to 6. | |
2237 | # @retval 1 if @a lvl is out of range. | |
2238 | # @retval 0 if @a lvl is correct. | |
2239 | # @result Echoes first letter of level name. | |
2240 | _lvl2char() { | |
2241 | case "$1" in | |
2242 | 1) echo F;; | |
2243 | 2) echo E;; | |
2244 | 3) echo W;; | |
2245 | 4) echo I;; | |
2246 | 5) echo D;; | |
2247 | 6) echo T;; | |
2248 | *) return 1;; | |
2249 | esac | |
2250 | } | |
2251 | ||
2252 | ## @brief Internal helper function for _do_dlog() | |
2253 | # | |
2254 | # @param lvl Numeric logging level. | |
2255 | # @param msg Message. | |
2256 | # @retval 0 It's always returned, even if logging failed. | |
2257 | # | |
2258 | # @note This function is not supposed to be called manually. Please use | |
2259 | # dtrace(), ddebug(), or others instead which wrap this one. | |
2260 | # | |
2261 | # This function calls _do_dlog() either with parameter msg, or if | |
2262 | # none is given, it will read standard input and will use every line as | |
2263 | # a message. | |
2264 | # | |
2265 | # This enables: | |
2266 | # dwarn "This is a warning" | |
2267 | # echo "This is a warning" | dwarn | |
1b8fcd9c | 2268 | LOG_LEVEL="${LOG_LEVEL:-4}" |
898720b7 HH |
2269 | |
2270 | dlog() { | |
1b8fcd9c FS |
2271 | local lvl lvlc |
2272 | ||
898720b7 | 2273 | [ -z "$LOG_LEVEL" ] && return 0 |
1b8fcd9c FS |
2274 | lvl="${1:?}"; shift |
2275 | [ "$lvl" -le "$LOG_LEVEL" ] || return 0 | |
2276 | lvlc="$(_lvl2char "$lvl")" || return 0 | |
898720b7 HH |
2277 | |
2278 | if [ $# -ge 1 ]; then | |
2279 | echo "$lvlc: $*" | |
2280 | else | |
1b8fcd9c | 2281 | while read -r line; do |
898720b7 HH |
2282 | echo "$lvlc: " "$line" |
2283 | done | |
2284 | fi | |
2285 | } | |
2286 | ||
2287 | ## @brief Logs message at TRACE level (6) | |
2288 | # | |
2289 | # @param msg Message. | |
2290 | # @retval 0 It's always returned, even if logging failed. | |
2291 | dtrace() { | |
2292 | set +x | |
2293 | dlog 6 "$@" | |
23f8e019 | 2294 | if get_bool "${debug:=}"; then |
1b8fcd9c FS |
2295 | set -x |
2296 | fi | |
898720b7 HH |
2297 | } |
2298 | ||
2299 | ## @brief Logs message at DEBUG level (5) | |
2300 | # | |
2301 | # @param msg Message. | |
2302 | # @retval 0 It's always returned, even if logging failed. | |
2303 | ddebug() { | |
898720b7 | 2304 | dlog 5 "$@" |
898720b7 HH |
2305 | } |
2306 | ||
2307 | ## @brief Logs message at INFO level (4) | |
2308 | # | |
2309 | # @param msg Message. | |
2310 | # @retval 0 It's always returned, even if logging failed. | |
2311 | dinfo() { | |
2312 | set +x | |
2313 | dlog 4 "$@" | |
23f8e019 | 2314 | if get_bool "${debug:=}"; then |
1b8fcd9c FS |
2315 | set -x |
2316 | fi | |
898720b7 HH |
2317 | } |
2318 | ||
2319 | ## @brief Logs message at WARN level (3) | |
2320 | # | |
2321 | # @param msg Message. | |
2322 | # @retval 0 It's always returned, even if logging failed. | |
2323 | dwarn() { | |
2324 | set +x | |
2325 | dlog 3 "$@" | |
23f8e019 | 2326 | if get_bool "${debug:=}"; then |
1b8fcd9c FS |
2327 | set -x |
2328 | fi | |
898720b7 HH |
2329 | } |
2330 | ||
2331 | ## @brief Logs message at ERROR level (2) | |
2332 | # | |
2333 | # @param msg Message. | |
2334 | # @retval 0 It's always returned, even if logging failed. | |
2335 | derror() { | |
898720b7 | 2336 | dlog 2 "$@" |
898720b7 HH |
2337 | } |
2338 | ||
2339 | ## @brief Logs message at FATAL level (1) | |
2340 | # | |
2341 | # @param msg Message. | |
2342 | # @retval 0 It's always returned, even if logging failed. | |
2343 | dfatal() { | |
2344 | set +x | |
2345 | dlog 1 "$@" | |
23f8e019 | 2346 | if get_bool "${debug:=}"; then |
1b8fcd9c FS |
2347 | set -x |
2348 | fi | |
898720b7 HH |
2349 | } |
2350 | ||
2351 | ||
2352 | # Generic substring function. If $2 is in $1, return 0. | |
c049acb2 | 2353 | strstr() { [ "${1#*"$2"*}" != "$1" ]; } |
898720b7 HH |
2354 | |
2355 | # normalize_path <path> | |
2356 | # Prints the normalized path, where it removes any duplicated | |
2357 | # and trailing slashes. | |
2358 | # Example: | |
2359 | # $ normalize_path ///test/test// | |
2360 | # /test/test | |
2361 | normalize_path() { | |
2362 | shopt -q -s extglob | |
2363 | set -- "${1//+(\/)//}" | |
2364 | shopt -q -u extglob | |
2365 | echo "${1%/}" | |
2366 | } | |
2367 | ||
2368 | # convert_abs_rel <from> <to> | |
2369 | # Prints the relative path, when creating a symlink to <to> from <from>. | |
2370 | # Example: | |
2371 | # $ convert_abs_rel /usr/bin/test /bin/test-2 | |
2372 | # ../../bin/test-2 | |
2373 | # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test | |
2374 | convert_abs_rel() { | |
2375 | local __current __absolute __abssize __cursize __newpath | |
2376 | local -i __i __level | |
2377 | ||
1b8fcd9c | 2378 | set -- "$(normalize_path "${1:?}")" "$(normalize_path "${2:?}")" |
898720b7 HH |
2379 | |
2380 | # corner case #1 - self looping link | |
2381 | [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; } | |
2382 | ||
2383 | # corner case #2 - own dir link | |
2384 | [[ "${1%/*}" == "$2" ]] && { echo "."; return; } | |
2385 | ||
1b8fcd9c FS |
2386 | IFS="/" read -ra __current <<< "$1" |
2387 | IFS="/" read -ra __absolute <<< "$2" | |
898720b7 HH |
2388 | |
2389 | __abssize=${#__absolute[@]} | |
2390 | __cursize=${#__current[@]} | |
2391 | ||
4627fb80 | 2392 | while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]; do |
898720b7 HH |
2393 | (( __level++ )) |
2394 | if (( __level > __abssize || __level > __cursize )) | |
2395 | then | |
2396 | break | |
2397 | fi | |
2398 | done | |
2399 | ||
4627fb80 | 2400 | for ((__i = __level; __i < __cursize-1; __i++)); do |
898720b7 HH |
2401 | if ((__i > __level)) |
2402 | then | |
2403 | __newpath=$__newpath"/" | |
2404 | fi | |
2405 | __newpath=$__newpath".." | |
2406 | done | |
2407 | ||
4627fb80 | 2408 | for ((__i = __level; __i < __abssize; __i++)); do |
898720b7 HH |
2409 | if [[ -n $__newpath ]] |
2410 | then | |
2411 | __newpath=$__newpath"/" | |
2412 | fi | |
2413 | __newpath=$__newpath${__absolute[__i]} | |
2414 | done | |
2415 | ||
2416 | echo "$__newpath" | |
2417 | } | |
2418 | ||
2419 | ||
2420 | # Install a directory, keeping symlinks as on the original system. | |
2421 | # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file" | |
2422 | # will create ${initdir}/lib64, ${initdir}/lib64/file, | |
2423 | # and a symlink ${initdir}/lib -> lib64. | |
2424 | inst_dir() { | |
1b8fcd9c FS |
2425 | local dir="${1:?}" |
2426 | local part="${dir%/*}" | |
2427 | local file | |
898720b7 | 2428 | |
1b8fcd9c FS |
2429 | [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there |
2430 | ||
2431 | while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do | |
2432 | dir="$part $dir" | |
2433 | part="${part%/*}" | |
898720b7 HH |
2434 | done |
2435 | ||
2436 | # iterate over parent directories | |
1b8fcd9c FS |
2437 | for file in $dir; do |
2438 | [[ -e "${initdir}/$file" ]] && continue | |
2439 | if [[ -L $file ]]; then | |
2440 | inst_symlink "$file" | |
898720b7 HH |
2441 | else |
2442 | # create directory | |
1b8fcd9c FS |
2443 | mkdir -m 0755 "${initdir}/$file" || return 1 |
2444 | [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file" | |
2445 | chmod u+w "${initdir}/$file" | |
898720b7 HH |
2446 | fi |
2447 | done | |
2448 | } | |
2449 | ||
2450 | # $1 = file to copy to ramdisk | |
2451 | # $2 (optional) Name for the file on the ramdisk | |
2452 | # Location of the image dir is assumed to be $initdir | |
2453 | # We never overwrite the target if it exists. | |
2454 | inst_simple() { | |
1b8fcd9c | 2455 | [[ -f "${1:?}" ]] || return 1 |
898720b7 HH |
2456 | strstr "$1" "/" || return 1 |
2457 | ||
1b8fcd9c FS |
2458 | local src="$1" |
2459 | local target="${2:-$1}" | |
2460 | if ! [[ -d ${initdir:?}/$target ]]; then | |
898720b7 HH |
2461 | [[ -e ${initdir}/$target ]] && return 0 |
2462 | [[ -L ${initdir}/$target ]] && return 0 | |
2463 | [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}" | |
2464 | fi | |
2465 | # install checksum files also | |
1b8fcd9c FS |
2466 | if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then |
2467 | inst "${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac" | |
898720b7 | 2468 | fi |
1b8fcd9c FS |
2469 | ddebug "Installing $src" |
2470 | cp --sparse=always -pfL "$src" "${initdir}/$target" | |
898720b7 HH |
2471 | } |
2472 | ||
2473 | # find symlinks linked to given library file | |
2474 | # $1 = library file | |
2475 | # Function searches for symlinks by stripping version numbers appended to | |
2476 | # library filename, checks if it points to the same target and finally | |
2477 | # prints the list of symlinks to stdout. | |
2478 | # | |
2479 | # Example: | |
2480 | # rev_lib_symlinks libfoo.so.8.1 | |
2481 | # output: libfoo.so.8 libfoo.so | |
2482 | # (Only if libfoo.so.8 and libfoo.so exists on host system.) | |
2483 | rev_lib_symlinks() { | |
1b8fcd9c FS |
2484 | local fn="${1:?}" |
2485 | local links="" | |
2486 | local orig | |
2487 | orig="$(readlink -f "$1")" | |
898720b7 | 2488 | |
1b8fcd9c | 2489 | [[ "${fn}" =~ .*\.so\..* ]] || return 1 |
898720b7 | 2490 | |
1b8fcd9c | 2491 | until [[ "${fn##*.}" == so ]]; do |
898720b7 | 2492 | fn="${fn%.*}" |
1b8fcd9c | 2493 | [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}" |
898720b7 HH |
2494 | done |
2495 | ||
2496 | echo "${links}" | |
2497 | } | |
2498 | ||
2499 | # Same as above, but specialized to handle dynamic libraries. | |
2500 | # It handles making symlinks according to how the original library | |
2501 | # is referenced. | |
2502 | inst_library() { | |
1b8fcd9c FS |
2503 | local src="${1:?}" |
2504 | local dest="${2:-$1}" | |
2505 | local reallib symlink | |
2506 | ||
898720b7 | 2507 | strstr "$1" "/" || return 1 |
1b8fcd9c FS |
2508 | [[ -e ${initdir:?}/$dest ]] && return 0 |
2509 | if [[ -L $src ]]; then | |
898720b7 | 2510 | # install checksum files also |
1b8fcd9c FS |
2511 | if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then |
2512 | inst "${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac" | |
898720b7 | 2513 | fi |
1b8fcd9c FS |
2514 | reallib="$(readlink -f "$src")" |
2515 | inst_simple "$reallib" "$reallib" | |
2516 | inst_dir "${dest%/*}" | |
2517 | [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}" | |
134d4f1b | 2518 | ddebug "Creating symlink $reallib -> $dest" |
1b8fcd9c | 2519 | ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}" |
898720b7 | 2520 | else |
1b8fcd9c | 2521 | inst_simple "$src" "$dest" |
898720b7 HH |
2522 | fi |
2523 | ||
2524 | # Create additional symlinks. See rev_symlinks description. | |
1b8fcd9c FS |
2525 | for symlink in $(rev_lib_symlinks "$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do |
2526 | if [[ ! -e "$initdir/$symlink" ]]; then | |
2527 | ddebug "Creating extra symlink: $symlink" | |
2528 | inst_symlink "$symlink" | |
2529 | fi | |
898720b7 HH |
2530 | done |
2531 | } | |
2532 | ||
2533 | # find a binary. If we were not passed the full path directly, | |
2534 | # search in the usual places to find the binary. | |
2535 | find_binary() { | |
1b8fcd9c FS |
2536 | local bin="${1:?}" |
2537 | if [[ -z ${bin##/*} ]]; then | |
2538 | if [[ -x "$bin" ]] || { strstr "$bin" ".so" && ldd "$bin" &>/dev/null; }; then | |
2539 | echo "$bin" | |
898720b7 HH |
2540 | return 0 |
2541 | fi | |
2542 | fi | |
2543 | ||
1b8fcd9c | 2544 | type -P "$bin" |
898720b7 HH |
2545 | } |
2546 | ||
2547 | # Same as above, but specialized to install binary executables. | |
2548 | # Install binary executable, and all shared library dependencies, if any. | |
2549 | inst_binary() { | |
1b8fcd9c FS |
2550 | local bin="${1:?}" |
2551 | local path target | |
3cdb93d0 FS |
2552 | |
2553 | # In certain cases we might attempt to install a binary which is already | |
2554 | # present in the test image, yet it's missing from the host system. | |
2555 | # In such cases, let's check if the binary indeed exists in the image | |
ff254eea | 2556 | # before doing any other checks. If it does, immediately return with |
3cdb93d0 | 2557 | # success. |
1b8fcd9c FS |
2558 | if [[ $# -eq 1 ]]; then |
2559 | for path in "" bin sbin usr/bin usr/sbin; do | |
2560 | [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0 | |
2561 | done | |
2562 | fi | |
3cdb93d0 | 2563 | |
1b8fcd9c FS |
2564 | bin="$(find_binary "$bin")" || return 1 |
2565 | target="${2:-$bin}" | |
2566 | [[ -e "${initdir:?}/$target" ]] && return 0 | |
2567 | [[ -L "$bin" ]] && inst_symlink "$bin" "$target" && return 0 | |
2568 | ||
2569 | local file line | |
2570 | local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)' | |
3917534d FS |
2571 | # DSOs provided by systemd |
2572 | local systemd_so_regex='/(libudev|libsystemd.*|.+[\-_]systemd([\-_].+)?|libnss_(mymachines|myhostname|resolve)).so' | |
2573 | local wrap_binary=0 | |
898720b7 | 2574 | # I love bash! |
1b8fcd9c FS |
2575 | while read -r line; do |
2576 | [[ "$line" = 'not a dynamic executable' ]] && break | |
898720b7 | 2577 | |
ff254eea ZJS |
2578 | # Ignore errors about our own stuff missing. This is most likely caused |
2579 | # by ldd attempting to use the unprefixed RPATH. | |
2580 | [[ "$line" =~ libsystemd.*\ not\ found ]] && continue | |
2581 | ||
3917534d FS |
2582 | # We're built with ASan and the target binary loads one of the systemd's |
2583 | # DSOs, so we need to tweak the environment before executing the binary | |
2584 | if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$line" =~ $systemd_so_regex ]]; then | |
2585 | wrap_binary=1 | |
2586 | fi | |
2587 | ||
1b8fcd9c FS |
2588 | if [[ "$line" =~ $so_regex ]]; then |
2589 | file="${BASH_REMATCH[1]}" | |
2590 | [[ -e "${initdir}/$file" ]] && continue | |
2591 | inst_library "$file" | |
898720b7 HH |
2592 | continue |
2593 | fi | |
2594 | ||
1b8fcd9c FS |
2595 | if [[ "$line" =~ not\ found ]]; then |
2596 | dfatal "Missing a shared library required by $bin." | |
2597 | dfatal "Run \"ldd $bin\" to find out what it is." | |
2598 | dfatal "$line" | |
39e17536 | 2599 | dfatal "Cannot create a test image." |
898720b7 HH |
2600 | exit 1 |
2601 | fi | |
1b8fcd9c | 2602 | done < <(LC_ALL=C ldd "$bin" 2>/dev/null) |
3917534d FS |
2603 | |
2604 | # Same as above, but we need to wrap certain libraries unconditionally | |
2605 | # | |
961549ab | 2606 | # chown, getent, login, su, useradd, userdel - dlopen()s (not only) systemd's PAM modules |
5ad15138 FS |
2607 | # ls, stat - pulls in nss_systemd with certain options (like ls -l) when |
2608 | # nsswitch.conf uses [SUCCESS=merge] (like on Arch Linux) | |
3917534d | 2609 | # tar - called by machinectl in TEST-25 |
961549ab | 2610 | if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ /(chown|getent|login|ls|stat|su|tar|useradd|userdel)$ ]]; then |
3917534d FS |
2611 | wrap_binary=1 |
2612 | fi | |
2613 | ||
b727d7e0 FS |
2614 | # If the target binary is built with ASan support, we don't need to wrap |
2615 | # it, as it should handle everything by itself | |
2616 | if get_bool "$wrap_binary" && ! is_built_with_asan "$bin"; then | |
3917534d FS |
2617 | dinfo "Creating ASan-compatible wrapper for binary '$target'" |
2618 | # Install the target binary with a ".orig" suffix | |
2619 | inst_simple "$bin" "${target}.orig" | |
2620 | # Create a simple shell wrapper in place of the target binary, which | |
2621 | # sets necessary ASan-related env variables and then exec()s the | |
2622 | # suffixed target binary | |
2623 | cat >"$initdir/$target" <<EOF | |
2624 | #!/bin/bash | |
2625 | # Preload the ASan runtime DSO, otherwise ASAn will complain | |
2626 | export LD_PRELOAD="$ASAN_RT_PATH" | |
2627 | # Disable LSan to speed things up, since we don't care about leak reports | |
2628 | # from 'external' binaries | |
2629 | export ASAN_OPTIONS=detect_leaks=0 | |
2630 | # Set argv[0] to the original binary name without the ".orig" suffix | |
2631 | exec -a "\$0" -- "${target}.orig" "\$@" | |
2632 | EOF | |
2633 | chmod +x "$initdir/$target" | |
2634 | else | |
2635 | inst_simple "$bin" "$target" | |
2636 | fi | |
898720b7 HH |
2637 | } |
2638 | ||
2639 | # same as above, except for shell scripts. | |
2640 | # If your shell script does not start with shebang, it is not a shell script. | |
2641 | inst_script() { | |
1b8fcd9c FS |
2642 | local bin line shebang_regex |
2643 | bin="$(find_binary "${1:?}")" || return 1 | |
898720b7 | 2644 | shift |
1b8fcd9c FS |
2645 | |
2646 | read -r -n 80 line <"$bin" | |
898720b7 | 2647 | # If debug is set, clean unprintable chars to prevent messing up the term |
23f8e019 | 2648 | get_bool "${debug:=}" && line="$(echo -n "$line" | tr -c -d '[:print:][:space:]')" |
1b8fcd9c FS |
2649 | shebang_regex='(#! *)(/[^ ]+).*' |
2650 | [[ "$line" =~ $shebang_regex ]] || return 1 | |
2651 | inst "${BASH_REMATCH[2]}" && inst_simple "$bin" "$@" | |
898720b7 HH |
2652 | } |
2653 | ||
2654 | # same as above, but specialized for symlinks | |
2655 | inst_symlink() { | |
1b8fcd9c FS |
2656 | local src="${1:?}" |
2657 | local target="${2:-$src}" | |
2658 | local realsrc | |
2659 | ||
2660 | strstr "$src" "/" || return 1 | |
2661 | [[ -L "$src" ]] || return 1 | |
2662 | [[ -L "${initdir:?}/$target" ]] && return 0 | |
2663 | realsrc="$(readlink -f "$src")" | |
2664 | if ! [[ -e "$initdir/$realsrc" ]]; then | |
2665 | if [[ -d "$realsrc" ]]; then | |
2666 | inst_dir "$realsrc" | |
898720b7 | 2667 | else |
1b8fcd9c | 2668 | inst "$realsrc" |
898720b7 HH |
2669 | fi |
2670 | fi | |
1b8fcd9c FS |
2671 | [[ ! -e "$initdir/${target%/*}" ]] && inst_dir "${target%/*}" |
2672 | [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}" | |
2673 | ln -sfn -- "$(convert_abs_rel "${target}" "${realsrc}")" "$initdir/$target" | |
898720b7 HH |
2674 | } |
2675 | ||
2676 | # attempt to install any programs specified in a udev rule | |
2677 | inst_rule_programs() { | |
1b8fcd9c FS |
2678 | local rule="${1:?}" |
2679 | local prog bin | |
898720b7 | 2680 | |
29bff80b | 2681 | sed -rn 's/^.*?PROGRAM==?"([^ "]+).*$/\1/p' "$rule" | while read -r prog; do |
1b8fcd9c FS |
2682 | if [ -x "/lib/udev/$prog" ]; then |
2683 | bin="/lib/udev/$prog" | |
2684 | else | |
2685 | if ! bin="$(find_binary "$prog")"; then | |
2686 | dinfo "Skipping program $prog used in udev rule $(basename "$rule") as it cannot be found" | |
2687 | continue | |
898720b7 | 2688 | fi |
1b8fcd9c | 2689 | fi |
898720b7 | 2690 | |
1b8fcd9c | 2691 | #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)" |
39e17536 | 2692 | image_install "$bin" |
1b8fcd9c | 2693 | done |
898720b7 HH |
2694 | } |
2695 | ||
2696 | # udev rules always get installed in the same place, so | |
2697 | # create a function to install them to make life simpler. | |
2698 | inst_rules() { | |
1b8fcd9c FS |
2699 | local target=/etc/udev/rules.d |
2700 | local found rule | |
898720b7 HH |
2701 | |
2702 | inst_dir "/lib/udev/rules.d" | |
1b8fcd9c FS |
2703 | inst_dir "$target" |
2704 | for rule in "$@"; do | |
898720b7 HH |
2705 | if [ "${rule#/}" = "$rule" ]; then |
2706 | for r in /lib/udev/rules.d /etc/udev/rules.d; do | |
1b8fcd9c FS |
2707 | if [[ -f "$r/$rule" ]]; then |
2708 | found="$r/$rule" | |
2709 | inst_simple "$found" | |
2710 | inst_rule_programs "$found" | |
898720b7 HH |
2711 | fi |
2712 | done | |
2713 | fi | |
1b8fcd9c FS |
2714 | for r in '' ./; do |
2715 | if [[ -f "${r}${rule}" ]]; then | |
2716 | found="${r}${rule}" | |
2717 | inst_simple "$found" "$target/${found##*/}" | |
2718 | inst_rule_programs "$found" | |
898720b7 HH |
2719 | fi |
2720 | done | |
1b8fcd9c FS |
2721 | [[ $found ]] || dinfo "Skipping udev rule: $rule" |
2722 | found= | |
898720b7 HH |
2723 | done |
2724 | } | |
2725 | ||
2726 | # general purpose installation function | |
2727 | # Same args as above. | |
2728 | inst() { | |
898720b7 HH |
2729 | case $# in |
2730 | 1) ;; | |
1b8fcd9c FS |
2731 | 2) |
2732 | [[ ! "$initdir" && -d "$2" ]] && export initdir="$2" | |
2733 | [[ "$initdir" = "$2" ]] && set "$1" | |
2734 | ;; | |
2735 | 3) | |
2736 | [[ -z "$initdir" ]] && export initdir="$2" | |
2737 | set "$1" "$3" | |
2738 | ;; | |
2739 | *) | |
2740 | dfatal "inst only takes 1 or 2 or 3 arguments" | |
2741 | exit 1 | |
2742 | ;; | |
898720b7 | 2743 | esac |
1b8fcd9c FS |
2744 | |
2745 | local fun | |
2746 | for fun in inst_symlink inst_script inst_binary inst_simple; do | |
2747 | "$fun" "$@" && return 0 | |
898720b7 | 2748 | done |
7074c047 FS |
2749 | |
2750 | dwarn "Failed to install '$1'" | |
898720b7 HH |
2751 | return 1 |
2752 | } | |
2753 | ||
2754 | # install any of listed files | |
2755 | # | |
2756 | # If first argument is '-d' and second some destination path, first accessible | |
2757 | # source is installed into this path, otherwise it will installed in the same | |
2758 | # path as source. If none of listed files was installed, function return 1. | |
2759 | # On first successful installation it returns with 0 status. | |
2760 | # | |
2761 | # Example: | |
2762 | # | |
2763 | # inst_any -d /bin/foo /bin/bar /bin/baz | |
2764 | # | |
2765 | # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in | |
32e27670 | 2766 | # initrd. |
898720b7 | 2767 | inst_any() { |
1b8fcd9c | 2768 | local dest file |
898720b7 | 2769 | |
1b8fcd9c | 2770 | [[ "${1:?}" = '-d' ]] && dest="${2:?}" && shift 2 |
898720b7 | 2771 | |
1b8fcd9c FS |
2772 | for file in "$@"; do |
2773 | if [[ -e "$file" ]]; then | |
2774 | [[ -n "$dest" ]] && inst "$file" "$dest" && return 0 | |
2775 | inst "$file" && return 0 | |
898720b7 HH |
2776 | fi |
2777 | done | |
2778 | ||
2779 | return 1 | |
2780 | } | |
2781 | ||
da0465dc YW |
2782 | inst_recursive() { |
2783 | local p item | |
2784 | ||
2785 | for p in "$@"; do | |
eb5d7730 FS |
2786 | # Make sure the source exists, as the process substitution below |
2787 | # suppresses errors | |
2788 | stat "$p" >/dev/null || return 1 | |
2789 | ||
da0465dc YW |
2790 | while read -r item; do |
2791 | if [[ -d "$item" ]]; then | |
2792 | inst_dir "$item" | |
2793 | elif [[ -f "$item" ]]; then | |
2794 | inst_simple "$item" | |
2795 | fi | |
2796 | done < <(find "$p" 2>/dev/null) | |
2797 | done | |
2798 | } | |
2799 | ||
39e17536 FS |
2800 | # image_install [-o ] <file> [<file> ... ] |
2801 | # Install <file> to the test image | |
898720b7 | 2802 | # -o optionally install the <file> and don't fail, if it is not there |
39e17536 | 2803 | image_install() { |
1b8fcd9c FS |
2804 | local optional=no |
2805 | local prog="${1:?}" | |
2806 | ||
2807 | if [[ "$prog" = '-o' ]]; then | |
2808 | optional=yes | |
898720b7 HH |
2809 | shift |
2810 | fi | |
1b8fcd9c FS |
2811 | |
2812 | for prog in "$@"; do | |
2813 | if ! inst "$prog" ; then | |
23f8e019 | 2814 | if get_bool "$optional"; then |
1b8fcd9c | 2815 | dinfo "Skipping program $prog as it cannot be found and is" \ |
898720b7 HH |
2816 | "flagged to be optional" |
2817 | else | |
1b8fcd9c | 2818 | dfatal "Failed to install $prog" |
898720b7 HH |
2819 | exit 1 |
2820 | fi | |
2821 | fi | |
898720b7 HH |
2822 | done |
2823 | } | |
2824 | ||
0d6e798a HH |
2825 | # Install a single kernel module along with any firmware it may require. |
2826 | # $1 = full path to kernel module to install | |
2827 | install_kmod_with_fw() { | |
94009c27 | 2828 | local module="${1:?}" |
0d6e798a | 2829 | # no need to go further if the module is already installed |
c049acb2 | 2830 | [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*"/lib/modules/$KERNEL_VER/"}" ]] && return 0 |
94009c27 | 2831 | [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0 |
0d6e798a | 2832 | |
94009c27 | 2833 | [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}" |
0d6e798a | 2834 | |
c049acb2 | 2835 | inst_simple "$module" "/lib/modules/$KERNEL_VER/${module##*"/lib/modules/$KERNEL_VER/"}" || return $? |
0d6e798a | 2836 | |
94009c27 FS |
2837 | local modname="${module##*/}" |
2838 | local fwdir found fw | |
2839 | modname="${modname%.ko*}" | |
0d6e798a | 2840 | |
94009c27 FS |
2841 | while read -r fw; do |
2842 | found= | |
2843 | for fwdir in /lib/firmware/updates /lib/firmware; do | |
2844 | if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then | |
2845 | inst_simple "$fwdir/$fw" "/lib/firmware/$fw" | |
2846 | found=yes | |
0d6e798a HH |
2847 | fi |
2848 | done | |
23f8e019 | 2849 | if ! get_bool "$found"; then |
94009c27 FS |
2850 | if ! grep -qe "\<${modname//-/_}\>" /proc/modules; then |
2851 | dinfo "Possible missing firmware \"${fw}\" for kernel module" \ | |
2852 | "\"${modname}.ko\"" | |
0d6e798a | 2853 | else |
94009c27 FS |
2854 | dwarn "Possible missing firmware \"${fw}\" for kernel module" \ |
2855 | "\"${modname}.ko\"" | |
0d6e798a HH |
2856 | fi |
2857 | fi | |
94009c27 | 2858 | done < <(modinfo -k "$KERNEL_VER" -F firmware "$module" 2>/dev/null) |
0d6e798a HH |
2859 | return 0 |
2860 | } | |
2861 | ||
2862 | # Do something with all the dependencies of a kernel module. | |
2863 | # Note that kernel modules depend on themselves using the technique we use | |
2864 | # $1 = function to call for each dependency we find | |
2865 | # It will be passed the full path to the found kernel module | |
2866 | # $2 = module to get dependencies for | |
2867 | # rest of args = arguments to modprobe | |
0d6e798a | 2868 | for_each_kmod_dep() { |
94009c27 FS |
2869 | local func="${1:?}" |
2870 | local kmod="${2:?}" | |
2871 | local found=0 | |
2872 | local cmd modpath | |
0d6e798a | 2873 | shift 2 |
0d6e798a | 2874 | |
94009c27 FS |
2875 | while read -r cmd modpath _; do |
2876 | [[ "$cmd" = insmod ]] || continue | |
2877 | "$func" "$modpath" || return $? | |
2878 | found=1 | |
2879 | done < <(modprobe "$@" --ignore-install --show-depends "$kmod") | |
0d6e798a | 2880 | |
23f8e019 | 2881 | ! get_bool "$found" && return 1 |
94009c27 | 2882 | return 0 |
0d6e798a HH |
2883 | } |
2884 | ||
2885 | # instmods [-c] <kernel module> [<kernel module> ... ] | |
2886 | # instmods [-c] <kernel subsystem> | |
2887 | # install kernel modules along with all their dependencies. | |
2888 | # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage" | |
94009c27 | 2889 | # FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5 |
0d6e798a | 2890 | instmods() { |
94009c27 FS |
2891 | local check=no |
2892 | if [[ $# -ge 0 && "$1" = '-c' ]]; then | |
2893 | check=yes | |
0d6e798a HH |
2894 | shift |
2895 | fi | |
2896 | ||
94009c27 FS |
2897 | inst1mod() { |
2898 | local mod="${1:?}" | |
2899 | local ret=0 | |
2900 | local mod_dir="/lib/modules/${KERNEL_VER:?}/" | |
2901 | ||
2902 | case "$mod" in | |
0d6e798a | 2903 | =*) |
94009c27 FS |
2904 | if [ -f "${mod_dir}/modules.${mod#=}" ]; then |
2905 | ( | |
2906 | [[ "$mpargs" ]] && echo "$mpargs" | |
2907 | cat "${mod_dir}/modules.${mod#=}" | |
2908 | ) | instmods | |
0d6e798a | 2909 | else |
94009c27 FS |
2910 | ( |
2911 | [[ "$mpargs" ]] && echo "$mpargs" | |
84817bfd | 2912 | find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f -printf '%f\n' |
94009c27 | 2913 | ) | instmods |
0d6e798a HH |
2914 | fi |
2915 | ;; | |
94009c27 FS |
2916 | --*) |
2917 | mpargs+=" $mod" | |
2918 | ;; | |
2919 | i2o_scsi) | |
2920 | # Do not load this diagnostic-only module | |
2921 | return | |
2922 | ;; | |
0d6e798a | 2923 | *) |
94009c27 | 2924 | mod=${mod##*/} |
0d6e798a HH |
2925 | # if we are already installed, skip this module and go on |
2926 | # to the next one. | |
94009c27 | 2927 | [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return |
0d6e798a HH |
2928 | |
2929 | # We use '-d' option in modprobe only if modules prefix path | |
2930 | # differs from default '/'. This allows us to use Dracut with | |
2931 | # old version of modprobe which doesn't have '-d' option. | |
94009c27 FS |
2932 | local mod_dirname=${mod_dir%%/lib/modules/*} |
2933 | [[ -n ${mod_dirname} ]] && mod_dirname="-d ${mod_dirname}/" | |
0d6e798a HH |
2934 | |
2935 | # ok, load the module, all its dependencies, and any firmware | |
2936 | # it may require | |
94009c27 FS |
2937 | for_each_kmod_dep install_kmod_with_fw "$mod" \ |
2938 | --set-version "$KERNEL_VER" \ | |
2939 | ${mod_dirname:+"$mod_dirname"} \ | |
2940 | ${mpargs:+"$mpargs"} | |
2941 | ((ret+=$?)) | |
0d6e798a HH |
2942 | ;; |
2943 | esac | |
7bf20e48 | 2944 | return "$ret" |
0d6e798a HH |
2945 | } |
2946 | ||
94009c27 FS |
2947 | local mod mpargs |
2948 | ||
2949 | if [[ $# -eq 0 ]]; then # filenames from stdin | |
2950 | while read -r mod; do | |
2951 | if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then | |
2952 | dfatal "Failed to install $mod" | |
2953 | return 1 | |
2954 | fi | |
0d6e798a | 2955 | done |
94009c27 | 2956 | fi |
0d6e798a | 2957 | |
94009c27 FS |
2958 | for mod in "$@"; do # filenames as arguments |
2959 | if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then | |
2960 | dfatal "Failed to install $mod" | |
2961 | return 1 | |
2962 | fi | |
2963 | done | |
2964 | ||
2965 | return 0 | |
0d6e798a | 2966 | } |
898720b7 | 2967 | |
9e19a8b0 | 2968 | _umount_dir() { |
1b8fcd9c FS |
2969 | local mountpoint="${1:?}" |
2970 | if mountpoint -q "$mountpoint"; then | |
2971 | ddebug "umount $mountpoint" | |
2972 | umount "$mountpoint" | |
9e19a8b0 DS |
2973 | fi |
2974 | } | |
2975 | ||
ec4cab49 DS |
2976 | # can be overridden in specific test |
2977 | test_setup_cleanup() { | |
ec43f686 | 2978 | cleanup_initdir |
ec4cab49 DS |
2979 | } |
2980 | ||
2981 | _test_cleanup() { | |
f85bc044 DS |
2982 | # (post-test) cleanup should always ignore failure and cleanup as much as possible |
2983 | ( | |
2984 | set +e | |
1b8fcd9c FS |
2985 | [[ -n "$initdir" ]] && _umount_dir "$initdir" |
2986 | [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC" | |
21be71ee | 2987 | # If multiple setups/cleans are ran in parallel, this can cause a race |
1c3f490f | 2988 | if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then |
21be71ee LB |
2989 | rm -vf "${IMAGESTATEDIR}/default.img" |
2990 | fi | |
1b8fcd9c FS |
2991 | [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR" |
2992 | [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE" | |
65dd488f | 2993 | ) || : |
ec4cab49 DS |
2994 | } |
2995 | ||
054ee249 MP |
2996 | # can be overridden in specific test |
2997 | test_cleanup() { | |
ec4cab49 | 2998 | _test_cleanup |
054ee249 MP |
2999 | } |
3000 | ||
693ad298 ZJS |
3001 | test_cleanup_again() { |
3002 | [ -n "$TESTDIR" ] || return | |
3003 | rm -rf "$TESTDIR/unprivileged-nspawn-root" | |
1b8fcd9c | 3004 | [[ -n "$initdir" ]] && _umount_dir "$initdir" |
693ad298 ZJS |
3005 | } |
3006 | ||
8c3534b5 | 3007 | test_create_image() { |
70ce817c ZJS |
3008 | create_empty_image_rootdir |
3009 | ||
3010 | # Create what will eventually be our root filesystem onto an overlay | |
3011 | ( | |
3012 | LOG_LEVEL=5 | |
70ce817c | 3013 | setup_basic_environment |
70ce817c | 3014 | ) |
8c3534b5 ZJS |
3015 | } |
3016 | ||
3017 | test_setup() { | |
23f8e019 | 3018 | if get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \ |
1b8fcd9c FS |
3019 | command -v meson >/dev/null && \ |
3020 | [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then | |
20f938ff | 3021 | dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true" |
8c3534b5 ZJS |
3022 | exit 1 |
3023 | fi | |
3024 | ||
1b8fcd9c FS |
3025 | if [ -e "${IMAGE_PRIVATE:?}" ]; then |
3026 | echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")" | |
8c3534b5 | 3027 | mount_initdir |
2991fa41 | 3028 | else |
1b8fcd9c | 3029 | if [ ! -e "${IMAGE_PUBLIC:?}" ]; then |
d9e606e8 | 3030 | # default.img is the base that every test uses and optionally appends to |
1b8fcd9c | 3031 | if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] || [ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then |
d9e606e8 LB |
3032 | # Create the backing public image, but then completely unmount |
3033 | # it and drop the loopback device responsible for it, since we're | |
3034 | # going to symlink/copy the image and mount it again from | |
3035 | # elsewhere. | |
1b8fcd9c | 3036 | local image_old="${IMAGE_PUBLIC}" |
d9e606e8 LB |
3037 | if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then |
3038 | IMAGE_PUBLIC="${IMAGESTATEDIR}/default.img" | |
3039 | fi | |
3040 | test_create_image | |
3041 | test_setup_cleanup | |
3042 | umount_loopback | |
3043 | cleanup_loopdev | |
3044 | IMAGE_PUBLIC="${image_old}" | |
3045 | fi | |
23f8e019 | 3046 | if [ "${IMAGE_NAME:?}" != "default" ] && ! get_bool "${TEST_FORCE_NEWIMAGE}"; then |
d9e606e8 LB |
3047 | cp -v "$(realpath "${IMAGESTATEDIR}/default.img")" "$IMAGE_PUBLIC" |
3048 | fi | |
3049 | fi | |
3050 | ||
23f8e019 FS |
3051 | local hook_defined |
3052 | declare -f -F test_append_files >/dev/null && hook_defined=yes || hook_defined=no | |
2991fa41 | 3053 | |
1b8fcd9c | 3054 | echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")" |
23f8e019 | 3055 | if get_bool "$TEST_PARALLELIZE" || get_bool "$hook_defined"; then |
1b8fcd9c | 3056 | cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE" |
7a57256c | 3057 | else |
1b8fcd9c | 3058 | ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE" |
7a57256c | 3059 | fi |
2991fa41 | 3060 | |
8c3534b5 | 3061 | mount_initdir |
508a7f04 FS |
3062 | |
3063 | if get_bool "${TEST_SUPPORTING_SERVICES_SHOULD_BE_MASKED}"; then | |
eb70d945 FS |
3064 | dinfo "Masking supporting services" |
3065 | mask_supporting_services | |
3066 | fi | |
3067 | ||
ba7abf79 FS |
3068 | # Send stdout/stderr of testsuite-*.service units to both journal and |
3069 | # console to make debugging in CIs easier | |
3070 | # Note: we can't use a dropin for `testsuite-.service`, since that also | |
3071 | # overrides 'sub-units' of some tests that already use a specific | |
3072 | # value for Standard(Output|Error)= | |
3073 | # (e.g. test/units/testsuite-66-deviceisolation.service) | |
3074 | if ! get_bool "$INTERACTIVE_DEBUG"; then | |
3075 | local dropin_dir="${initdir:?}/etc/systemd/system/testsuite-${TESTID:?}.service.d" | |
3076 | mkdir -p "$dropin_dir" | |
3077 | printf '[Service]\nStandardOutput=journal+console\nStandardError=journal+console' >"$dropin_dir/99-stdout.conf" | |
3078 | fi | |
3079 | ||
23f8e019 | 3080 | if get_bool "$hook_defined"; then |
1b8fcd9c | 3081 | test_append_files "${initdir:?}" |
d9e606e8 | 3082 | fi |
8c3534b5 ZJS |
3083 | fi |
3084 | ||
70ce817c ZJS |
3085 | setup_nspawn_root |
3086 | } | |
3087 | ||
054ee249 | 3088 | test_run() { |
1b8fcd9c | 3089 | local test_id="${1:?}" |
4962ed9f | 3090 | mount_initdir |
4962ed9f | 3091 | |
23f8e019 | 3092 | if ! get_bool "${TEST_NO_QEMU:=}"; then |
1b8fcd9c | 3093 | if run_qemu "$test_id"; then |
3e8caa34 | 3094 | check_result_qemu || { echo "qemu test failed"; return 1; } |
054ee249 | 3095 | else |
3e8caa34 | 3096 | dwarn "can't run qemu, skipping" |
054ee249 MP |
3097 | fi |
3098 | fi | |
23f8e019 | 3099 | if ! get_bool "${TEST_NO_NSPAWN:=}"; then |
ec43f686 | 3100 | mount_initdir |
1b8fcd9c | 3101 | if run_nspawn "${initdir:?}" "$test_id"; then |
ec43f686 | 3102 | check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; } |
054ee249 MP |
3103 | else |
3104 | dwarn "can't run systemd-nspawn, skipping" | |
3105 | fi | |
746fbd9c | 3106 | |
23f8e019 | 3107 | if get_bool "${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then |
ec43f686 | 3108 | dir="$TESTDIR/unprivileged-nspawn-root" |
1b8fcd9c | 3109 | if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_id"; then |
ec43f686 | 3110 | check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; } |
746fbd9c EV |
3111 | else |
3112 | dwarn "can't run systemd-nspawn, skipping" | |
3113 | fi | |
d56db495 | 3114 | fi |
054ee249 MP |
3115 | fi |
3116 | return 0 | |
3117 | } | |
3118 | ||
898720b7 | 3119 | do_test() { |
33a5e20f HH |
3120 | if [[ $UID != "0" ]]; then |
3121 | echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2 | |
3122 | exit 0 | |
3123 | fi | |
3124 | ||
23f8e019 | 3125 | if get_bool "${TEST_NO_QEMU:=}" && get_bool "${TEST_NO_NSPAWN:=}"; then |
3e8caa34 | 3126 | echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both qemu and nspawn disabled" >&2 |
aeac20fc LB |
3127 | exit 0 |
3128 | fi | |
3129 | ||
23f8e019 | 3130 | if get_bool "${TEST_QEMU_ONLY:=}" && ! get_bool "$TEST_NO_NSPAWN"; then |
3e8caa34 | 3131 | echo "TEST: $TEST_DESCRIPTION [SKIPPED]: qemu-only tests requested" >&2 |
51d56d3b LB |
3132 | exit 0 |
3133 | fi | |
3134 | ||
23f8e019 | 3135 | if get_bool "${TEST_PREFER_NSPAWN:=}" && ! get_bool "$TEST_NO_NSPAWN"; then |
eb3785f3 LB |
3136 | TEST_NO_QEMU=1 |
3137 | fi | |
3138 | ||
cc5549ca | 3139 | # Detect lib paths |
1b8fcd9c | 3140 | [[ "$libdir" ]] || for libdir in /lib64 /lib; do |
898720b7 HH |
3141 | [[ -d $libdir ]] && libdirs+=" $libdir" && break |
3142 | done | |
3143 | ||
1b8fcd9c | 3144 | [[ "$usrlibdir" ]] || for usrlibdir in /usr/lib64 /usr/lib; do |
898720b7 HH |
3145 | [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break |
3146 | done | |
3147 | ||
22077c9c MP |
3148 | mkdir -p "$STATEDIR" |
3149 | ||
898720b7 | 3150 | import_testdir |
889a9042 | 3151 | import_initdir |
898720b7 | 3152 | |
954c77c2 ZJS |
3153 | if [ -n "${SUDO_USER}" ]; then |
3154 | ddebug "Making ${TESTDIR:?} readable for ${SUDO_USER} (acquired from sudo)" | |
3155 | setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}" | |
954c77c2 ZJS |
3156 | fi |
3157 | ||
1b8fcd9c | 3158 | testname="$(basename "$PWD")" |
8af10ca3 | 3159 | |
898720b7 HH |
3160 | while (($# > 0)); do |
3161 | case $1 in | |
3162 | --run) | |
8af10ca3 | 3163 | echo "${testname} RUN: $TEST_DESCRIPTION" |
c4cd6205 | 3164 | test_run "$TESTID" |
0013fac2 | 3165 | ret=$? |
1b8fcd9c | 3166 | if [ $ret -eq 0 ]; then |
8af10ca3 | 3167 | echo "${testname} RUN: $TEST_DESCRIPTION [OK]" |
898720b7 | 3168 | else |
8af10ca3 | 3169 | echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]" |
898720b7 | 3170 | fi |
23f8e019 FS |
3171 | exit $ret |
3172 | ;; | |
898720b7 | 3173 | --setup) |
8af10ca3 | 3174 | echo "${testname} SETUP: $TEST_DESCRIPTION" |
898720b7 | 3175 | test_setup |
ec4cab49 | 3176 | test_setup_cleanup |
818567fc | 3177 | ;; |
693ad298 | 3178 | --clean) |
8af10ca3 | 3179 | echo "${testname} CLEANUP: $TEST_DESCRIPTION" |
898720b7 | 3180 | test_cleanup |
818567fc | 3181 | ;; |
693ad298 | 3182 | --clean-again) |
8af10ca3 | 3183 | echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION" |
693ad298 ZJS |
3184 | test_cleanup_again |
3185 | ;; | |
898720b7 | 3186 | --all) |
818567fc | 3187 | ret=0 |
8af10ca3 | 3188 | echo -n "${testname}: $TEST_DESCRIPTION " |
0761da38 LB |
3189 | # Do not use a subshell, otherwise cleanup variables (LOOPDEV) will be lost |
3190 | # and loop devices will leak | |
3191 | test_setup </dev/null >"$TESTLOG" 2>&1 || ret=$? | |
3192 | if [ $ret -eq 0 ]; then | |
3193 | test_setup_cleanup </dev/null >>"$TESTLOG" 2>&1 || ret=$? | |
3194 | fi | |
3195 | if [ $ret -eq 0 ]; then | |
c4cd6205 | 3196 | test_run "$TESTID" </dev/null >>"$TESTLOG" 2>&1 || ret=$? |
0761da38 | 3197 | fi |
f85bc044 | 3198 | test_cleanup |
898720b7 | 3199 | if [ $ret -eq 0 ]; then |
22077c9c | 3200 | rm "$TESTLOG" |
898720b7 HH |
3201 | echo "[OK]" |
3202 | else | |
3203 | echo "[FAILED]" | |
22077c9c | 3204 | echo "see $TESTLOG" |
898720b7 | 3205 | fi |
23f8e019 FS |
3206 | exit $ret |
3207 | ;; | |
3208 | *) | |
3209 | break | |
3210 | ;; | |
898720b7 HH |
3211 | esac |
3212 | shift | |
3213 | done | |
3214 | } |