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