]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-functions
test: be a little bit more verbose when installing service binaries
[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 dinfo "Attempting to install $i"
663 inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
664 done
665 )
666 }
667
668 generate_module_dependencies() {
669 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
670 ! depmod -a -b "$initdir" $KERNEL_VER; then
671 dfatal "\"depmod -a $KERNEL_VER\" failed."
672 exit 1
673 fi
674 }
675
676 install_depmod_files() {
677 inst /lib/modules/$KERNEL_VER/modules.order
678 inst /lib/modules/$KERNEL_VER/modules.builtin
679 }
680
681 install_plymouth() {
682 # install plymouth, if found... else remove plymouth service files
683 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
684 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
685 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
686 # dracut_install plymouth plymouthd
687 # else
688 rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
689 # fi
690 }
691
692 install_ld_so_conf() {
693 cp -a /etc/ld.so.conf* $initdir/etc
694 ldconfig -r "$initdir"
695 }
696
697 install_config_files() {
698 inst /etc/sysconfig/init || true
699 inst /etc/passwd
700 inst /etc/shadow
701 inst /etc/login.defs
702 inst /etc/group
703 inst /etc/shells
704 inst /etc/nsswitch.conf
705 inst /etc/pam.conf || true
706 inst /etc/securetty || true
707 inst /etc/os-release
708 inst /etc/localtime
709 # we want an empty environment
710 > $initdir/etc/environment
711 > $initdir/etc/machine-id
712 # set the hostname
713 echo systemd-testsuite > $initdir/etc/hostname
714 # fstab
715 if [[ "$LOOKS_LIKE_SUSE" ]]; then
716 ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1"
717 else
718 ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1"
719 fi
720
721 cat >$initdir/etc/fstab <<EOF
722 $ROOTMOUNT
723 EOF
724 }
725
726 install_basic_tools() {
727 [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
728 dracut_install -o sushell
729 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
730 dracut_install -o ldconfig.real
731 }
732
733 install_debug_tools() {
734 [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
735
736 if [[ $INTERACTIVE_DEBUG ]]; then
737 # Set default TERM from vt220 to linux, so at least basic key shortcuts work
738 local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
739 mkdir -p "$_getty_override"
740 echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
741
742 cat > "$initdir/etc/motd" << EOF
743 To adjust the terminal size use:
744 export COLUMNS=xx
745 export LINES=yy
746 or
747 stty cols xx rows yy
748 EOF
749 fi
750 }
751
752 install_libnss() {
753 # install libnss_files for login
754 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
755 dracut_install $NSS_LIBS
756 }
757
758 install_dbus() {
759 inst $ROOTLIBDIR/system/dbus.socket
760
761 # Newer Fedora versions use dbus-broker by default. Let's install it is available.
762 if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
763 inst $ROOTLIBDIR/system/dbus-broker.service
764 inst_symlink /etc/systemd/system/dbus.service
765 inst /usr/bin/dbus-broker
766 inst /usr/bin/dbus-broker-launch
767 elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
768 # Fedora rawhide replaced dbus.service with dbus-daemon.service
769 inst $ROOTLIBDIR/system/dbus-daemon.service
770 # Alias symlink
771 inst_symlink /etc/systemd/system/dbus.service
772 else
773 inst $ROOTLIBDIR/system/dbus.service
774 fi
775
776 find \
777 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
778 | while read file; do
779 inst $file
780 done
781 }
782
783 install_pam() {
784 (
785 if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
786 find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
787 else
788 find /lib*/security -xtype f
789 fi
790 find /etc/pam.d /etc/security -xtype f
791 ) | while read file; do
792 inst $file
793 done
794
795 # pam_unix depends on unix_chkpwd.
796 # see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
797 dracut_install -o unix_chkpwd
798
799 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
800 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
801
802 # set empty root password for easy debugging
803 sed -i 's/^root:x:/root::/' $initdir/etc/passwd
804 }
805
806 install_keymaps() {
807 # The first three paths may be deprecated.
808 # It seems now the last two paths are used by many distributions.
809 for i in \
810 /usr/lib/kbd/keymaps/include/* \
811 /usr/lib/kbd/keymaps/i386/include/* \
812 /usr/lib/kbd/keymaps/i386/qwerty/us.* \
813 /usr/lib/kbd/keymaps/legacy/include/* \
814 /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
815 [[ -f $i ]] || continue
816 inst $i
817 done
818
819 # When it takes any argument, then install more keymaps.
820 if [[ -n $1 ]]; then
821 for i in \
822 /usr/lib/kbd/keymaps/i386/*/* \
823 /usr/lib/kbd/keymaps/legacy/i386/*/*; do
824 [[ -f $i ]] || continue
825 inst $i
826 done
827 fi
828 }
829
830 install_zoneinfo() {
831 for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
832 [[ -f $i ]] || continue
833 inst $i
834 done
835 }
836
837 install_fonts() {
838 for i in \
839 /usr/lib/kbd/consolefonts/eurlatgr* \
840 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
841 [[ -f $i ]] || continue
842 inst $i
843 done
844 }
845
846 install_terminfo() {
847 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
848 [ -f ${_terminfodir}/l/linux ] && break
849 done
850 dracut_install -o ${_terminfodir}/l/linux
851 }
852
853 setup_testsuite() {
854 cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
855 cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
856
857 mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
858 ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
859 # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
860 [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
861
862 # make the testsuite the default target
863 ln -fs testsuite.target $initdir/etc/systemd/system/default.target
864 }
865
866 setup_nspawn_root() {
867 rm -fr $TESTDIR/nspawn-root
868 ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
869 cp -ar $initdir $TESTDIR/nspawn-root
870 # we don't mount in the nspawn root
871 rm -f $TESTDIR/nspawn-root/etc/fstab
872 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
873 cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
874 fi
875 }
876
877 setup_basic_dirs() {
878 mkdir -p $initdir/run
879 mkdir -p $initdir/etc/systemd/system
880 mkdir -p $initdir/var/log/journal
881
882 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
883 if [ -L "/$d" ]; then
884 inst_symlink "/$d"
885 else
886 inst_dir "/$d"
887 fi
888 done
889
890 ln -sfn /run "$initdir/var/run"
891 ln -sfn /run/lock "$initdir/var/lock"
892 }
893
894 inst_libs() {
895 local _bin=$1
896 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
897 local _file _line
898
899 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
900 [[ $_line = 'not a dynamic executable' ]] && break
901
902 if [[ $_line =~ $_so_regex ]]; then
903 _file=${BASH_REMATCH[1]}
904 [[ -e ${initdir}/$_file ]] && continue
905 inst_library "$_file"
906 continue
907 fi
908
909 if [[ $_line =~ not\ found ]]; then
910 dfatal "Missing a shared library required by $_bin."
911 dfatal "Run \"ldd $_bin\" to find out what it is."
912 dfatal "$_line"
913 dfatal "dracut cannot create an initrd."
914 exit 1
915 fi
916 done
917 }
918
919 import_testdir() {
920 [[ -e $STATEFILE ]] && . $STATEFILE
921 if [[ ! -d "$TESTDIR" ]]; then
922 if [[ -z "$TESTDIR" ]]; then
923 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
924 else
925 mkdir -p "$TESTDIR"
926 fi
927
928 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
929 export TESTDIR
930 fi
931 }
932
933 import_initdir() {
934 initdir=$TESTDIR/root
935 export initdir
936 }
937
938 ## @brief Converts numeric logging level to the first letter of level name.
939 #
940 # @param lvl Numeric logging level in range from 1 to 6.
941 # @retval 1 if @a lvl is out of range.
942 # @retval 0 if @a lvl is correct.
943 # @result Echoes first letter of level name.
944 _lvl2char() {
945 case "$1" in
946 1) echo F;;
947 2) echo E;;
948 3) echo W;;
949 4) echo I;;
950 5) echo D;;
951 6) echo T;;
952 *) return 1;;
953 esac
954 }
955
956 ## @brief Internal helper function for _do_dlog()
957 #
958 # @param lvl Numeric logging level.
959 # @param msg Message.
960 # @retval 0 It's always returned, even if logging failed.
961 #
962 # @note This function is not supposed to be called manually. Please use
963 # dtrace(), ddebug(), or others instead which wrap this one.
964 #
965 # This function calls _do_dlog() either with parameter msg, or if
966 # none is given, it will read standard input and will use every line as
967 # a message.
968 #
969 # This enables:
970 # dwarn "This is a warning"
971 # echo "This is a warning" | dwarn
972 LOG_LEVEL=${LOG_LEVEL:-4}
973
974 dlog() {
975 [ -z "$LOG_LEVEL" ] && return 0
976 [ $1 -le $LOG_LEVEL ] || return 0
977 local lvl="$1"; shift
978 local lvlc=$(_lvl2char "$lvl") || return 0
979
980 if [ $# -ge 1 ]; then
981 echo "$lvlc: $*"
982 else
983 while read line; do
984 echo "$lvlc: " "$line"
985 done
986 fi
987 }
988
989 ## @brief Logs message at TRACE level (6)
990 #
991 # @param msg Message.
992 # @retval 0 It's always returned, even if logging failed.
993 dtrace() {
994 set +x
995 dlog 6 "$@"
996 [ -n "$debug" ] && set -x || :
997 }
998
999 ## @brief Logs message at DEBUG level (5)
1000 #
1001 # @param msg Message.
1002 # @retval 0 It's always returned, even if logging failed.
1003 ddebug() {
1004 # set +x
1005 dlog 5 "$@"
1006 # [ -n "$debug" ] && set -x || :
1007 }
1008
1009 ## @brief Logs message at INFO level (4)
1010 #
1011 # @param msg Message.
1012 # @retval 0 It's always returned, even if logging failed.
1013 dinfo() {
1014 set +x
1015 dlog 4 "$@"
1016 [ -n "$debug" ] && set -x || :
1017 }
1018
1019 ## @brief Logs message at WARN level (3)
1020 #
1021 # @param msg Message.
1022 # @retval 0 It's always returned, even if logging failed.
1023 dwarn() {
1024 set +x
1025 dlog 3 "$@"
1026 [ -n "$debug" ] && set -x || :
1027 }
1028
1029 ## @brief Logs message at ERROR level (2)
1030 #
1031 # @param msg Message.
1032 # @retval 0 It's always returned, even if logging failed.
1033 derror() {
1034 # set +x
1035 dlog 2 "$@"
1036 # [ -n "$debug" ] && set -x || :
1037 }
1038
1039 ## @brief Logs message at FATAL level (1)
1040 #
1041 # @param msg Message.
1042 # @retval 0 It's always returned, even if logging failed.
1043 dfatal() {
1044 set +x
1045 dlog 1 "$@"
1046 [ -n "$debug" ] && set -x || :
1047 }
1048
1049
1050 # Generic substring function. If $2 is in $1, return 0.
1051 strstr() { [ "${1#*$2*}" != "$1" ]; }
1052
1053 # normalize_path <path>
1054 # Prints the normalized path, where it removes any duplicated
1055 # and trailing slashes.
1056 # Example:
1057 # $ normalize_path ///test/test//
1058 # /test/test
1059 normalize_path() {
1060 shopt -q -s extglob
1061 set -- "${1//+(\/)//}"
1062 shopt -q -u extglob
1063 echo "${1%/}"
1064 }
1065
1066 # convert_abs_rel <from> <to>
1067 # Prints the relative path, when creating a symlink to <to> from <from>.
1068 # Example:
1069 # $ convert_abs_rel /usr/bin/test /bin/test-2
1070 # ../../bin/test-2
1071 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
1072 convert_abs_rel() {
1073 local __current __absolute __abssize __cursize __newpath
1074 local -i __i __level
1075
1076 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
1077
1078 # corner case #1 - self looping link
1079 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
1080
1081 # corner case #2 - own dir link
1082 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
1083
1084 IFS="/" __current=($1)
1085 IFS="/" __absolute=($2)
1086
1087 __abssize=${#__absolute[@]}
1088 __cursize=${#__current[@]}
1089
1090 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
1091 do
1092 (( __level++ ))
1093 if (( __level > __abssize || __level > __cursize ))
1094 then
1095 break
1096 fi
1097 done
1098
1099 for ((__i = __level; __i < __cursize-1; __i++))
1100 do
1101 if ((__i > __level))
1102 then
1103 __newpath=$__newpath"/"
1104 fi
1105 __newpath=$__newpath".."
1106 done
1107
1108 for ((__i = __level; __i < __abssize; __i++))
1109 do
1110 if [[ -n $__newpath ]]
1111 then
1112 __newpath=$__newpath"/"
1113 fi
1114 __newpath=$__newpath${__absolute[__i]}
1115 done
1116
1117 echo "$__newpath"
1118 }
1119
1120
1121 # Install a directory, keeping symlinks as on the original system.
1122 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
1123 # will create ${initdir}/lib64, ${initdir}/lib64/file,
1124 # and a symlink ${initdir}/lib -> lib64.
1125 inst_dir() {
1126 [[ -e ${initdir}/"$1" ]] && return 0 # already there
1127
1128 local _dir="$1" _part="${1%/*}" _file
1129 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
1130 _dir="$_part $_dir"
1131 _part=${_part%/*}
1132 done
1133
1134 # iterate over parent directories
1135 for _file in $_dir; do
1136 [[ -e "${initdir}/$_file" ]] && continue
1137 if [[ -L $_file ]]; then
1138 inst_symlink "$_file"
1139 else
1140 # create directory
1141 mkdir -m 0755 -p "${initdir}/$_file" || return 1
1142 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
1143 chmod u+w "${initdir}/$_file"
1144 fi
1145 done
1146 }
1147
1148 # $1 = file to copy to ramdisk
1149 # $2 (optional) Name for the file on the ramdisk
1150 # Location of the image dir is assumed to be $initdir
1151 # We never overwrite the target if it exists.
1152 inst_simple() {
1153 [[ -f "$1" ]] || return 1
1154 strstr "$1" "/" || return 1
1155
1156 local _src=$1 target="${2:-$1}"
1157 if ! [[ -d ${initdir}/$target ]]; then
1158 [[ -e ${initdir}/$target ]] && return 0
1159 [[ -L ${initdir}/$target ]] && return 0
1160 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
1161 fi
1162 # install checksum files also
1163 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1164 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
1165 fi
1166 ddebug "Installing $_src"
1167 cp --sparse=always -pfL "$_src" "${initdir}/$target"
1168 }
1169
1170 # find symlinks linked to given library file
1171 # $1 = library file
1172 # Function searches for symlinks by stripping version numbers appended to
1173 # library filename, checks if it points to the same target and finally
1174 # prints the list of symlinks to stdout.
1175 #
1176 # Example:
1177 # rev_lib_symlinks libfoo.so.8.1
1178 # output: libfoo.so.8 libfoo.so
1179 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
1180 rev_lib_symlinks() {
1181 [[ ! $1 ]] && return 0
1182
1183 local fn="$1" orig="$(readlink -f "$1")" links=''
1184
1185 [[ ${fn} =~ .*\.so\..* ]] || return 1
1186
1187 until [[ ${fn##*.} == so ]]; do
1188 fn="${fn%.*}"
1189 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
1190 done
1191
1192 echo "${links}"
1193 }
1194
1195 # Same as above, but specialized to handle dynamic libraries.
1196 # It handles making symlinks according to how the original library
1197 # is referenced.
1198 inst_library() {
1199 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
1200 strstr "$1" "/" || return 1
1201 [[ -e $initdir/$_dest ]] && return 0
1202 if [[ -L $_src ]]; then
1203 # install checksum files also
1204 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
1205 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
1206 fi
1207 _reallib=$(readlink -f "$_src")
1208 inst_simple "$_reallib" "$_reallib"
1209 inst_dir "${_dest%/*}"
1210 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
1211 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
1212 else
1213 inst_simple "$_src" "$_dest"
1214 fi
1215
1216 # Create additional symlinks. See rev_symlinks description.
1217 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
1218 [[ -e $initdir/$_symlink ]] || {
1219 ddebug "Creating extra symlink: $_symlink"
1220 inst_symlink $_symlink
1221 }
1222 done
1223 }
1224
1225 # find a binary. If we were not passed the full path directly,
1226 # search in the usual places to find the binary.
1227 find_binary() {
1228 if [[ -z ${1##/*} ]]; then
1229 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
1230 echo $1
1231 return 0
1232 fi
1233 fi
1234
1235 type -P $1
1236 }
1237
1238 # Same as above, but specialized to install binary executables.
1239 # Install binary executable, and all shared library dependencies, if any.
1240 inst_binary() {
1241 local _bin _target
1242
1243 # In certain cases we might attempt to install a binary which is already
1244 # present in the test image, yet it's missing from the host system.
1245 # In such cases, let's check if the binary indeed exists in the image
1246 # before doing any other chcecks. If it does, immediately return with
1247 # success.
1248 [[ $# -eq 1 && -e $initdir/$1 ]] && return 0
1249
1250 _bin=$(find_binary "$1") || return 1
1251 _target=${2:-$_bin}
1252 [[ -e $initdir/$_target ]] && return 0
1253 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
1254 local _file _line
1255 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
1256 # I love bash!
1257 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
1258 [[ $_line = 'not a dynamic executable' ]] && break
1259
1260 if [[ $_line =~ $_so_regex ]]; then
1261 _file=${BASH_REMATCH[1]}
1262 [[ -e ${initdir}/$_file ]] && continue
1263 inst_library "$_file"
1264 continue
1265 fi
1266
1267 if [[ $_line =~ not\ found ]]; then
1268 dfatal "Missing a shared library required by $_bin."
1269 dfatal "Run \"ldd $_bin\" to find out what it is."
1270 dfatal "$_line"
1271 dfatal "dracut cannot create an initrd."
1272 exit 1
1273 fi
1274 done
1275 inst_simple "$_bin" "$_target"
1276 }
1277
1278 # same as above, except for shell scripts.
1279 # If your shell script does not start with shebang, it is not a shell script.
1280 inst_script() {
1281 local _bin
1282 _bin=$(find_binary "$1") || return 1
1283 shift
1284 local _line _shebang_regex
1285 read -r -n 80 _line <"$_bin"
1286 # If debug is set, clean unprintable chars to prevent messing up the term
1287 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
1288 _shebang_regex='(#! *)(/[^ ]+).*'
1289 [[ $_line =~ $_shebang_regex ]] || return 1
1290 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
1291 }
1292
1293 # same as above, but specialized for symlinks
1294 inst_symlink() {
1295 local _src=$1 _target=${2:-$1} _realsrc
1296 strstr "$1" "/" || return 1
1297 [[ -L $1 ]] || return 1
1298 [[ -L $initdir/$_target ]] && return 0
1299 _realsrc=$(readlink -f "$_src")
1300 if ! [[ -e $initdir/$_realsrc ]]; then
1301 if [[ -d $_realsrc ]]; then
1302 inst_dir "$_realsrc"
1303 else
1304 inst "$_realsrc"
1305 fi
1306 fi
1307 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
1308 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
1309 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
1310 }
1311
1312 # attempt to install any programs specified in a udev rule
1313 inst_rule_programs() {
1314 local _prog _bin
1315
1316 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
1317 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
1318 if [ -x /lib/udev/$_prog ]; then
1319 _bin=/lib/udev/$_prog
1320 else
1321 _bin=$(find_binary "$_prog") || {
1322 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
1323 continue;
1324 }
1325 fi
1326
1327 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
1328 dracut_install "$_bin"
1329 done
1330 fi
1331 }
1332
1333 # udev rules always get installed in the same place, so
1334 # create a function to install them to make life simpler.
1335 inst_rules() {
1336 local _target=/etc/udev/rules.d _rule _found
1337
1338 inst_dir "/lib/udev/rules.d"
1339 inst_dir "$_target"
1340 for _rule in "$@"; do
1341 if [ "${rule#/}" = "$rule" ]; then
1342 for r in /lib/udev/rules.d /etc/udev/rules.d; do
1343 if [[ -f $r/$_rule ]]; then
1344 _found="$r/$_rule"
1345 inst_simple "$_found"
1346 inst_rule_programs "$_found"
1347 fi
1348 done
1349 fi
1350 for r in '' ./ $dracutbasedir/rules.d/; do
1351 if [[ -f ${r}$_rule ]]; then
1352 _found="${r}$_rule"
1353 inst_simple "$_found" "$_target/${_found##*/}"
1354 inst_rule_programs "$_found"
1355 fi
1356 done
1357 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
1358 _found=
1359 done
1360 }
1361
1362 # general purpose installation function
1363 # Same args as above.
1364 inst() {
1365 local _x
1366
1367 case $# in
1368 1) ;;
1369 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
1370 [[ $initdir = $2 ]] && set $1;;
1371 3) [[ -z $initdir ]] && export initdir=$2
1372 set $1 $3;;
1373 *) dfatal "inst only takes 1 or 2 or 3 arguments"
1374 exit 1;;
1375 esac
1376 for _x in inst_symlink inst_script inst_binary inst_simple; do
1377 $_x "$@" && return 0
1378 done
1379 return 1
1380 }
1381
1382 # install any of listed files
1383 #
1384 # If first argument is '-d' and second some destination path, first accessible
1385 # source is installed into this path, otherwise it will installed in the same
1386 # path as source. If none of listed files was installed, function return 1.
1387 # On first successful installation it returns with 0 status.
1388 #
1389 # Example:
1390 #
1391 # inst_any -d /bin/foo /bin/bar /bin/baz
1392 #
1393 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
1394 # initramfs.
1395 inst_any() {
1396 local to f
1397
1398 [[ $1 = '-d' ]] && to="$2" && shift 2
1399
1400 for f in "$@"; do
1401 if [[ -e $f ]]; then
1402 [[ $to ]] && inst "$f" "$to" && return 0
1403 inst "$f" && return 0
1404 fi
1405 done
1406
1407 return 1
1408 }
1409
1410 # dracut_install [-o ] <file> [<file> ... ]
1411 # Install <file> to the initramfs image
1412 # -o optionally install the <file> and don't fail, if it is not there
1413 dracut_install() {
1414 local _optional=no
1415 if [[ $1 = '-o' ]]; then
1416 _optional=yes
1417 shift
1418 fi
1419 while (($# > 0)); do
1420 if ! inst "$1" ; then
1421 if [[ $_optional = yes ]]; then
1422 dinfo "Skipping program $1 as it cannot be found and is" \
1423 "flagged to be optional"
1424 else
1425 dfatal "Failed to install $1"
1426 exit 1
1427 fi
1428 fi
1429 shift
1430 done
1431 }
1432
1433 # Install a single kernel module along with any firmware it may require.
1434 # $1 = full path to kernel module to install
1435 install_kmod_with_fw() {
1436 # no need to go further if the module is already installed
1437
1438 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
1439 && return 0
1440
1441 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1442
1443 if [[ $omit_drivers ]]; then
1444 local _kmod=${1##*/}
1445 _kmod=${_kmod%.ko}
1446 _kmod=${_kmod/-/_}
1447 if [[ "$_kmod" =~ $omit_drivers ]]; then
1448 dinfo "Omitting driver $_kmod"
1449 return 1
1450 fi
1451 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
1452 dinfo "Omitting driver $_kmod"
1453 return 1
1454 fi
1455 fi
1456
1457 [ -d "$initdir/.kernelmodseen" ] && \
1458 > "$initdir/.kernelmodseen/${1##*/}"
1459
1460 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
1461 || return $?
1462
1463 local _modname=${1##*/} _fwdir _found _fw
1464 _modname=${_modname%.ko*}
1465 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
1466 _found=''
1467 for _fwdir in $fw_dir; do
1468 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1469 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1470 _found=yes
1471 fi
1472 done
1473 if [[ $_found != yes ]]; then
1474 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1475 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1476 "\"${_modname}.ko\""
1477 else
1478 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1479 "\"${_modname}.ko\""
1480 fi
1481 fi
1482 done
1483 return 0
1484 }
1485
1486 # Do something with all the dependencies of a kernel module.
1487 # Note that kernel modules depend on themselves using the technique we use
1488 # $1 = function to call for each dependency we find
1489 # It will be passed the full path to the found kernel module
1490 # $2 = module to get dependencies for
1491 # rest of args = arguments to modprobe
1492 # _fderr specifies FD passed from surrounding scope
1493 for_each_kmod_dep() {
1494 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1495 shift 2
1496 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1497 while read _cmd _modpath _options; do
1498 [[ $_cmd = insmod ]] || continue
1499 $_func ${_modpath} || exit $?
1500 _found=1
1501 done
1502 [[ $_found -eq 0 ]] && exit 1
1503 exit 0
1504 )
1505 }
1506
1507 # filter kernel modules to install certain modules that meet specific
1508 # requirements.
1509 # $1 = search only in subdirectory of /kernel/$1
1510 # $2 = function to call with module name to filter.
1511 # This function will be passed the full path to the module to test.
1512 # The behavior of this function can vary depending on whether $hostonly is set.
1513 # If it is, we will only look at modules that are already in memory.
1514 # If it is not, we will look at all kernel modules
1515 # This function returns the full filenames of modules that match $1
1516 filter_kernel_modules_by_path () (
1517 local _modname _filtercmd
1518 if ! [[ $hostonly ]]; then
1519 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1520 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1521 _filtercmd+=' -o -name "*.ko.xz"'
1522 _filtercmd+=' 2>/dev/null'
1523 else
1524 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1525 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1526 fi
1527 for _modname in $(eval $_filtercmd); do
1528 case $_modname in
1529 *.ko) "$2" "$_modname" && echo "$_modname";;
1530 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1531 $2 $initdir/$$.ko && echo "$_modname"
1532 rm -f $initdir/$$.ko
1533 ;;
1534 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1535 $2 $initdir/$$.ko && echo "$_modname"
1536 rm -f $initdir/$$.ko
1537 ;;
1538 esac
1539 done
1540 )
1541 find_kernel_modules_by_path () (
1542 if ! [[ $hostonly ]]; then
1543 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1544 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1545 else
1546 cut -d " " -f 1 </proc/modules \
1547 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1548 fi
1549 )
1550
1551 filter_kernel_modules () {
1552 filter_kernel_modules_by_path drivers "$1"
1553 }
1554
1555 find_kernel_modules () {
1556 find_kernel_modules_by_path drivers
1557 }
1558
1559 # instmods [-c] <kernel module> [<kernel module> ... ]
1560 # instmods [-c] <kernel subsystem>
1561 # install kernel modules along with all their dependencies.
1562 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1563 instmods() {
1564 [[ $no_kernel = yes ]] && return
1565 # called [sub]functions inherit _fderr
1566 local _fderr=9
1567 local _check=no
1568 if [[ $1 = '-c' ]]; then
1569 _check=yes
1570 shift
1571 fi
1572
1573 function inst1mod() {
1574 local _ret=0 _mod="$1"
1575 case $_mod in
1576 =*)
1577 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1578 ( [[ "$_mpargs" ]] && echo $_mpargs
1579 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1580 | instmods
1581 else
1582 ( [[ "$_mpargs" ]] && echo $_mpargs
1583 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \
1584 | instmods
1585 fi
1586 ;;
1587 --*) _mpargs+=" $_mod" ;;
1588 i2o_scsi) return ;; # Do not load this diagnostic-only module
1589 *)
1590 _mod=${_mod##*/}
1591 # if we are already installed, skip this module and go on
1592 # to the next one.
1593 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1594
1595 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1596 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1597 return
1598 fi
1599 # If we are building a host-specific initramfs and this
1600 # module is not already loaded, move on to the next one.
1601 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1602 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1603 && return
1604
1605 # We use '-d' option in modprobe only if modules prefix path
1606 # differs from default '/'. This allows us to use Dracut with
1607 # old version of modprobe which doesn't have '-d' option.
1608 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1609 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1610
1611 # ok, load the module, all its dependencies, and any firmware
1612 # it may require
1613 for_each_kmod_dep install_kmod_with_fw $_mod \
1614 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1615 ((_ret+=$?))
1616 ;;
1617 esac
1618 return $_ret
1619 }
1620
1621 function instmods_1() {
1622 local _mod _mpargs
1623 if (($# == 0)); then # filenames from stdin
1624 while read _mod; do
1625 inst1mod "${_mod%.ko*}" || {
1626 if [ "$_check" = "yes" ]; then
1627 dfatal "Failed to install $_mod"
1628 return 1
1629 fi
1630 }
1631 done
1632 fi
1633 while (($# > 0)); do # filenames as arguments
1634 inst1mod ${1%.ko*} || {
1635 if [ "$_check" = "yes" ]; then
1636 dfatal "Failed to install $1"
1637 return 1
1638 fi
1639 }
1640 shift
1641 done
1642 return 0
1643 }
1644
1645 local _ret _filter_not_found='FATAL: Module .* not found.'
1646 set -o pipefail
1647 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1648 # redirections, but that would make dracut require bash4 at least.
1649 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1650 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1651 _ret=$?
1652 set +o pipefail
1653 return $_ret
1654 }
1655
1656 # inst_libdir_file [-n <pattern>] <file> [<file>...]
1657 # Install a <file> located on a lib directory to the initramfs image
1658 # -n <pattern> install non-matching files
1659 inst_libdir_file() {
1660 if [[ "$1" == "-n" ]]; then
1661 local _pattern=$1
1662 shift 2
1663 for _dir in $libdirs; do
1664 for _i in "$@"; do
1665 for _f in "$_dir"/$_i; do
1666 [[ "$_i" =~ $_pattern ]] || continue
1667 [[ -e "$_i" ]] && dracut_install "$_i"
1668 done
1669 done
1670 done
1671 else
1672 for _dir in $libdirs; do
1673 for _i in "$@"; do
1674 for _f in "$_dir"/$_i; do
1675 [[ -e "$_f" ]] && dracut_install "$_f"
1676 done
1677 done
1678 done
1679 fi
1680 }
1681
1682 setup_suse() {
1683 ln -fs ../usr/bin/systemctl $initdir/bin/
1684 ln -fs ../usr/lib/systemd $initdir/lib/
1685 inst_simple "/usr/lib/systemd/system/haveged.service"
1686 }
1687
1688 # can be overridden in specific test
1689 test_cleanup() {
1690 umount $TESTDIR/root 2>/dev/null || true
1691 [[ $LOOPDEV ]] && losetup -d $LOOPDEV || true
1692 return 0
1693 }
1694
1695 test_run() {
1696 if [ -z "$TEST_NO_QEMU" ]; then
1697 if run_qemu; then
1698 check_result_qemu || return 1
1699 else
1700 dwarn "can't run QEMU, skipping"
1701 fi
1702 fi
1703 if [ -z "$TEST_NO_NSPAWN" ]; then
1704 if run_nspawn "nspawn-root"; then
1705 check_result_nspawn "nspawn-root" || return 1
1706 else
1707 dwarn "can't run systemd-nspawn, skipping"
1708 fi
1709
1710 if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
1711 if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
1712 check_result_nspawn "unprivileged-nspawn-root" || return 1
1713 else
1714 dwarn "can't run systemd-nspawn, skipping"
1715 fi
1716 fi
1717 fi
1718 return 0
1719 }
1720
1721 do_test() {
1722 if [[ $UID != "0" ]]; then
1723 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
1724 exit 0
1725 fi
1726
1727 # Detect lib paths
1728 [[ $libdir ]] || for libdir in /lib64 /lib; do
1729 [[ -d $libdir ]] && libdirs+=" $libdir" && break
1730 done
1731
1732 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
1733 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
1734 done
1735
1736 mkdir -p "$STATEDIR"
1737
1738 import_testdir
1739 import_initdir
1740
1741 while (($# > 0)); do
1742 case $1 in
1743 --run)
1744 echo "TEST RUN: $TEST_DESCRIPTION"
1745 test_run
1746 ret=$?
1747 if (( $ret == 0 )); then
1748 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
1749 else
1750 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
1751 fi
1752 exit $ret;;
1753 --setup)
1754 echo "TEST SETUP: $TEST_DESCRIPTION"
1755 test_setup
1756 ;;
1757 --clean)
1758 echo "TEST CLEANUP: $TEST_DESCRIPTION"
1759 test_cleanup
1760 rm -fr "$TESTDIR"
1761 rm -f "$STATEFILE"
1762 ;;
1763 --all)
1764 ret=0
1765 echo -n "TEST: $TEST_DESCRIPTION ";
1766 (
1767 test_setup && test_run
1768 ret=$?
1769 test_cleanup
1770 rm -fr "$TESTDIR"
1771 rm -f "$STATEFILE"
1772 exit $ret
1773 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
1774 if [ $ret -eq 0 ]; then
1775 rm "$TESTLOG"
1776 echo "[OK]"
1777 else
1778 echo "[FAILED]"
1779 echo "see $TESTLOG"
1780 fi
1781 exit $ret;;
1782 *) break ;;
1783 esac
1784 shift
1785 done
1786 }