]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-functions
man: fix reference to sd_bus_add_fallback_vtable
[thirdparty/systemd.git] / test / test-functions
1 #!/bin/bash
2 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
3 # ex: ts=8 sw=4 sts=4 et filetype=sh
4 PATH=/sbin:/bin:/usr/sbin:/usr/bin
5 export PATH
6
7 LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && echo yes || true)
8 LOOKS_LIKE_ARCH=$(source /etc/os-release && [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && echo yes || true)
9 LOOKS_LIKE_SUSE=$(source /etc/os-release && [[ " $ID_LIKE " = *" suse "* ]] && echo yes || true)
10 KERNEL_VER=${KERNEL_VER-$(uname -r)}
11 KERNEL_MODS="/lib/modules/$KERNEL_VER/"
12 QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}"
13 NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}"
14 TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
15 [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
16 UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
17 EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
18 QEMU_MEM="${QEMU_MEM:-512M}"
19
20 if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
21 echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
22 ROOTLIBDIR=/usr/lib/systemd
23 fi
24
25 PATH_TO_INIT=$ROOTLIBDIR/systemd
26
27 BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
28 DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv"
29
30 STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
31 STATEFILE="$STATEDIR/.testdir"
32 TESTLOG="$STATEDIR/test.log"
33
34 is_built_with_asan() {
35 if ! type -P objdump >/dev/null; then
36 ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN."
37 return 1
38 fi
39
40 # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
41 local _asan_calls=$(objdump -dC $BUILD_DIR/systemd-journald | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
42 if (( $_asan_calls < 1000 )); then
43 return 1
44 else
45 return 0
46 fi
47 }
48
49 IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no)
50
51 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
52 STRIP_BINARIES=no
53 SKIP_INITRD="${SKIP_INITRD:-yes}"
54 PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
55 QEMU_MEM="1536M"
56 QEMU_SMP=4
57
58 # We need to correctly distinguish between gcc's and clang's ASan DSOs.
59 if ldd $BUILD_DIR/systemd | grep -q libasan.so; then
60 ASAN_COMPILER=gcc
61 elif ldd $BUILD_DIR/systemd | grep -q libclang_rt.asan; then
62 ASAN_COMPILER=clang
63
64 # As clang's ASan DSO is usually in a non-standard path, let's check if
65 # the environment is set accordingly. If not, warn the user and exit.
66 # We're not setting the LD_LIBRARY_PATH automagically here, because
67 # user should encounter (and fix) the same issue when running the unit
68 # tests (meson test)
69 if ldd "$BUILD_DIR/systemd" | grep -q "libclang_rt.asan.*not found"; then
70 _asan_rt_name="$(ldd $BUILD_DIR/systemd | awk '/libclang_rt.asan/ {print $1; exit}')"
71 _asan_rt_path="$(find /usr/lib* /usr/local/lib* -type f -name "$_asan_rt_name" 2>/dev/null | sed 1q)"
72 echo >&2 "clang's ASan DSO ($_asan_rt_name) is not present in the runtime library path"
73 echo >&2 "Consider setting LD_LIBRARY_PATH=${_asan_rt_path%/*}"
74 exit 1
75 fi
76 else
77 echo >&2 "systemd is not linked against the ASan DSO"
78 echo >&2 "gcc does this by default, for clang compile with -shared-libasan"
79 exit 1
80 fi
81 fi
82
83 function find_qemu_bin() {
84 # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
85 # Either way, only use this version if we aren't running in KVM, because
86 # nested KVM is flaky still.
87 if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
88 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
89 fi
90
91 [ "$ARCH" ] || ARCH=$(uname -m)
92 case $ARCH in
93 x86_64)
94 # QEMU's own build system calls it qemu-system-x86_64
95 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-x86_64 2>/dev/null | grep '^/' -m1)
96 ;;
97 i*86)
98 # new i386 version of QEMU
99 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-i386 2>/dev/null | grep '^/' -m1)
100
101 # i386 version of QEMU
102 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu 2>/dev/null | grep '^/' -m1)
103 ;;
104 ppc64*)
105 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-$ARCH 2>/dev/null | grep '^/' -m1)
106 ;;
107 esac
108
109 if [ ! -e "$QEMU_BIN" ]; then
110 echo "Could not find a suitable QEMU binary" >&2
111 return 1
112 fi
113 }
114
115 # Return 0 if QEMU did run (then you must check the result state/logs for actual
116 # success), or 1 if QEMU is not available.
117 run_qemu() {
118 if [ -f /etc/machine-id ]; then
119 read MACHINE_ID < /etc/machine-id
120 [ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
121 && INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
122 [ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
123 && KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
124 fi
125
126 CONSOLE=ttyS0
127
128 if [[ ! "$KERNEL_BIN" ]]; then
129 if [[ "$LOOKS_LIKE_ARCH" ]]; then
130 KERNEL_BIN=/boot/vmlinuz-linux
131 else
132 [ "$ARCH" ] || ARCH=$(uname -m)
133 case $ARCH in
134 ppc64*)
135 KERNEL_BIN=/boot/vmlinux-$KERNEL_VER
136 CONSOLE=hvc0
137 ;;
138 *)
139 KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
140 ;;
141 esac
142 fi
143 fi
144
145 default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
146 default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
147 default_arch_initrd=/boot/initramfs-linux-fallback.img
148 default_suse_initrd=/boot/initrd-${KERNEL_VER}
149 if [[ ! "$INITRD" ]]; then
150 if [[ -e "$default_fedora_initrd" ]]; then
151 INITRD="$default_fedora_initrd"
152 elif [[ "$LOOKS_LIKE_DEBIAN" && -e "$default_debian_initrd" ]]; then
153 INITRD="$default_debian_initrd"
154 elif [[ "$LOOKS_LIKE_ARCH" && -e "$default_arch_initrd" ]]; then
155 INITRD="$default_arch_initrd"
156 elif [[ "$LOOKS_LIKE_SUSE" && -e "$default_suse_initrd" ]]; then
157 INITRD="$default_suse_initrd"
158 fi
159 fi
160
161 # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically'
162 # i.e. use the number of online CPUs on the host machine. If the nproc utility
163 # is not installed or there's some other error when calling it, fall back
164 # to the original value (QEMU_SMP=1).
165 if ! [ "$QEMU_SMP" ]; then
166 if ! QEMU_SMP=$(nproc); then
167 dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
168 QEMU_SMP=1
169 fi
170 fi
171
172 find_qemu_bin || return 1
173
174 local _cgroup_args
175 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
176 _cgroup_args="systemd.unified_cgroup_hierarchy=yes"
177 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
178 _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=yes"
179 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
180 _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=no"
181 elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
182 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
183 exit 1
184 fi
185
186 if [[ "$LOOKS_LIKE_SUSE" ]]; then
187 PARAMS+="rd.hostonly=0"
188 elif [[ "$LOOKS_LIKE_ARCH" ]]; then
189 PARAMS+="rw"
190 else
191 PARAMS+="ro"
192 fi
193
194 KERNEL_APPEND="$PARAMS \
195 root=/dev/sda1 \
196 raid=noautodetect \
197 loglevel=2 \
198 init=$PATH_TO_INIT \
199 console=$CONSOLE \
200 selinux=0 \
201 printk.devkmsg=on \
202 $_cgroup_args \
203 $KERNEL_APPEND \
204 "
205
206 QEMU_OPTIONS="-smp $QEMU_SMP \
207 -net none \
208 -m $QEMU_MEM \
209 -nographic \
210 -kernel $KERNEL_BIN \
211 -drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
212 "
213
214 if [[ "$INITRD" && "$SKIP_INITRD" != "yes" ]]; then
215 QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
216 fi
217
218 # Let's use KVM if it is available, but let's avoid using nested KVM as that is still flaky
219 if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
220 QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
221 fi
222
223 if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
224 QEMU_BIN="timeout --foreground $QEMU_TIMEOUT $QEMU_BIN"
225 fi
226 (set -x; $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND")
227 rc=$?
228 if [ "$rc" = 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
229 derror "test timed out after $QEMU_TIMEOUT s"
230 TIMED_OUT=1
231 else
232 [ "$rc" != 0 ] && derror "QEMU failed with exit code $rc"
233 fi
234 return 0
235 }
236
237 # Return 0 if nspawn did run (then you must check the result state/logs for actual
238 # success), or 1 if nspawn is not available.
239 run_nspawn() {
240 [[ -d /run/systemd/system ]] || return 1
241
242 local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
243 if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
244 _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
245 fi
246
247 if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
248 dwarn "nspawn doesn't support UNIFIED_CGROUP_HIERARCHY=hybrid, skipping"
249 exit
250 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
251 _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
252 elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
253 _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
254 else
255 dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
256 exit 1
257 fi
258
259 (set -x; $_nspawn_cmd)
260 rc=$?
261 if [ "$rc" = 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
262 derror "test timed out after $NSPAWN_TIMEOUT s"
263 TIMED_OUT=1
264 else
265 [ "$rc" != 0 ] && derror "nspawn failed with exit code $rc"
266 fi
267 return 0
268 }
269
270 setup_basic_environment() {
271 # create the basic filesystem layout
272 setup_basic_dirs
273
274 install_systemd
275 install_missing_libraries
276 install_config_files
277 create_rc_local
278 install_basic_tools
279 install_libnss
280 install_pam
281 install_dbus
282 install_fonts
283 install_keymaps
284 install_terminfo
285 install_execs
286 install_fsck
287 install_plymouth
288 install_debug_tools
289 install_ld_so_conf
290 setup_selinux
291 strip_binaries
292 install_depmod_files
293 generate_module_dependencies
294 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
295 create_asan_wrapper
296 fi
297 }
298
299 setup_selinux() {
300 # don't forget KERNEL_APPEND='... selinux=1 ...'
301 if [[ "$SETUP_SELINUX" != "yes" ]]; then
302 ddebug "Don't setup SELinux"
303 return 0
304 fi
305 ddebug "Setup SELinux"
306 local _conf_dir=/etc/selinux
307 local _fixfiles_tools="bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles"
308
309 rm -rf $initdir/$_conf_dir
310 if ! cp -ar $_conf_dir $initdir/$_conf_dir; then
311 dfatal "Failed to copy $_conf_dir"
312 exit 1
313 fi
314
315 cat <<EOF >$initdir/etc/systemd/system/autorelabel.service
316 [Unit]
317 Description=Relabel all filesystems
318 DefaultDependencies=no
319 Requires=local-fs.target
320 Conflicts=shutdown.target
321 After=local-fs.target
322 Before=sysinit.target shutdown.target
323 ConditionSecurity=selinux
324 ConditionPathExists=|/.autorelabel
325
326 [Service]
327 ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot'
328 Type=oneshot
329 TimeoutSec=0
330 RemainAfterExit=yes
331 EOF
332
333 touch $initdir/.autorelabel
334 mkdir -p $initdir/etc/systemd/system/basic.target.wants
335 ln -fs autorelabel.service $initdir/etc/systemd/system/basic.target.wants/autorelabel.service
336
337 dracut_install $_fixfiles_tools
338 dracut_install fixfiles
339 dracut_install sestatus
340 }
341
342 install_valgrind() {
343 if ! type -p valgrind; then
344 dfatal "Failed to install valgrind"
345 exit 1
346 fi
347
348 local _valgrind_bins=$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
349 dracut_install $_valgrind_bins
350
351 local _valgrind_libs=$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
352 dracut_install $_valgrind_libs
353
354 local _valgrind_dbg_and_supp=$(
355 strace -e open valgrind /bin/true 2>&1 >/dev/null |
356 perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
357 )
358 dracut_install $_valgrind_dbg_and_supp
359 }
360
361 create_valgrind_wrapper() {
362 local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
363 ddebug "Create $_valgrind_wrapper"
364 cat >$_valgrind_wrapper <<EOF
365 #!/bin/bash
366
367 mount -t proc proc /proc
368 exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
369 EOF
370 chmod 0755 $_valgrind_wrapper
371 }
372
373 create_asan_wrapper() {
374 local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
375 local _asan_rt_pattern
376 ddebug "Create $_asan_wrapper"
377
378 case "$ASAN_COMPILER" in
379 gcc)
380 _asan_rt_pattern="*libasan*"
381 ;;
382 clang)
383 _asan_rt_pattern="libclang_rt.asan-*"
384 # Install llvm-symbolizer to generate useful reports
385 # See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
386 dracut_install "llvm-symbolizer"
387 ;;
388 *)
389 dfail "Unsupported compiler: $ASAN_COMPILER"
390 exit 1
391 esac
392
393 cat >$_asan_wrapper <<EOF
394 #!/bin/bash
395
396 set -x
397
398 DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
399 DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
400 DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
401
402 # As right now bash is the PID 1, we can't expect PATH to have a sane value.
403 # Let's make one to prevent unexpected "<bin> not found" issues in the future
404 export PATH="/sbin:/bin:/usr/sbin:/usr/bin"
405
406 mount -t proc proc /proc
407 mount -t sysfs sysfs /sys
408 mount -o remount,rw /
409
410 PATH_TO_ASAN=\$(find / -name '$_asan_rt_pattern' | sed 1q)
411 if [[ "\$PATH_TO_ASAN" ]]; then
412 # A lot of services (most notably dbus) won't start without preloading libasan
413 # See https://github.com/systemd/systemd/issues/5004
414 DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
415 # Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
416 # unnecessary for gcc & libasan, however, for clang this is crucial, as its
417 # runtime ASan DSO is in a non-standard (library) path.
418 echo \${PATH_TO_ASAN%/*} > /etc/ld.so.conf.d/asan-path-override.conf
419 ldconfig
420 fi
421 echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
422 echo DefaultTimeoutStartSec=180s >>/etc/systemd/system.conf
423 echo DefaultStandardOutput=journal+console >>/etc/systemd/system.conf
424
425 # ASAN and syscall filters aren't compatible with each other.
426 find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
427
428 # The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
429 # But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
430 JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
431 mkdir -p "\$JOURNALD_CONF_DIR"
432 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"
433
434 # Sometimes UBSan sends its reports to stderr regardless of what is specified in log_path
435 # Let's try to catch them by redirecting stderr (and stdout just in case) to a file
436 # See https://github.com/systemd/systemd/pull/12524#issuecomment-491108821
437 printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CONF_DIR/out.conf"
438
439 # 90s isn't enough for some services to finish when literally everything is run
440 # under ASan+UBSan in containers, which, in turn, are run in VMs.
441 # Let's limit which environments such services should be executed in.
442 mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
443 printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
444
445 # Let's override another hard-coded timeout that kicks in too early
446 mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
447 printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush.service.d/timeout.conf
448
449 # The 'mount' utility doesn't behave well under libasan, causing unexpected
450 # fails during boot and subsequent test results check:
451 # bash-5.0# mount -o remount,rw -v /
452 # mount: /dev/sda1 mounted on /.
453 # bash-5.0# echo \$?
454 # 1
455 # Let's workaround this by clearing the previously set LD_PRELOAD env variable,
456 # so the libasan library is not loaded for this particular service
457 REMOUNTFS_CONF_DIR=/etc/systemd/system/systemd-remount-fs.service.d
458 mkdir -p "\$REMOUNTFS_CONF_DIR"
459 printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$REMOUNTFS_CONF_DIR/env.conf"
460
461 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
462 exec $ROOTLIBDIR/systemd "\$@"
463 EOF
464
465 chmod 0755 $_asan_wrapper
466 }
467
468 create_strace_wrapper() {
469 local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
470 ddebug "Create $_strace_wrapper"
471 cat >$_strace_wrapper <<EOF
472 #!/bin/bash
473
474 exec strace -D -o /strace.out $ROOTLIBDIR/systemd "\$@"
475 EOF
476 chmod 0755 $_strace_wrapper
477 }
478
479 install_fsck() {
480 dracut_install /sbin/fsck*
481 dracut_install -o /bin/fsck*
482
483 # fskc.reiserfs calls reiserfsck. so, install it
484 dracut_install -o reiserfsck
485 }
486
487 install_dmevent() {
488 instmods dm_crypt =crypto
489 type -P dmeventd >/dev/null && dracut_install dmeventd
490 inst_libdir_file "libdevmapper-event.so*"
491 if [[ "$LOOKS_LIKE_DEBIAN" ]]; then
492 # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
493 # and since buster/bionic 95-dm-notify.rules
494 # see https://gitlab.com/debian-lvm/lvm2/blob/master/debian/patches/udev.patch
495 inst_rules 55-dm.rules 60-persistent-storage-dm.rules 95-dm-notify.rules
496 else
497 inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
498 fi
499 }
500
501 install_systemd() {
502 # install compiled files
503 local _ninja_bin=$(type -P ninja || type -P ninja-build)
504 if [[ -z "$_ninja_bin" ]]; then
505 dfatal "ninja was not found"
506 exit 1
507 fi
508 (set -x; DESTDIR=$initdir "$_ninja_bin" -C $BUILD_DIR install)
509 # remove unneeded documentation
510 rm -fr $initdir/usr/share/{man,doc}
511 # we strip binaries since debug symbols increase binaries size a lot
512 # and it could fill the available space
513 strip_binaries
514
515 [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
516
517 # enable debug logging in PID1
518 echo LogLevel=debug >> $initdir/etc/systemd/system.conf
519 }
520
521 get_ldpath() {
522 local _bin="$1"
523 objdump -p "$_bin" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :
524 }
525
526 install_missing_libraries() {
527 # install possible missing libraries
528 for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
529 LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
530 done
531 }
532
533 create_empty_image() {
534 local _size=500
535 if [[ "$STRIP_BINARIES" = "no" ]]; then
536 _size=$((4*_size))
537 fi
538 rm -f "$TESTDIR/rootdisk.img"
539 # Create the blank file to use as a root filesystem
540 dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek="$_size"
541 LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
542 [ -b "$LOOPDEV" ] || return 1
543 echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
544 sfdisk "$LOOPDEV" <<EOF
545 ,$((_size-50))M
546 ,
547 EOF
548
549 udevadm settle
550
551 local _label="-L systemd"
552 # mkfs.reiserfs doesn't know -L. so, use --label instead
553 [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd"
554 if ! mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; then
555 dfatal "Failed to mkfs -t ${FSTYPE}"
556 exit 1
557 fi
558 }
559
560 check_asan_reports() {
561 local ret=0
562 local root="$1"
563
564 if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
565 ls -l "$root"
566 if [[ -e "$root/systemd.asan.log.1" ]]; then
567 cat "$root/systemd.asan.log.1"
568 ret=$(($ret+1))
569 fi
570
571 journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
572 if [[ ! -z "$journald_report" ]]; then
573 printf "%s\n" "$journald_report"
574 cat "$root/systemd-journald.out" || true
575 ret=$(($ret+1))
576 fi
577
578 pids=$(
579 "$BUILD_DIR/journalctl" -D "$root/var/log/journal" | perl -alne '
580 BEGIN {
581 %services_to_ignore = (
582 "dbus-daemon" => undef,
583 );
584 }
585 print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
586 )
587 if [[ ! -z "$pids" ]]; then
588 ret=$(($ret+1))
589 for pid in $pids; do
590 "$BUILD_DIR/journalctl" -D "$root/var/log/journal" _PID=$pid --no-pager
591 done
592 fi
593 fi
594
595 return $ret
596 }
597
598 check_result_nspawn() {
599 local ret=1
600 local journald_report=""
601 local pids=""
602 [[ -e $TESTDIR/$1/testok ]] && ret=0
603 [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
604 cp -a $TESTDIR/$1/var/log/journal $TESTDIR
605 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
606 ls -l $TESTDIR/journal/*/*.journal
607 test -s $TESTDIR/failed && ret=$(($ret+1))
608 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
609 check_asan_reports "$TESTDIR/$1" || ret=$(($ret+1))
610 return $ret
611 }
612
613 # can be overridden in specific test
614 check_result_qemu() {
615 local ret=1
616 mkdir -p $TESTDIR/root
617 mount ${LOOPDEV}p1 $TESTDIR/root
618 [[ -e $TESTDIR/root/testok ]] && ret=0
619 [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
620 cp -a $TESTDIR/root/var/log/journal $TESTDIR
621 check_asan_reports "$TESTDIR/root" || ret=$(($ret+1))
622 umount $TESTDIR/root
623 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
624 ls -l $TESTDIR/journal/*/*.journal
625 test -s $TESTDIR/failed && ret=$(($ret+1))
626 [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
627 return $ret
628 }
629
630 strip_binaries() {
631 if [[ "$STRIP_BINARIES" = "no" ]]; then
632 ddebug "Don't strip binaries"
633 return 0
634 fi
635 ddebug "Strip binaries"
636 find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
637 xargs strip --strip-unneeded |& \
638 grep -v 'file format not recognized' | \
639 ddebug
640 }
641
642 create_rc_local() {
643 mkdir -p $initdir/etc/rc.d
644 cat >$initdir/etc/rc.d/rc.local <<EOF
645 #!/bin/bash
646 exit 0
647 EOF
648 chmod 0755 $initdir/etc/rc.d/rc.local
649 }
650
651 install_execs() {
652 ddebug "install any Execs from the service files"
653 (
654 export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
655 systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
656 systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
657 sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
658 | sort -u | while read i; do
659 # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
660 # also, plymouth is pulled in by rescue.service, but even there the exit code
661 # is ignored; as it's not present on some distros, don't fail if it doesn't exist
662 inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
663 done
664 )
665 }
666
667 generate_module_dependencies() {
668 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
669 ! depmod -a -b "$initdir" $KERNEL_VER; then
670 dfatal "\"depmod -a $KERNEL_VER\" failed."
671 exit 1
672 fi
673 }
674
675 install_depmod_files() {
676 inst /lib/modules/$KERNEL_VER/modules.order
677 inst /lib/modules/$KERNEL_VER/modules.builtin
678 }
679
680 install_plymouth() {
681 # install plymouth, if found... else remove plymouth service files
682 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
683 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
684 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
685 # dracut_install plymouth plymouthd
686 # else
687 rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
688 # fi
689 }
690
691 install_ld_so_conf() {
692 cp -a /etc/ld.so.conf* $initdir/etc
693 ldconfig -r "$initdir"
694 }
695
696 install_config_files() {
697 inst /etc/sysconfig/init || true
698 inst /etc/passwd
699 inst /etc/shadow
700 inst /etc/login.defs
701 inst /etc/group
702 inst /etc/shells
703 inst /etc/nsswitch.conf
704 inst /etc/pam.conf || true
705 inst /etc/securetty || true
706 inst /etc/os-release
707 inst /etc/localtime
708 # we want an empty environment
709 > $initdir/etc/environment
710 > $initdir/etc/machine-id
711 # set the hostname
712 echo systemd-testsuite > $initdir/etc/hostname
713 # fstab
714 if [[ "$LOOKS_LIKE_SUSE" ]]; then
715 ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1"
716 else
717 ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1"
718 fi
719
720 cat >$initdir/etc/fstab <<EOF
721 $ROOTMOUNT
722 EOF
723 }
724
725 install_basic_tools() {
726 [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
727 dracut_install -o sushell
728 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
729 dracut_install -o ldconfig.real
730 }
731
732 install_debug_tools() {
733 [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
734
735 if [[ $INTERACTIVE_DEBUG ]]; then
736 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
737 local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
738 mkdir -p "$_getty_override"
739 echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
740
741 cat > "$initdir/etc/motd" << EOF
742 To adjust the terminal size use:
743 export COLUMNS=xx
744 export LINES=yy
745 or
746 stty cols xx rows yy
747 EOF
748 fi
749 }
750
751 install_libnss() {
752 # install libnss_files for login
753 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
754 dracut_install $NSS_LIBS
755 }
756
757 install_dbus() {
758 inst $ROOTLIBDIR/system/dbus.socket
759
760 # Newer Fedora versions use dbus-broker by default. Let's install it is available.
761 if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
762 inst $ROOTLIBDIR/system/dbus-broker.service
763 inst_symlink /etc/systemd/system/dbus.service
764 inst /usr/bin/dbus-broker
765 inst /usr/bin/dbus-broker-launch
766 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
767 # Fedora rawhide replaced dbus.service with dbus-daemon.service
768 inst $ROOTLIBDIR/system/dbus-daemon.service
769 # Alias symlink
770 inst_symlink /etc/systemd/system/dbus.service
771 else
772 inst $ROOTLIBDIR/system/dbus.service
773 fi
774
775 find \
776 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
777 | while read file; do
778 inst $file
779 done
780 }
781
782 install_pam() {
783 (
784 if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
785 find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
786 else
787 find /lib*/security -xtype f
788 fi
789 find /etc/pam.d /etc/security -xtype f
790 ) | while read file; do
791 inst $file
792 done
793
794 # pam_unix depends on unix_chkpwd.
795 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
796 dracut_install -o unix_chkpwd
797
798 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
799 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
800
801 # set empty root password for easy debugging
802 sed -i 's/^root:x:/root::/' $initdir/etc/passwd
803 }
804
805 install_keymaps() {
806 # The first three paths may be deprecated.
807 # It seems now the last two paths are used by many distributions.
808 for i in \
809 /usr/lib/kbd/keymaps/include/* \
810 /usr/lib/kbd/keymaps/i386/include/* \
811 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
812 /usr/lib/kbd/keymaps/legacy/include/* \
813 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
814 [[ -f $i ]] || continue
815 inst $i
816 done
817
818 # When it takes any argument, then install more keymaps.
819 if [[ -n $1 ]]; then
820 for i in \
821 /usr/lib/kbd/keymaps/i386/*/* \
822 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
823 [[ -f $i ]] || continue
824 inst $i
825 done
826 fi
827 }
828
829 install_zoneinfo() {
830 for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
831 [[ -f $i ]] || continue
832 inst $i
833 done
834 }
835
836 install_fonts() {
837 for i in \
838 /usr/lib/kbd/consolefonts/eurlatgr* \
839 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
840 [[ -f $i ]] || continue
841 inst $i
842 done
843 }
844
845 install_terminfo() {
846 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
847 [ -f ${_terminfodir}/l/linux ] && break
848 done
849 dracut_install -o ${_terminfodir}/l/linux
850 }
851
852 setup_testsuite() {
853 cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
854 cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
855
856 mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
857 ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
858 # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
859 [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
860
861 # make the testsuite the default target
862 ln -fs testsuite.target $initdir/etc/systemd/system/default.target
863 }
864
865 setup_nspawn_root() {
866 rm -fr $TESTDIR/nspawn-root
867 ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
868 cp -ar $initdir $TESTDIR/nspawn-root
869 # we don't mount in the nspawn root
870 rm -f $TESTDIR/nspawn-root/etc/fstab
871 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
872 cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
873 fi
874 }
875
876 setup_basic_dirs() {
877 mkdir -p $initdir/run
878 mkdir -p $initdir/etc/systemd/system
879 mkdir -p $initdir/var/log/journal
880
881 for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log dev proc sys sysroot root run run/lock run/initramfs; do
882 if [ -L "/$d" ]; then
883 inst_symlink "/$d"
884 else
885 inst_dir "/$d"
886 fi
887 done
888
889 ln -sfn /run "$initdir/var/run"
890 ln -sfn /run/lock "$initdir/var/lock"
891 }
892
893 inst_libs() {
894 local _bin=$1
895 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
896 local _file _line
897
898 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
899 [[ $_line = 'not a dynamic executable' ]] && break
900
901 if [[ $_line =~ $_so_regex ]]; then
902 _file=${BASH_REMATCH[1]}
903 [[ -e ${initdir}/$_file ]] && continue
904 inst_library "$_file"
905 continue
906 fi
907
908 if [[ $_line =~ not\ found ]]; then
909 dfatal "Missing a shared library required by $_bin."
910 dfatal "Run \"ldd $_bin\" to find out what it is."
911 dfatal "$_line"
912 dfatal "dracut cannot create an initrd."
913 exit 1
914 fi
915 done
916 }
917
918 import_testdir() {
919 [[ -e $STATEFILE ]] && . $STATEFILE
920 if [[ ! -d "$TESTDIR" ]]; then
921 if [[ -z "$TESTDIR" ]]; then
922 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
923 else
924 mkdir -p "$TESTDIR"
925 fi
926
927 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
928 export TESTDIR
929 fi
930 }
931
932 import_initdir() {
933 initdir=$TESTDIR/root
934 export initdir
935 }
936
937 ## @brief Converts numeric logging level to the first letter of level name.
938 #
939 # @param lvl Numeric logging level in range from 1 to 6.
940 # @retval 1 if @a lvl is out of range.
941 # @retval 0 if @a lvl is correct.
942 # @result Echoes first letter of level name.
943 _lvl2char() {
944 case "$1" in
945 1) echo F;;
946 2) echo E;;
947 3) echo W;;
948 4) echo I;;
949 5) echo D;;
950 6) echo T;;
951 *) return 1;;
952 esac
953 }
954
955 ## @brief Internal helper function for _do_dlog()
956 #
957 # @param lvl Numeric logging level.
958 # @param msg Message.
959 # @retval 0 It's always returned, even if logging failed.
960 #
961 # @note This function is not supposed to be called manually. Please use
962 # dtrace(), ddebug(), or others instead which wrap this one.
963 #
964 # This function calls _do_dlog() either with parameter msg, or if
965 # none is given, it will read standard input and will use every line as
966 # a message.
967 #
968 # This enables:
969 # dwarn "This is a warning"
970 # echo "This is a warning" | dwarn
971 LOG_LEVEL=${LOG_LEVEL:-4}
972
973 dlog() {
974 [ -z "$LOG_LEVEL" ] && return 0
975 [ $1 -le $LOG_LEVEL ] || return 0
976 local lvl="$1"; shift
977 local lvlc=$(_lvl2char "$lvl") || return 0
978
979 if [ $# -ge 1 ]; then
980 echo "$lvlc: $*"
981 else
982 while read line; do
983 echo "$lvlc: " "$line"
984 done
985 fi
986 }
987
988 ## @brief Logs message at TRACE level (6)
989 #
990 # @param msg Message.
991 # @retval 0 It's always returned, even if logging failed.
992 dtrace() {
993 set +x
994 dlog 6 "$@"
995 [ -n "$debug" ] && set -x || :
996 }
997
998 ## @brief Logs message at DEBUG level (5)
999 #
1000 # @param msg Message.
1001 # @retval 0 It's always returned, even if logging failed.
1002 ddebug() {
1003 # set +x
1004 dlog 5 "$@"
1005 # [ -n "$debug" ] && set -x || :
1006 }
1007
1008 ## @brief Logs message at INFO level (4)
1009 #
1010 # @param msg Message.
1011 # @retval 0 It's always returned, even if logging failed.
1012 dinfo() {
1013 set +x
1014 dlog 4 "$@"
1015 [ -n "$debug" ] && set -x || :
1016 }
1017
1018 ## @brief Logs message at WARN level (3)
1019 #
1020 # @param msg Message.
1021 # @retval 0 It's always returned, even if logging failed.
1022 dwarn() {
1023 set +x
1024 dlog 3 "$@"
1025 [ -n "$debug" ] && set -x || :
1026 }
1027
1028 ## @brief Logs message at ERROR level (2)
1029 #
1030 # @param msg Message.
1031 # @retval 0 It's always returned, even if logging failed.
1032 derror() {
1033 # set +x
1034 dlog 2 "$@"
1035 # [ -n "$debug" ] && set -x || :
1036 }
1037
1038 ## @brief Logs message at FATAL level (1)
1039 #
1040 # @param msg Message.
1041 # @retval 0 It's always returned, even if logging failed.
1042 dfatal() {
1043 set +x
1044 dlog 1 "$@"
1045 [ -n "$debug" ] && set -x || :
1046 }
1047
1048
1049 # Generic substring function. If $2 is in $1, return 0.
1050 strstr() { [ "${1#*$2*}" != "$1" ]; }
1051
1052 # normalize_path <path>
1053 # Prints the normalized path, where it removes any duplicated
1054 # and trailing slashes.
1055 # Example:
1056 # $ normalize_path ///test/test//
1057 # /test/test
1058 normalize_path() {
1059 shopt -q -s extglob
1060 set -- "${1//+(\/)//}"
1061 shopt -q -u extglob
1062 echo "${1%/}"
1063 }
1064
1065 # convert_abs_rel <from> <to>
1066 # Prints the relative path, when creating a symlink to <to> from <from>.
1067 # Example:
1068 # $ convert_abs_rel /usr/bin/test /bin/test-2
1069 # ../../bin/test-2
1070 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
1071 convert_abs_rel() {
1072 local __current __absolute __abssize __cursize __newpath
1073 local -i __i __level
1074
1075 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
1076
1077 # corner case #1 - self looping link
1078 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
1079
1080 # corner case #2 - own dir link
1081 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
1082
1083 IFS="/" __current=($1)
1084 IFS="/" __absolute=($2)
1085
1086 __abssize=${#__absolute[@]}
1087 __cursize=${#__current[@]}
1088
1089 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
1090 do
1091 (( __level++ ))
1092 if (( __level > __abssize || __level > __cursize ))
1093 then
1094 break
1095 fi
1096 done
1097
1098 for ((__i = __level; __i < __cursize-1; __i++))
1099 do
1100 if ((__i > __level))
1101 then
1102 __newpath=$__newpath"/"
1103 fi
1104 __newpath=$__newpath".."
1105 done
1106
1107 for ((__i = __level; __i < __abssize; __i++))
1108 do
1109 if [[ -n $__newpath ]]
1110 then
1111 __newpath=$__newpath"/"
1112 fi
1113 __newpath=$__newpath${__absolute[__i]}
1114 done
1115
1116 echo "$__newpath"
1117 }
1118
1119
1120 # Install a directory, keeping symlinks as on the original system.
1121 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
1122 # will create ${initdir}/lib64, ${initdir}/lib64/file,
1123 # and a symlink ${initdir}/lib -> lib64.
1124 inst_dir() {
1125 [[ -e ${initdir}/"$1" ]] && return 0 # already there
1126
1127 local _dir="$1" _part="${1%/*}" _file
1128 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
1129 _dir="$_part $_dir"
1130 _part=${_part%/*}
1131 done
1132
1133 # iterate over parent directories
1134 for _file in $_dir; do
1135 [[ -e "${initdir}/$_file" ]] && continue
1136 if [[ -L $_file ]]; then
1137 inst_symlink "$_file"
1138 else
1139 # create directory
1140 mkdir -m 0755 -p "${initdir}/$_file" || return 1
1141 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
1142 chmod u+w "${initdir}/$_file"
1143 fi
1144 done
1145 }
1146
1147 # $1 = file to copy to ramdisk
1148 # $2 (optional) Name for the file on the ramdisk
1149 # Location of the image dir is assumed to be $initdir
1150 # We never overwrite the target if it exists.
1151 inst_simple() {
1152 [[ -f "$1" ]] || return 1
1153 strstr "$1" "/" || return 1
1154
1155 local _src=$1 target="${2:-$1}"
1156 if ! [[ -d ${initdir}/$target ]]; then
1157 [[ -e ${initdir}/$target ]] && return 0
1158 [[ -L ${initdir}/$target ]] && return 0
1159 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
1160 fi
1161 # install checksum files also
1162 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1163 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
1164 fi
1165 ddebug "Installing $_src"
1166 cp --sparse=always -pfL "$_src" "${initdir}/$target"
1167 }
1168
1169 # find symlinks linked to given library file
1170 # $1 = library file
1171 # Function searches for symlinks by stripping version numbers appended to
1172 # library filename, checks if it points to the same target and finally
1173 # prints the list of symlinks to stdout.
1174 #
1175 # Example:
1176 # rev_lib_symlinks libfoo.so.8.1
1177 # output: libfoo.so.8 libfoo.so
1178 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
1179 rev_lib_symlinks() {
1180 [[ ! $1 ]] && return 0
1181
1182 local fn="$1" orig="$(readlink -f "$1")" links=''
1183
1184 [[ ${fn} =~ .*\.so\..* ]] || return 1
1185
1186 until [[ ${fn##*.} == so ]]; do
1187 fn="${fn%.*}"
1188 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
1189 done
1190
1191 echo "${links}"
1192 }
1193
1194 # Same as above, but specialized to handle dynamic libraries.
1195 # It handles making symlinks according to how the original library
1196 # is referenced.
1197 inst_library() {
1198 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
1199 strstr "$1" "/" || return 1
1200 [[ -e $initdir/$_dest ]] && return 0
1201 if [[ -L $_src ]]; then
1202 # install checksum files also
1203 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1204 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
1205 fi
1206 _reallib=$(readlink -f "$_src")
1207 inst_simple "$_reallib" "$_reallib"
1208 inst_dir "${_dest%/*}"
1209 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
1210 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
1211 else
1212 inst_simple "$_src" "$_dest"
1213 fi
1214
1215 # Create additional symlinks. See rev_symlinks description.
1216 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
1217 [[ -e $initdir/$_symlink ]] || {
1218 ddebug "Creating extra symlink: $_symlink"
1219 inst_symlink $_symlink
1220 }
1221 done
1222 }
1223
1224 # find a binary. If we were not passed the full path directly,
1225 # search in the usual places to find the binary.
1226 find_binary() {
1227 if [[ -z ${1##/*} ]]; then
1228 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
1229 echo $1
1230 return 0
1231 fi
1232 fi
1233
1234 type -P $1
1235 }
1236
1237 # Same as above, but specialized to install binary executables.
1238 # Install binary executable, and all shared library dependencies, if any.
1239 inst_binary() {
1240 local _bin _target
1241 _bin=$(find_binary "$1") || return 1
1242 _target=${2:-$_bin}
1243 [[ -e $initdir/$_target ]] && return 0
1244 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
1245 local _file _line
1246 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1247 # I love bash!
1248 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1249 [[ $_line = 'not a dynamic executable' ]] && break
1250
1251 if [[ $_line =~ $_so_regex ]]; then
1252 _file=${BASH_REMATCH[1]}
1253 [[ -e ${initdir}/$_file ]] && continue
1254 inst_library "$_file"
1255 continue
1256 fi
1257
1258 if [[ $_line =~ not\ found ]]; then
1259 dfatal "Missing a shared library required by $_bin."
1260 dfatal "Run \"ldd $_bin\" to find out what it is."
1261 dfatal "$_line"
1262 dfatal "dracut cannot create an initrd."
1263 exit 1
1264 fi
1265 done
1266 inst_simple "$_bin" "$_target"
1267 }
1268
1269 # same as above, except for shell scripts.
1270 # If your shell script does not start with shebang, it is not a shell script.
1271 inst_script() {
1272 local _bin
1273 _bin=$(find_binary "$1") || return 1
1274 shift
1275 local _line _shebang_regex
1276 read -r -n 80 _line <"$_bin"
1277 # If debug is set, clean unprintable chars to prevent messing up the term
1278 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
1279 _shebang_regex='(#! *)(/[^ ]+).*'
1280 [[ $_line =~ $_shebang_regex ]] || return 1
1281 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
1282 }
1283
1284 # same as above, but specialized for symlinks
1285 inst_symlink() {
1286 local _src=$1 _target=${2:-$1} _realsrc
1287 strstr "$1" "/" || return 1
1288 [[ -L $1 ]] || return 1
1289 [[ -L $initdir/$_target ]] && return 0
1290 _realsrc=$(readlink -f "$_src")
1291 if ! [[ -e $initdir/$_realsrc ]]; then
1292 if [[ -d $_realsrc ]]; then
1293 inst_dir "$_realsrc"
1294 else
1295 inst "$_realsrc"
1296 fi
1297 fi
1298 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
1299 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
1300 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
1301 }
1302
1303 # attempt to install any programs specified in a udev rule
1304 inst_rule_programs() {
1305 local _prog _bin
1306
1307 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
1308 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
1309 if [ -x /lib/udev/$_prog ]; then
1310 _bin=/lib/udev/$_prog
1311 else
1312 _bin=$(find_binary "$_prog") || {
1313 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
1314 continue;
1315 }
1316 fi
1317
1318 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
1319 dracut_install "$_bin"
1320 done
1321 fi
1322 }
1323
1324 # udev rules always get installed in the same place, so
1325 # create a function to install them to make life simpler.
1326 inst_rules() {
1327 local _target=/etc/udev/rules.d _rule _found
1328
1329 inst_dir "/lib/udev/rules.d"
1330 inst_dir "$_target"
1331 for _rule in "$@"; do
1332 if [ "${rule#/}" = "$rule" ]; then
1333 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1334 if [[ -f $r/$_rule ]]; then
1335 _found="$r/$_rule"
1336 inst_simple "$_found"
1337 inst_rule_programs "$_found"
1338 fi
1339 done
1340 fi
1341 for r in '' ./ $dracutbasedir/rules.d/; do
1342 if [[ -f ${r}$_rule ]]; then
1343 _found="${r}$_rule"
1344 inst_simple "$_found" "$_target/${_found##*/}"
1345 inst_rule_programs "$_found"
1346 fi
1347 done
1348 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
1349 _found=
1350 done
1351 }
1352
1353 # general purpose installation function
1354 # Same args as above.
1355 inst() {
1356 local _x
1357
1358 case $# in
1359 1) ;;
1360 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
1361 [[ $initdir = $2 ]] && set $1;;
1362 3) [[ -z $initdir ]] && export initdir=$2
1363 set $1 $3;;
1364 *) dfatal "inst only takes 1 or 2 or 3 arguments"
1365 exit 1;;
1366 esac
1367 for _x in inst_symlink inst_script inst_binary inst_simple; do
1368 $_x "$@" && return 0
1369 done
1370 return 1
1371 }
1372
1373 # install any of listed files
1374 #
1375 # If first argument is '-d' and second some destination path, first accessible
1376 # source is installed into this path, otherwise it will installed in the same
1377 # path as source. If none of listed files was installed, function return 1.
1378 # On first successful installation it returns with 0 status.
1379 #
1380 # Example:
1381 #
1382 # inst_any -d /bin/foo /bin/bar /bin/baz
1383 #
1384 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
1385 # initramfs.
1386 inst_any() {
1387 local to f
1388
1389 [[ $1 = '-d' ]] && to="$2" && shift 2
1390
1391 for f in "$@"; do
1392 if [[ -e $f ]]; then
1393 [[ $to ]] && inst "$f" "$to" && return 0
1394 inst "$f" && return 0
1395 fi
1396 done
1397
1398 return 1
1399 }
1400
1401 # dracut_install [-o ] <file> [<file> ... ]
1402 # Install <file> to the initramfs image
1403 # -o optionally install the <file> and don't fail, if it is not there
1404 dracut_install() {
1405 local _optional=no
1406 if [[ $1 = '-o' ]]; then
1407 _optional=yes
1408 shift
1409 fi
1410 while (($# > 0)); do
1411 if ! inst "$1" ; then
1412 if [[ $_optional = yes ]]; then
1413 dinfo "Skipping program $1 as it cannot be found and is" \
1414 "flagged to be optional"
1415 else
1416 dfatal "Failed to install $1"
1417 exit 1
1418 fi
1419 fi
1420 shift
1421 done
1422 }
1423
1424 # Install a single kernel module along with any firmware it may require.
1425 # $1 = full path to kernel module to install
1426 install_kmod_with_fw() {
1427 # no need to go further if the module is already installed
1428
1429 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
1430 && return 0
1431
1432 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1433
1434 if [[ $omit_drivers ]]; then
1435 local _kmod=${1##*/}
1436 _kmod=${_kmod%.ko}
1437 _kmod=${_kmod/-/_}
1438 if [[ "$_kmod" =~ $omit_drivers ]]; then
1439 dinfo "Omitting driver $_kmod"
1440 return 1
1441 fi
1442 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
1443 dinfo "Omitting driver $_kmod"
1444 return 1
1445 fi
1446 fi
1447
1448 [ -d "$initdir/.kernelmodseen" ] && \
1449 > "$initdir/.kernelmodseen/${1##*/}"
1450
1451 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
1452 || return $?
1453
1454 local _modname=${1##*/} _fwdir _found _fw
1455 _modname=${_modname%.ko*}
1456 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
1457 _found=''
1458 for _fwdir in $fw_dir; do
1459 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1460 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1461 _found=yes
1462 fi
1463 done
1464 if [[ $_found != yes ]]; then
1465 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1466 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1467 "\"${_modname}.ko\""
1468 else
1469 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1470 "\"${_modname}.ko\""
1471 fi
1472 fi
1473 done
1474 return 0
1475 }
1476
1477 # Do something with all the dependencies of a kernel module.
1478 # Note that kernel modules depend on themselves using the technique we use
1479 # $1 = function to call for each dependency we find
1480 # It will be passed the full path to the found kernel module
1481 # $2 = module to get dependencies for
1482 # rest of args = arguments to modprobe
1483 # _fderr specifies FD passed from surrounding scope
1484 for_each_kmod_dep() {
1485 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1486 shift 2
1487 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1488 while read _cmd _modpath _options; do
1489 [[ $_cmd = insmod ]] || continue
1490 $_func ${_modpath} || exit $?
1491 _found=1
1492 done
1493 [[ $_found -eq 0 ]] && exit 1
1494 exit 0
1495 )
1496 }
1497
1498 # filter kernel modules to install certain modules that meet specific
1499 # requirements.
1500 # $1 = search only in subdirectory of /kernel/$1
1501 # $2 = function to call with module name to filter.
1502 # This function will be passed the full path to the module to test.
1503 # The behavior of this function can vary depending on whether $hostonly is set.
1504 # If it is, we will only look at modules that are already in memory.
1505 # If it is not, we will look at all kernel modules
1506 # This function returns the full filenames of modules that match $1
1507 filter_kernel_modules_by_path () (
1508 local _modname _filtercmd
1509 if ! [[ $hostonly ]]; then
1510 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1511 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1512 _filtercmd+=' -o -name "*.ko.xz"'
1513 _filtercmd+=' 2>/dev/null'
1514 else
1515 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1516 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1517 fi
1518 for _modname in $(eval $_filtercmd); do
1519 case $_modname in
1520 *.ko) "$2" "$_modname" && echo "$_modname";;
1521 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1522 $2 $initdir/$$.ko && echo "$_modname"
1523 rm -f $initdir/$$.ko
1524 ;;
1525 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1526 $2 $initdir/$$.ko && echo "$_modname"
1527 rm -f $initdir/$$.ko
1528 ;;
1529 esac
1530 done
1531 )
1532 find_kernel_modules_by_path () (
1533 if ! [[ $hostonly ]]; then
1534 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1535 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1536 else
1537 cut -d " " -f 1 </proc/modules \
1538 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1539 fi
1540 )
1541
1542 filter_kernel_modules () {
1543 filter_kernel_modules_by_path drivers "$1"
1544 }
1545
1546 find_kernel_modules () {
1547 find_kernel_modules_by_path drivers
1548 }
1549
1550 # instmods [-c] <kernel module> [<kernel module> ... ]
1551 # instmods [-c] <kernel subsystem>
1552 # install kernel modules along with all their dependencies.
1553 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1554 instmods() {
1555 [[ $no_kernel = yes ]] && return
1556 # called [sub]functions inherit _fderr
1557 local _fderr=9
1558 local _check=no
1559 if [[ $1 = '-c' ]]; then
1560 _check=yes
1561 shift
1562 fi
1563
1564 function inst1mod() {
1565 local _ret=0 _mod="$1"
1566 case $_mod in
1567 =*)
1568 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1569 ( [[ "$_mpargs" ]] && echo $_mpargs
1570 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1571 | instmods
1572 else
1573 ( [[ "$_mpargs" ]] && echo $_mpargs
1574 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \
1575 | instmods
1576 fi
1577 ;;
1578 --*) _mpargs+=" $_mod" ;;
1579 i2o_scsi) return ;; # Do not load this diagnostic-only module
1580 *)
1581 _mod=${_mod##*/}
1582 # if we are already installed, skip this module and go on
1583 # to the next one.
1584 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1585
1586 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1587 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1588 return
1589 fi
1590 # If we are building a host-specific initramfs and this
1591 # module is not already loaded, move on to the next one.
1592 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1593 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1594 && return
1595
1596 # We use '-d' option in modprobe only if modules prefix path
1597 # differs from default '/'. This allows us to use Dracut with
1598 # old version of modprobe which doesn't have '-d' option.
1599 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1600 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1601
1602 # ok, load the module, all its dependencies, and any firmware
1603 # it may require
1604 for_each_kmod_dep install_kmod_with_fw $_mod \
1605 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1606 ((_ret+=$?))
1607 ;;
1608 esac
1609 return $_ret
1610 }
1611
1612 function instmods_1() {
1613 local _mod _mpargs
1614 if (($# == 0)); then # filenames from stdin
1615 while read _mod; do
1616 inst1mod "${_mod%.ko*}" || {
1617 if [ "$_check" = "yes" ]; then
1618 dfatal "Failed to install $_mod"
1619 return 1
1620 fi
1621 }
1622 done
1623 fi
1624 while (($# > 0)); do # filenames as arguments
1625 inst1mod ${1%.ko*} || {
1626 if [ "$_check" = "yes" ]; then
1627 dfatal "Failed to install $1"
1628 return 1
1629 fi
1630 }
1631 shift
1632 done
1633 return 0
1634 }
1635
1636 local _ret _filter_not_found='FATAL: Module .* not found.'
1637 set -o pipefail
1638 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1639 # redirections, but that would make dracut require bash4 at least.
1640 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1641 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1642 _ret=$?
1643 set +o pipefail
1644 return $_ret
1645 }
1646
1647 # inst_libdir_file [-n <pattern>] <file> [<file>...]
1648 # Install a <file> located on a lib directory to the initramfs image
1649 # -n <pattern> install non-matching files
1650 inst_libdir_file() {
1651 if [[ "$1" == "-n" ]]; then
1652 local _pattern=$1
1653 shift 2
1654 for _dir in $libdirs; do
1655 for _i in "$@"; do
1656 for _f in "$_dir"/$_i; do
1657 [[ "$_i" =~ $_pattern ]] || continue
1658 [[ -e "$_i" ]] && dracut_install "$_i"
1659 done
1660 done
1661 done
1662 else
1663 for _dir in $libdirs; do
1664 for _i in "$@"; do
1665 for _f in "$_dir"/$_i; do
1666 [[ -e "$_f" ]] && dracut_install "$_f"
1667 done
1668 done
1669 done
1670 fi
1671 }
1672
1673 setup_suse() {
1674 ln -fs ../usr/bin/systemctl $initdir/bin/
1675 ln -fs ../usr/lib/systemd $initdir/lib/
1676 inst_simple "/usr/lib/systemd/system/haveged.service"
1677 }
1678
1679 # can be overridden in specific test
1680 test_cleanup() {
1681 umount $TESTDIR/root 2>/dev/null || true
1682 [[ $LOOPDEV ]] && losetup -d $LOOPDEV || true
1683 return 0
1684 }
1685
1686 test_run() {
1687 if [ -z "$TEST_NO_QEMU" ]; then
1688 if run_qemu; then
1689 check_result_qemu || return 1
1690 else
1691 dwarn "can't run QEMU, skipping"
1692 fi
1693 fi
1694 if [ -z "$TEST_NO_NSPAWN" ]; then
1695 if run_nspawn "nspawn-root"; then
1696 check_result_nspawn "nspawn-root" || return 1
1697 else
1698 dwarn "can't run systemd-nspawn, skipping"
1699 fi
1700
1701 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
1702 if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
1703 check_result_nspawn "unprivileged-nspawn-root" || return 1
1704 else
1705 dwarn "can't run systemd-nspawn, skipping"
1706 fi
1707 fi
1708 fi
1709 return 0
1710 }
1711
1712 do_test() {
1713 if [[ $UID != "0" ]]; then
1714 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
1715 exit 0
1716 fi
1717
1718 # Detect lib paths
1719 [[ $libdir ]] || for libdir in /lib64 /lib; do
1720 [[ -d $libdir ]] && libdirs+=" $libdir" && break
1721 done
1722
1723 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
1724 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
1725 done
1726
1727 mkdir -p "$STATEDIR"
1728
1729 import_testdir
1730 import_initdir
1731
1732 while (($# > 0)); do
1733 case $1 in
1734 --run)
1735 echo "TEST RUN: $TEST_DESCRIPTION"
1736 test_run
1737 ret=$?
1738 if (( $ret == 0 )); then
1739 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
1740 else
1741 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
1742 fi
1743 exit $ret;;
1744 --setup)
1745 echo "TEST SETUP: $TEST_DESCRIPTION"
1746 test_setup
1747 ;;
1748 --clean)
1749 echo "TEST CLEANUP: $TEST_DESCRIPTION"
1750 test_cleanup
1751 rm -fr "$TESTDIR"
1752 rm -f "$STATEFILE"
1753 ;;
1754 --all)
1755 ret=0
1756 echo -n "TEST: $TEST_DESCRIPTION ";
1757 (
1758 test_setup && test_run
1759 ret=$?
1760 test_cleanup
1761 rm -fr "$TESTDIR"
1762 rm -f "$STATEFILE"
1763 exit $ret
1764 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
1765 if [ $ret -eq 0 ]; then
1766 rm "$TESTLOG"
1767 echo "[OK]"
1768 else
1769 echo "[FAILED]"
1770 echo "see $TESTLOG"
1771 fi
1772 exit $ret;;
1773 *) break ;;
1774 esac
1775 shift
1776 done
1777 }