]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-functions
tests: remove unnecessary --boot
[thirdparty/systemd.git] / test / test-functions
CommitLineData
898720b7
HH
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
4PATH=/sbin:/bin:/usr/sbin:/usr/bin
5export PATH
6
0fe15dc8 7LOOKS_LIKE_DEBIAN=$(source /etc/os-release && [[ "$ID" = "debian" || "$ID_LIKE" = "debian" ]] && echo yes)
0d6e798a
HH
8KERNEL_VER=${KERNEL_VER-$(uname -r)}
9KERNEL_MODS="/lib/modules/$KERNEL_VER/"
898720b7 10
3486cb6c
MP
11if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
12 echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
13 ROOTLIBDIR=/usr/lib/systemd
14fi
15
1c36b4a7 16BASICTOOLS="sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee"
c50a4525 17DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname"
889a9042 18
c6a77179
RC
19function find_qemu_bin() {
20 # SUSE and Red Hat call the binary qemu-kvm
21 # Debian and Gentoo call it kvm
22 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
23
24 [ "$ARCH" ] || ARCH=$(uname -m)
25 case $ARCH in
26 x86_64)
27 # QEMU's own build system calls it qemu-system-x86_64
28 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-x86_64 2>/dev/null | grep '^/' -m1)
29 ;;
30 i*86)
31 # new i386 version of QEMU
32 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-i386 2>/dev/null | grep '^/' -m1)
33
34 # i386 version of QEMU
35 [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu 2>/dev/null | grep '^/' -m1)
36 ;;
37 esac
38
39 if [ ! -e "$QEMU_BIN" ]; then
40 echo "Could not find a suitable QEMU binary" >&2
41 return 1
42 fi
43}
44
889a9042 45run_qemu() {
b6f0c419
HH
46 if [ -f /etc/machine-id ]; then
47 read MACHINE_ID < /etc/machine-id
48 [ -z "$INITRD" ] && [ -e "/boot/$MACHINE_ID/$KERNEL_VER/initrd" ] \
49 && INITRD="/boot/$MACHINE_ID/$KERNEL_VER/initrd"
50 [ -z "$KERNEL_BIN" ] && [ -e "/boot/$MACHINE_ID/$KERNEL_VER/linux" ] \
51 && KERNEL_BIN="/boot/$MACHINE_ID/$KERNEL_VER/linux"
52 fi
53
61fea35e
EV
54 default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
55 default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
c6a77179 56 [ "$KERNEL_BIN" ] || KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
61fea35e
EV
57 [ "$INITRD" ] || { [ -e "$default_fedora_initrd" ] && INITRD=$default_fedora_initrd; }
58 [ "$INITRD" ] || { [ "$LOOKS_LIKE_DEBIAN" ] && [ -e "$default_debian_initrd" ] && INITRD=$default_debian_initrd; }
c6a77179
RC
59 [ "$QEMU_SMP" ] || QEMU_SMP=1
60
61 find_qemu_bin || return 1
62
63 KERNEL_APPEND="root=/dev/sda1 \
64systemd.log_level=debug \
65raid=noautodetect \
66loglevel=2 \
3486cb6c 67init=$ROOTLIBDIR/systemd \
c6a77179
RC
68ro \
69console=ttyS0 \
70selinux=0 \
71$KERNEL_APPEND \
72"
73
dbf43a42 74 QEMU_OPTIONS="-smp $QEMU_SMP \
c6a77179
RC
75-net none \
76-m 512M \
77-nographic \
78-kernel $KERNEL_BIN \
79"
80
81 if [ "$INITRD" ]; then
82 QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
83 fi
84
dbf43a42
DM
85 if [ -c /dev/kvm ]; then
86 QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
87 fi
88
8a8332f7
ZJS
89 ( set -x
90 $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND" $TESTDIR/rootdisk.img ) || return 1
889a9042
RC
91}
92
93run_nspawn() {
8a8332f7 94 set -x
24be78d7 95 ../../systemd-nspawn --register=no --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND
889a9042
RC
96}
97
98setup_basic_environment() {
99 # create the basic filesystem layout
100 setup_basic_dirs
101
102 install_systemd
103 install_missing_libraries
104 install_config_files
105 create_rc_local
106 install_basic_tools
107 install_libnss
108 install_pam
109 install_dbus
110 install_fonts
111 install_keymaps
112 install_terminfo
113 install_execs
114 install_plymouth
115 install_debug_tools
116 install_ld_so_conf
117 strip_binaries
118 install_depmod_files
119 generate_module_dependencies
889a9042
RC
120}
121
a2fbff31
EV
122install_valgrind() {
123 if ! type -p valgrind; then
124 dfatal "Failed to install valgrind"
125 exit 1
126 fi
127
128 local _valgrind_bins=$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
129 dracut_install $_valgrind_bins
130
131 local _valgrind_libs=$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
132 dracut_install $_valgrind_libs
133
134 local _valgrind_dbg_and_supp=$(
135 strace -e open valgrind /bin/true 2>&1 >/dev/null |
136 perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
137 )
138 dracut_install $_valgrind_dbg_and_supp
139}
140
cb2f9d3f
EV
141create_valgrind_wrapper() {
142 local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
143 ddebug "Create $_valgrind_wrapper"
144 cat >$_valgrind_wrapper <<EOF
145#!/bin/bash
146
147exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
148EOF
149 chmod 0755 $_valgrind_wrapper
150}
151
889a9042
RC
152install_dmevent() {
153 instmods dm_crypt =crypto
154 type -P dmeventd >/dev/null && dracut_install dmeventd
155 inst_libdir_file "libdevmapper-event.so*"
156 inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
157}
158
159install_systemd() {
160 # install compiled files
8a8332f7 161 (cd $TEST_BASE_DIR/..; set -x; make DESTDIR=$initdir install)
889a9042 162 # remove unneeded documentation
23756070 163 rm -fr $initdir/usr/share/{man,doc}
889a9042
RC
164 # we strip binaries since debug symbols increase binaries size a lot
165 # and it could fill the available space
166 strip_binaries
167}
168
169install_missing_libraries() {
170 # install possible missing libraries
171 for i in $initdir/{sbin,bin}/* $initdir/lib/systemd/*; do
172 inst_libs $i
173 done
174}
175
176create_empty_image() {
739d81dd 177 rm -f "$TESTDIR/rootdisk.img"
889a9042 178 # Create the blank file to use as a root filesystem
1b1eae69 179 dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=400
889a9042 180 LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
739d81dd 181 [ -b "$LOOPDEV" ] || return 1
889a9042 182 echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
edbced8a 183 sfdisk "$LOOPDEV" <<EOF
1b1eae69 184,390M
889a9042
RC
185,
186EOF
187
739d81dd 188 mkfs.ext3 -L systemd "${LOOPDEV}p1"
889a9042
RC
189}
190
191check_result_nspawn() {
192 ret=1
193 [[ -e $TESTDIR/nspawn-root/testok ]] && ret=0
194 [[ -f $TESTDIR/nspawn-root/failed ]] && cp -a $TESTDIR/nspawn-root/failed $TESTDIR
195 cp -a $TESTDIR/nspawn-root/var/log/journal $TESTDIR
196 [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
197 ls -l $TESTDIR/journal/*/*.journal
198 test -s $TESTDIR/failed && ret=$(($ret+1))
199 return $ret
200}
201
202strip_binaries() {
203 ddebug "Strip binaries"
204 find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | xargs strip --strip-unneeded | ddebug
205}
206
207create_rc_local() {
208 mkdir -p $initdir/etc/rc.d
209 cat >$initdir/etc/rc.d/rc.local <<EOF
210#!/bin/bash
211exit 0
212EOF
213 chmod 0755 $initdir/etc/rc.d/rc.local
214}
215
216install_execs() {
c7eda013
EV
217 ddebug "install any Execs from the service files"
218 (
219 export PKG_CONFIG_PATH=$TEST_BASE_DIR/../src/core/
220 systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
221 systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
222 egrep -ho '^Exec[^ ]*=[^ ]+' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
223 | while read i; do
224 i=${i##Exec*=}; i=${i##-}
225 inst $i
226 done
227 )
889a9042
RC
228}
229
230generate_module_dependencies() {
231 if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
232 ! depmod -a -b "$initdir" $KERNEL_VER; then
233 dfatal "\"depmod -a $KERNEL_VER\" failed."
234 exit 1
235 fi
236}
237
238install_depmod_files() {
239 inst /lib/modules/$KERNEL_VER/modules.order
240 inst /lib/modules/$KERNEL_VER/modules.builtin
241}
242
243install_plymouth() {
244 # install plymouth, if found... else remove plymouth service files
245 # if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
246 # PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
247 # /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
248 # dracut_install plymouth plymouthd
249 # else
250 rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
251 # fi
252}
253
254install_ld_so_conf() {
255 cp -a /etc/ld.so.conf* $initdir/etc
256 ldconfig -r "$initdir"
257}
258
259install_config_files() {
260 inst /etc/sysconfig/init
261 inst /etc/passwd
262 inst /etc/shadow
bf3a947c 263 inst /etc/login.defs
889a9042
RC
264 inst /etc/group
265 inst /etc/shells
266 inst /etc/nsswitch.conf
267 inst /etc/pam.conf
268 inst /etc/securetty
269 inst /etc/os-release
270 inst /etc/localtime
271 # we want an empty environment
272 > $initdir/etc/environment
273 > $initdir/etc/machine-id
274 # set the hostname
275 echo systemd-testsuite > $initdir/etc/hostname
276 # fstab
277 cat >$initdir/etc/fstab <<EOF
278LABEL=systemd / ext3 rw 0 1
279EOF
280}
281
282install_basic_tools() {
283 [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
4be4833e 284 dracut_install -o sushell
7d023341
MP
285 # in Debian ldconfig is just a shell script wrapper around ldconfig.real
286 dracut_install -o ldconfig.real
889a9042
RC
287}
288
289install_debug_tools() {
290 [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
291}
292
293install_libnss() {
294 # install libnss_files for login
cffae62b 295 NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
99877b7e 296 dracut_install $NSS_LIBS
889a9042
RC
297}
298
299install_dbus() {
3486cb6c
MP
300 inst $ROOTLIBDIR/system/dbus.socket
301 inst $ROOTLIBDIR/system/dbus.service
889a9042
RC
302
303 find \
e63b61be 304 /etc/dbus-1 /usr/share/dbus-1 -xtype f \
889a9042
RC
305 | while read file; do
306 inst $file
307 done
308}
309
310install_pam() {
0fe15dc8
EV
311 (
312 [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null && find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
889a9042
RC
313 find \
314 /etc/pam.d \
315 /etc/security \
316 /lib64/security \
317 /lib/security -xtype f \
0fe15dc8 318 ) | while read file; do
889a9042
RC
319 inst $file
320 done
417491f1
EV
321
322 [[ "$LOOKS_LIKE_DEBIAN" ]] &&
323 cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
889a9042
RC
324}
325
326install_keymaps() {
327 for i in \
328 /usr/lib/kbd/keymaps/include/* \
329 /usr/lib/kbd/keymaps/i386/include/* \
330 /usr/lib/kbd/keymaps/i386/qwerty/us.*; do
331 [[ -f $i ]] || continue
332 inst $i
333 done
334}
335
336install_fonts() {
337 for i in \
25b47f96 338 /usr/lib/kbd/consolefonts/eurlatgr* \
889a9042
RC
339 /usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
340 [[ -f $i ]] || continue
341 inst $i
342 done
343}
344
345install_terminfo() {
346 for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
347 [ -f ${_terminfodir}/l/linux ] && break
348 done
349 dracut_install -o ${_terminfodir}/l/linux
350}
351
352setup_testsuite() {
53d90f95 353 cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
5c404f1a 354 cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
889a9042
RC
355
356 mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
357 ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
358 ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
359
360 # make the testsuite the default target
361 ln -fs testsuite.target $initdir/etc/systemd/system/default.target
362}
363
364setup_nspawn_root() {
365 rm -fr $TESTDIR/nspawn-root
366 ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
367 cp -ar $initdir $TESTDIR/nspawn-root
368 # we don't mount in the nspawn root
369 rm -f $TESTDIR/nspawn-root/etc/fstab
370}
371
0d6e798a 372setup_basic_dirs() {
889a9042
RC
373 mkdir -p $initdir/run
374 mkdir -p $initdir/etc/systemd/system
375 mkdir -p $initdir/var/log/journal
376
0d6e798a 377 for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log dev proc sys sysroot root run run/lock run/initramfs; do
898720b7
HH
378 if [ -L "/$d" ]; then
379 inst_symlink "/$d"
380 else
0d6e798a 381 inst_dir "/$d"
898720b7
HH
382 fi
383 done
384
385 ln -sfn /run "$initdir/var/run"
386 ln -sfn /run/lock "$initdir/var/lock"
387}
388
389inst_libs() {
390 local _bin=$1
391 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
392 local _file _line
393
394 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
395 [[ $_line = 'not a dynamic executable' ]] && break
396
397 if [[ $_line =~ $_so_regex ]]; then
398 _file=${BASH_REMATCH[1]}
399 [[ -e ${initdir}/$_file ]] && continue
400 inst_library "$_file"
401 continue
402 fi
403
404 if [[ $_line =~ not\ found ]]; then
405 dfatal "Missing a shared library required by $_bin."
406 dfatal "Run \"ldd $_bin\" to find out what it is."
407 dfatal "$_line"
408 dfatal "dracut cannot create an initrd."
409 exit 1
410 fi
411 done
412}
413
414import_testdir() {
415 STATEFILE=".testdir"
416 [[ -e $STATEFILE ]] && . $STATEFILE
417 if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then
418 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
419 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
420 export TESTDIR
421 fi
422}
423
889a9042
RC
424import_initdir() {
425 initdir=$TESTDIR/root
426 export initdir
427}
428
898720b7
HH
429## @brief Converts numeric logging level to the first letter of level name.
430#
431# @param lvl Numeric logging level in range from 1 to 6.
432# @retval 1 if @a lvl is out of range.
433# @retval 0 if @a lvl is correct.
434# @result Echoes first letter of level name.
435_lvl2char() {
436 case "$1" in
437 1) echo F;;
438 2) echo E;;
439 3) echo W;;
440 4) echo I;;
441 5) echo D;;
442 6) echo T;;
443 *) return 1;;
444 esac
445}
446
447## @brief Internal helper function for _do_dlog()
448#
449# @param lvl Numeric logging level.
450# @param msg Message.
451# @retval 0 It's always returned, even if logging failed.
452#
453# @note This function is not supposed to be called manually. Please use
454# dtrace(), ddebug(), or others instead which wrap this one.
455#
456# This function calls _do_dlog() either with parameter msg, or if
457# none is given, it will read standard input and will use every line as
458# a message.
459#
460# This enables:
461# dwarn "This is a warning"
462# echo "This is a warning" | dwarn
463LOG_LEVEL=4
464
465dlog() {
466 [ -z "$LOG_LEVEL" ] && return 0
467 [ $1 -le $LOG_LEVEL ] || return 0
468 local lvl="$1"; shift
469 local lvlc=$(_lvl2char "$lvl") || return 0
470
471 if [ $# -ge 1 ]; then
472 echo "$lvlc: $*"
473 else
474 while read line; do
475 echo "$lvlc: " "$line"
476 done
477 fi
478}
479
480## @brief Logs message at TRACE level (6)
481#
482# @param msg Message.
483# @retval 0 It's always returned, even if logging failed.
484dtrace() {
485 set +x
486 dlog 6 "$@"
487 [ -n "$debug" ] && set -x || :
488}
489
490## @brief Logs message at DEBUG level (5)
491#
492# @param msg Message.
493# @retval 0 It's always returned, even if logging failed.
494ddebug() {
0d6e798a 495# set +x
898720b7 496 dlog 5 "$@"
0d6e798a 497# [ -n "$debug" ] && set -x || :
898720b7
HH
498}
499
500## @brief Logs message at INFO level (4)
501#
502# @param msg Message.
503# @retval 0 It's always returned, even if logging failed.
504dinfo() {
505 set +x
506 dlog 4 "$@"
507 [ -n "$debug" ] && set -x || :
508}
509
510## @brief Logs message at WARN level (3)
511#
512# @param msg Message.
513# @retval 0 It's always returned, even if logging failed.
514dwarn() {
515 set +x
516 dlog 3 "$@"
517 [ -n "$debug" ] && set -x || :
518}
519
520## @brief Logs message at ERROR level (2)
521#
522# @param msg Message.
523# @retval 0 It's always returned, even if logging failed.
524derror() {
0d6e798a 525# set +x
898720b7 526 dlog 2 "$@"
0d6e798a 527# [ -n "$debug" ] && set -x || :
898720b7
HH
528}
529
530## @brief Logs message at FATAL level (1)
531#
532# @param msg Message.
533# @retval 0 It's always returned, even if logging failed.
534dfatal() {
535 set +x
536 dlog 1 "$@"
537 [ -n "$debug" ] && set -x || :
538}
539
540
541# Generic substring function. If $2 is in $1, return 0.
542strstr() { [ "${1#*$2*}" != "$1" ]; }
543
544# normalize_path <path>
545# Prints the normalized path, where it removes any duplicated
546# and trailing slashes.
547# Example:
548# $ normalize_path ///test/test//
549# /test/test
550normalize_path() {
551 shopt -q -s extglob
552 set -- "${1//+(\/)//}"
553 shopt -q -u extglob
554 echo "${1%/}"
555}
556
557# convert_abs_rel <from> <to>
558# Prints the relative path, when creating a symlink to <to> from <from>.
559# Example:
560# $ convert_abs_rel /usr/bin/test /bin/test-2
561# ../../bin/test-2
562# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
563convert_abs_rel() {
564 local __current __absolute __abssize __cursize __newpath
565 local -i __i __level
566
567 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
568
569 # corner case #1 - self looping link
570 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
571
572 # corner case #2 - own dir link
573 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
574
575 IFS="/" __current=($1)
576 IFS="/" __absolute=($2)
577
578 __abssize=${#__absolute[@]}
579 __cursize=${#__current[@]}
580
581 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
582 do
583 (( __level++ ))
584 if (( __level > __abssize || __level > __cursize ))
585 then
586 break
587 fi
588 done
589
590 for ((__i = __level; __i < __cursize-1; __i++))
591 do
592 if ((__i > __level))
593 then
594 __newpath=$__newpath"/"
595 fi
596 __newpath=$__newpath".."
597 done
598
599 for ((__i = __level; __i < __abssize; __i++))
600 do
601 if [[ -n $__newpath ]]
602 then
603 __newpath=$__newpath"/"
604 fi
605 __newpath=$__newpath${__absolute[__i]}
606 done
607
608 echo "$__newpath"
609}
610
611
612# Install a directory, keeping symlinks as on the original system.
613# Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
614# will create ${initdir}/lib64, ${initdir}/lib64/file,
615# and a symlink ${initdir}/lib -> lib64.
616inst_dir() {
617 [[ -e ${initdir}/"$1" ]] && return 0 # already there
618
619 local _dir="$1" _part="${1%/*}" _file
620 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
621 _dir="$_part $_dir"
622 _part=${_part%/*}
623 done
624
625 # iterate over parent directories
626 for _file in $_dir; do
627 [[ -e "${initdir}/$_file" ]] && continue
628 if [[ -L $_file ]]; then
629 inst_symlink "$_file"
630 else
631 # create directory
632 mkdir -m 0755 -p "${initdir}/$_file" || return 1
633 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
634 chmod u+w "${initdir}/$_file"
635 fi
636 done
637}
638
639# $1 = file to copy to ramdisk
640# $2 (optional) Name for the file on the ramdisk
641# Location of the image dir is assumed to be $initdir
642# We never overwrite the target if it exists.
643inst_simple() {
644 [[ -f "$1" ]] || return 1
645 strstr "$1" "/" || return 1
646
647 local _src=$1 target="${2:-$1}"
648 if ! [[ -d ${initdir}/$target ]]; then
649 [[ -e ${initdir}/$target ]] && return 0
650 [[ -L ${initdir}/$target ]] && return 0
651 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
652 fi
653 # install checksum files also
654 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
655 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
656 fi
657 ddebug "Installing $_src"
658 cp --sparse=always -pfL "$_src" "${initdir}/$target"
659}
660
661# find symlinks linked to given library file
662# $1 = library file
663# Function searches for symlinks by stripping version numbers appended to
664# library filename, checks if it points to the same target and finally
665# prints the list of symlinks to stdout.
666#
667# Example:
668# rev_lib_symlinks libfoo.so.8.1
669# output: libfoo.so.8 libfoo.so
670# (Only if libfoo.so.8 and libfoo.so exists on host system.)
671rev_lib_symlinks() {
672 [[ ! $1 ]] && return 0
673
674 local fn="$1" orig="$(readlink -f "$1")" links=''
675
676 [[ ${fn} =~ .*\.so\..* ]] || return 1
677
678 until [[ ${fn##*.} == so ]]; do
679 fn="${fn%.*}"
680 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
681 done
682
683 echo "${links}"
684}
685
686# Same as above, but specialized to handle dynamic libraries.
687# It handles making symlinks according to how the original library
688# is referenced.
689inst_library() {
690 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
691 strstr "$1" "/" || return 1
692 [[ -e $initdir/$_dest ]] && return 0
693 if [[ -L $_src ]]; then
694 # install checksum files also
695 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
696 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
697 fi
698 _reallib=$(readlink -f "$_src")
699 inst_simple "$_reallib" "$_reallib"
700 inst_dir "${_dest%/*}"
701 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
702 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
703 else
704 inst_simple "$_src" "$_dest"
705 fi
706
707 # Create additional symlinks. See rev_symlinks description.
708 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
709 [[ ! -e $initdir/$_symlink ]] && {
710 ddebug "Creating extra symlink: $_symlink"
711 inst_symlink $_symlink
712 }
713 done
714}
715
716# find a binary. If we were not passed the full path directly,
717# search in the usual places to find the binary.
718find_binary() {
719 if [[ -z ${1##/*} ]]; then
720 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
721 echo $1
722 return 0
723 fi
724 fi
725
726 type -P $1
727}
728
729# Same as above, but specialized to install binary executables.
730# Install binary executable, and all shared library dependencies, if any.
731inst_binary() {
732 local _bin _target
733 _bin=$(find_binary "$1") || return 1
734 _target=${2:-$_bin}
735 [[ -e $initdir/$_target ]] && return 0
736 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
737 local _file _line
738 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
739 # I love bash!
740 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
741 [[ $_line = 'not a dynamic executable' ]] && break
742
743 if [[ $_line =~ $_so_regex ]]; then
744 _file=${BASH_REMATCH[1]}
745 [[ -e ${initdir}/$_file ]] && continue
746 inst_library "$_file"
747 continue
748 fi
749
750 if [[ $_line =~ not\ found ]]; then
751 dfatal "Missing a shared library required by $_bin."
752 dfatal "Run \"ldd $_bin\" to find out what it is."
753 dfatal "$_line"
754 dfatal "dracut cannot create an initrd."
755 exit 1
756 fi
757 done
758 inst_simple "$_bin" "$_target"
759}
760
761# same as above, except for shell scripts.
762# If your shell script does not start with shebang, it is not a shell script.
763inst_script() {
764 local _bin
765 _bin=$(find_binary "$1") || return 1
766 shift
767 local _line _shebang_regex
768 read -r -n 80 _line <"$_bin"
769 # If debug is set, clean unprintable chars to prevent messing up the term
770 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
771 _shebang_regex='(#! *)(/[^ ]+).*'
772 [[ $_line =~ $_shebang_regex ]] || return 1
773 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
774}
775
776# same as above, but specialized for symlinks
777inst_symlink() {
778 local _src=$1 _target=${2:-$1} _realsrc
779 strstr "$1" "/" || return 1
780 [[ -L $1 ]] || return 1
781 [[ -L $initdir/$_target ]] && return 0
782 _realsrc=$(readlink -f "$_src")
783 if ! [[ -e $initdir/$_realsrc ]]; then
784 if [[ -d $_realsrc ]]; then
785 inst_dir "$_realsrc"
786 else
787 inst "$_realsrc"
788 fi
789 fi
790 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
791 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
792 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
793}
794
795# attempt to install any programs specified in a udev rule
796inst_rule_programs() {
797 local _prog _bin
798
799 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
800 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
801 if [ -x /lib/udev/$_prog ]; then
802 _bin=/lib/udev/$_prog
803 else
804 _bin=$(find_binary "$_prog") || {
805 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
806 continue;
807 }
808 fi
809
810 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
811 dracut_install "$_bin"
812 done
813 fi
814}
815
816# udev rules always get installed in the same place, so
817# create a function to install them to make life simpler.
818inst_rules() {
819 local _target=/etc/udev/rules.d _rule _found
820
821 inst_dir "/lib/udev/rules.d"
822 inst_dir "$_target"
823 for _rule in "$@"; do
824 if [ "${rule#/}" = "$rule" ]; then
825 for r in /lib/udev/rules.d /etc/udev/rules.d; do
826 if [[ -f $r/$_rule ]]; then
827 _found="$r/$_rule"
828 inst_simple "$_found"
829 inst_rule_programs "$_found"
830 fi
831 done
832 fi
833 for r in '' ./ $dracutbasedir/rules.d/; do
834 if [[ -f ${r}$_rule ]]; then
835 _found="${r}$_rule"
836 inst_simple "$_found" "$_target/${_found##*/}"
837 inst_rule_programs "$_found"
838 fi
839 done
840 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
841 done
842}
843
844# general purpose installation function
845# Same args as above.
846inst() {
847 local _x
848
849 case $# in
850 1) ;;
851 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
852 [[ $initdir = $2 ]] && set $1;;
853 3) [[ -z $initdir ]] && export initdir=$2
854 set $1 $3;;
855 *) dfatal "inst only takes 1 or 2 or 3 arguments"
856 exit 1;;
857 esac
858 for _x in inst_symlink inst_script inst_binary inst_simple; do
859 $_x "$@" && return 0
860 done
861 return 1
862}
863
864# install any of listed files
865#
866# If first argument is '-d' and second some destination path, first accessible
867# source is installed into this path, otherwise it will installed in the same
868# path as source. If none of listed files was installed, function return 1.
869# On first successful installation it returns with 0 status.
870#
871# Example:
872#
873# inst_any -d /bin/foo /bin/bar /bin/baz
874#
875# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
876# initramfs.
877inst_any() {
878 local to f
879
880 [[ $1 = '-d' ]] && to="$2" && shift 2
881
882 for f in "$@"; do
883 if [[ -e $f ]]; then
884 [[ $to ]] && inst "$f" "$to" && return 0
885 inst "$f" && return 0
886 fi
887 done
888
889 return 1
890}
891
892# dracut_install [-o ] <file> [<file> ... ]
893# Install <file> to the initramfs image
894# -o optionally install the <file> and don't fail, if it is not there
895dracut_install() {
896 local _optional=no
897 if [[ $1 = '-o' ]]; then
898 _optional=yes
899 shift
900 fi
901 while (($# > 0)); do
902 if ! inst "$1" ; then
903 if [[ $_optional = yes ]]; then
904 dinfo "Skipping program $1 as it cannot be found and is" \
905 "flagged to be optional"
906 else
907 dfatal "Failed to install $1"
908 exit 1
909 fi
910 fi
911 shift
912 done
913}
914
0d6e798a
HH
915# Install a single kernel module along with any firmware it may require.
916# $1 = full path to kernel module to install
917install_kmod_with_fw() {
918 # no need to go further if the module is already installed
919
920 [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
921 && return 0
922
923 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
924
925 if [[ $omit_drivers ]]; then
926 local _kmod=${1##*/}
927 _kmod=${_kmod%.ko}
928 _kmod=${_kmod/-/_}
929 if [[ "$_kmod" =~ $omit_drivers ]]; then
930 dinfo "Omitting driver $_kmod"
931 return 1
932 fi
933 if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
934 dinfo "Omitting driver $_kmod"
935 return 1
936 fi
937 fi
938
939 [ -d "$initdir/.kernelmodseen" ] && \
940 > "$initdir/.kernelmodseen/${1##*/}"
941
942 inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
943 || return $?
944
945 local _modname=${1##*/} _fwdir _found _fw
946 _modname=${_modname%.ko*}
947 for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
948 _found=''
949 for _fwdir in $fw_dir; do
950 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
951 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
952 _found=yes
953 fi
954 done
955 if [[ $_found != yes ]]; then
956 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
957 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
958 "\"${_modname}.ko\""
959 else
960 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
961 "\"${_modname}.ko\""
962 fi
963 fi
964 done
965 return 0
966}
967
968# Do something with all the dependencies of a kernel module.
969# Note that kernel modules depend on themselves using the technique we use
970# $1 = function to call for each dependency we find
971# It will be passed the full path to the found kernel module
972# $2 = module to get dependencies for
973# rest of args = arguments to modprobe
974# _fderr specifies FD passed from surrounding scope
975for_each_kmod_dep() {
976 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
977 shift 2
978 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
979 while read _cmd _modpath _options; do
980 [[ $_cmd = insmod ]] || continue
981 $_func ${_modpath} || exit $?
982 _found=1
983 done
984 [[ $_found -eq 0 ]] && exit 1
985 exit 0
986 )
987}
988
989# filter kernel modules to install certain modules that meet specific
990# requirements.
991# $1 = search only in subdirectory of /kernel/$1
992# $2 = function to call with module name to filter.
993# This function will be passed the full path to the module to test.
c5315881 994# The behavior of this function can vary depending on whether $hostonly is set.
0d6e798a
HH
995# If it is, we will only look at modules that are already in memory.
996# If it is not, we will look at all kernel modules
997# This function returns the full filenames of modules that match $1
998filter_kernel_modules_by_path () (
999 local _modname _filtercmd
1000 if ! [[ $hostonly ]]; then
1001 _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
1002 _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1003 _filtercmd+=' -o -name "*.ko.xz"'
1004 _filtercmd+=' 2>/dev/null'
1005 else
1006 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1007 _filtercmd+='-k $KERNEL_VER 2>/dev/null'
1008 fi
1009 for _modname in $(eval $_filtercmd); do
1010 case $_modname in
1011 *.ko) "$2" "$_modname" && echo "$_modname";;
1012 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1013 $2 $initdir/$$.ko && echo "$_modname"
1014 rm -f $initdir/$$.ko
1015 ;;
1016 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1017 $2 $initdir/$$.ko && echo "$_modname"
1018 rm -f $initdir/$$.ko
1019 ;;
1020 esac
1021 done
1022)
1023find_kernel_modules_by_path () (
1024 if ! [[ $hostonly ]]; then
1025 find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
1026 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1027 else
1028 cut -d " " -f 1 </proc/modules \
1029 | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
1030 fi
1031)
1032
1033filter_kernel_modules () {
1034 filter_kernel_modules_by_path drivers "$1"
1035}
1036
1037find_kernel_modules () {
1038 find_kernel_modules_by_path drivers
1039}
1040
1041# instmods [-c] <kernel module> [<kernel module> ... ]
1042# instmods [-c] <kernel subsystem>
1043# install kernel modules along with all their dependencies.
1044# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1045instmods() {
1046 [[ $no_kernel = yes ]] && return
1047 # called [sub]functions inherit _fderr
1048 local _fderr=9
1049 local _check=no
1050 if [[ $1 = '-c' ]]; then
1051 _check=yes
1052 shift
1053 fi
1054
1055 function inst1mod() {
1056 local _ret=0 _mod="$1"
1057 case $_mod in
1058 =*)
1059 if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
1060 ( [[ "$_mpargs" ]] && echo $_mpargs
1061 cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
1062 | instmods
1063 else
1064 ( [[ "$_mpargs" ]] && echo $_mpargs
1065 find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \
1066 | instmods
1067 fi
1068 ;;
1069 --*) _mpargs+=" $_mod" ;;
1070 i2o_scsi) return ;; # Do not load this diagnostic-only module
1071 *)
1072 _mod=${_mod##*/}
1073 # if we are already installed, skip this module and go on
1074 # to the next one.
1075 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1076
1077 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1078 dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
1079 return
1080 fi
1081 # If we are building a host-specific initramfs and this
1082 # module is not already loaded, move on to the next one.
1083 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1084 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1085 && return
1086
1087 # We use '-d' option in modprobe only if modules prefix path
1088 # differs from default '/'. This allows us to use Dracut with
1089 # old version of modprobe which doesn't have '-d' option.
1090 local _moddirname=${KERNEL_MODS%%/lib/modules/*}
1091 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1092
1093 # ok, load the module, all its dependencies, and any firmware
1094 # it may require
1095 for_each_kmod_dep install_kmod_with_fw $_mod \
1096 --set-version $KERNEL_VER ${_moddirname} $_mpargs
1097 ((_ret+=$?))
1098 ;;
1099 esac
1100 return $_ret
1101 }
1102
1103 function instmods_1() {
1104 local _mod _mpargs
1105 if (($# == 0)); then # filenames from stdin
1106 while read _mod; do
1107 inst1mod "${_mod%.ko*}" || {
1108 if [ "$_check" = "yes" ]; then
1109 dfatal "Failed to install $_mod"
1110 return 1
1111 fi
1112 }
1113 done
1114 fi
1115 while (($# > 0)); do # filenames as arguments
1116 inst1mod ${1%.ko*} || {
1117 if [ "$_check" = "yes" ]; then
1118 dfatal "Failed to install $1"
1119 return 1
1120 fi
1121 }
1122 shift
1123 done
1124 return 0
1125 }
1126
1127 local _ret _filter_not_found='FATAL: Module .* not found.'
1128 set -o pipefail
1129 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1130 # redirections, but that would make dracut require bash4 at least.
1131 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1132 | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
1133 _ret=$?
1134 set +o pipefail
1135 return $_ret
1136}
898720b7
HH
1137
1138# inst_libdir_file [-n <pattern>] <file> [<file>...]
1139# Install a <file> located on a lib directory to the initramfs image
1140# -n <pattern> install non-matching files
1141inst_libdir_file() {
1142 if [[ "$1" == "-n" ]]; then
1143 local _pattern=$1
1144 shift 2
1145 for _dir in $libdirs; do
1146 for _i in "$@"; do
1147 for _f in "$_dir"/$_i; do
1148 [[ "$_i" =~ $_pattern ]] || continue
1149 [[ -e "$_i" ]] && dracut_install "$_i"
1150 done
1151 done
1152 done
1153 else
1154 for _dir in $libdirs; do
1155 for _i in "$@"; do
1156 for _f in "$_dir"/$_i; do
1157 [[ -e "$_f" ]] && dracut_install "$_f"
1158 done
1159 done
1160 done
1161 fi
1162}
1163
1ecf6a2b 1164check_nspawn() {
2c393ed7 1165 [[ -d /run/systemd/system ]]
1ecf6a2b
HH
1166}
1167
1168
898720b7 1169do_test() {
33a5e20f
HH
1170 if [[ $UID != "0" ]]; then
1171 echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2
1172 exit 0
1173 fi
1174
898720b7
HH
1175# Detect lib paths
1176 [[ $libdir ]] || for libdir in /lib64 /lib; do
1177 [[ -d $libdir ]] && libdirs+=" $libdir" && break
1178 done
1179
1180 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
1181 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
1182 done
1183
1184 import_testdir
889a9042 1185 import_initdir
898720b7
HH
1186
1187 while (($# > 0)); do
1188 case $1 in
1189 --run)
1190 echo "TEST RUN: $TEST_DESCRIPTION"
1191 test_run
1192 ret=$?
1193 if [ $ret -eq 0 ]; then
1194 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
1195 else
1196 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
1197 fi
1198 exit $ret;;
1199 --setup)
1200 echo "TEST SETUP: $TEST_DESCRIPTION"
1201 test_setup
1202 exit $?;;
1203 --clean)
1204 echo "TEST CLEANUP: $TEST_DESCRIPTION"
1205 test_cleanup
1206 rm -fr "$TESTDIR"
1207 rm -f .testdir
1208 exit $?;;
1209 --all)
1210 echo -n "TEST: $TEST_DESCRIPTION ";
1211 (
1212 test_setup && test_run
1213 ret=$?
1214 test_cleanup
1215 rm -fr "$TESTDIR"
1216 rm -f .testdir
1217 exit $ret
1218 ) </dev/null >test.log 2>&1
1219 ret=$?
1220 if [ $ret -eq 0 ]; then
1221 rm test.log
1222 echo "[OK]"
1223 else
1224 echo "[FAILED]"
1225 echo "see $(pwd)/test.log"
1226 fi
1227 exit $ret;;
1228 *) break ;;
1229 esac
1230 shift
1231 done
1232}