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