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
8 for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log; do
9 [[ -e "${initdir}${prefix}/$d" ]] && continue
11 inst_symlink "/$d" "${prefix}/$d"
13 mkdir -m 0755 -p "${initdir}${prefix}/$d"
17 for d in dev proc sys sysroot root run run/lock run/initramfs; do
21 mkdir -m 0755 -p "$initdir/$d"
25 ln -sfn /run "$initdir/var/run"
26 ln -sfn /run/lock "$initdir/var/lock"
31 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
34 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
35 [[ $_line = 'not a dynamic executable' ]] && break
37 if [[ $_line =~ $_so_regex ]]; then
38 _file=${BASH_REMATCH[1]}
39 [[ -e ${initdir}/$_file ]] && continue
44 if [[ $_line =~ not\ found ]]; then
45 dfatal "Missing a shared library required by $_bin."
46 dfatal "Run \"ldd $_bin\" to find out what it is."
48 dfatal "dracut cannot create an initrd."
56 [[ -e $STATEFILE ]] && . $STATEFILE
57 if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then
58 TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
59 echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
64 ## @brief Converts numeric logging level to the first letter of level name.
66 # @param lvl Numeric logging level in range from 1 to 6.
67 # @retval 1 if @a lvl is out of range.
68 # @retval 0 if @a lvl is correct.
69 # @result Echoes first letter of level name.
82 ## @brief Internal helper function for _do_dlog()
84 # @param lvl Numeric logging level.
86 # @retval 0 It's always returned, even if logging failed.
88 # @note This function is not supposed to be called manually. Please use
89 # dtrace(), ddebug(), or others instead which wrap this one.
91 # This function calls _do_dlog() either with parameter msg, or if
92 # none is given, it will read standard input and will use every line as
96 # dwarn "This is a warning"
97 # echo "This is a warning" | dwarn
101 [ -z "$LOG_LEVEL" ] && return 0
102 [ $1 -le $LOG_LEVEL ] || return 0
103 local lvl="$1"; shift
104 local lvlc=$(_lvl2char "$lvl") || return 0
106 if [ $# -ge 1 ]; then
110 echo "$lvlc: " "$line"
115 ## @brief Logs message at TRACE level (6)
117 # @param msg Message.
118 # @retval 0 It's always returned, even if logging failed.
122 [ -n "$debug" ] && set -x || :
125 ## @brief Logs message at DEBUG level (5)
127 # @param msg Message.
128 # @retval 0 It's always returned, even if logging failed.
132 [ -n "$debug" ] && set -x || :
135 ## @brief Logs message at INFO level (4)
137 # @param msg Message.
138 # @retval 0 It's always returned, even if logging failed.
142 [ -n "$debug" ] && set -x || :
145 ## @brief Logs message at WARN level (3)
147 # @param msg Message.
148 # @retval 0 It's always returned, even if logging failed.
152 [ -n "$debug" ] && set -x || :
155 ## @brief Logs message at ERROR level (2)
157 # @param msg Message.
158 # @retval 0 It's always returned, even if logging failed.
162 [ -n "$debug" ] && set -x || :
165 ## @brief Logs message at FATAL level (1)
167 # @param msg Message.
168 # @retval 0 It's always returned, even if logging failed.
172 [ -n "$debug" ] && set -x || :
176 # Generic substring function. If $2 is in $1, return 0.
177 strstr() { [ "${1#*$2*}" != "$1" ]; }
179 # normalize_path <path>
180 # Prints the normalized path, where it removes any duplicated
181 # and trailing slashes.
183 # $ normalize_path ///test/test//
187 set -- "${1//+(\/)//}"
192 # convert_abs_rel <from> <to>
193 # Prints the relative path, when creating a symlink to <to> from <from>.
195 # $ convert_abs_rel /usr/bin/test /bin/test-2
197 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
199 local __current __absolute __abssize __cursize __newpath
202 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
204 # corner case #1 - self looping link
205 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
207 # corner case #2 - own dir link
208 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
210 IFS="/" __current=($1)
211 IFS="/" __absolute=($2)
213 __abssize=${#__absolute[@]}
214 __cursize=${#__current[@]}
216 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
219 if (( __level > __abssize || __level > __cursize ))
225 for ((__i = __level; __i < __cursize-1; __i++))
229 __newpath=$__newpath"/"
231 __newpath=$__newpath".."
234 for ((__i = __level; __i < __abssize; __i++))
236 if [[ -n $__newpath ]]
238 __newpath=$__newpath"/"
240 __newpath=$__newpath${__absolute[__i]}
247 # Install a directory, keeping symlinks as on the original system.
248 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
249 # will create ${initdir}/lib64, ${initdir}/lib64/file,
250 # and a symlink ${initdir}/lib -> lib64.
252 [[ -e ${initdir}/"$1" ]] && return 0 # already there
254 local _dir="$1" _part="${1%/*}" _file
255 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
260 # iterate over parent directories
261 for _file in $_dir; do
262 [[ -e "${initdir}/$_file" ]] && continue
263 if [[ -L $_file ]]; then
264 inst_symlink "$_file"
267 mkdir -m 0755 -p "${initdir}/$_file" || return 1
268 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
269 chmod u+w "${initdir}/$_file"
274 # $1 = file to copy to ramdisk
275 # $2 (optional) Name for the file on the ramdisk
276 # Location of the image dir is assumed to be $initdir
277 # We never overwrite the target if it exists.
279 [[ -f "$1" ]] || return 1
280 strstr "$1" "/" || return 1
282 local _src=$1 target="${2:-$1}"
283 if ! [[ -d ${initdir}/$target ]]; then
284 [[ -e ${initdir}/$target ]] && return 0
285 [[ -L ${initdir}/$target ]] && return 0
286 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
288 # install checksum files also
289 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
290 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
292 ddebug "Installing $_src"
293 cp --sparse=always -pfL "$_src" "${initdir}/$target"
296 # find symlinks linked to given library file
298 # Function searches for symlinks by stripping version numbers appended to
299 # library filename, checks if it points to the same target and finally
300 # prints the list of symlinks to stdout.
303 # rev_lib_symlinks libfoo.so.8.1
304 # output: libfoo.so.8 libfoo.so
305 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
307 [[ ! $1 ]] && return 0
309 local fn="$1" orig="$(readlink -f "$1")" links=''
311 [[ ${fn} =~ .*\.so\..* ]] || return 1
313 until [[ ${fn##*.} == so ]]; do
315 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
321 # Same as above, but specialized to handle dynamic libraries.
322 # It handles making symlinks according to how the original library
325 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
326 strstr "$1" "/" || return 1
327 [[ -e $initdir/$_dest ]] && return 0
328 if [[ -L $_src ]]; then
329 # install checksum files also
330 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
331 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
333 _reallib=$(readlink -f "$_src")
334 inst_simple "$_reallib" "$_reallib"
335 inst_dir "${_dest%/*}"
336 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
337 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
339 inst_simple "$_src" "$_dest"
342 # Create additional symlinks. See rev_symlinks description.
343 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
344 [[ ! -e $initdir/$_symlink ]] && {
345 ddebug "Creating extra symlink: $_symlink"
346 inst_symlink $_symlink
351 # find a binary. If we were not passed the full path directly,
352 # search in the usual places to find the binary.
354 if [[ -z ${1##/*} ]]; then
355 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
364 # Same as above, but specialized to install binary executables.
365 # Install binary executable, and all shared library dependencies, if any.
368 _bin=$(find_binary "$1") || return 1
370 [[ -e $initdir/$_target ]] && return 0
371 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
373 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
375 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
376 [[ $_line = 'not a dynamic executable' ]] && break
378 if [[ $_line =~ $_so_regex ]]; then
379 _file=${BASH_REMATCH[1]}
380 [[ -e ${initdir}/$_file ]] && continue
381 inst_library "$_file"
385 if [[ $_line =~ not\ found ]]; then
386 dfatal "Missing a shared library required by $_bin."
387 dfatal "Run \"ldd $_bin\" to find out what it is."
389 dfatal "dracut cannot create an initrd."
393 inst_simple "$_bin" "$_target"
396 # same as above, except for shell scripts.
397 # If your shell script does not start with shebang, it is not a shell script.
400 _bin=$(find_binary "$1") || return 1
402 local _line _shebang_regex
403 read -r -n 80 _line <"$_bin"
404 # If debug is set, clean unprintable chars to prevent messing up the term
405 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
406 _shebang_regex='(#! *)(/[^ ]+).*'
407 [[ $_line =~ $_shebang_regex ]] || return 1
408 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
411 # same as above, but specialized for symlinks
413 local _src=$1 _target=${2:-$1} _realsrc
414 strstr "$1" "/" || return 1
415 [[ -L $1 ]] || return 1
416 [[ -L $initdir/$_target ]] && return 0
417 _realsrc=$(readlink -f "$_src")
418 if ! [[ -e $initdir/$_realsrc ]]; then
419 if [[ -d $_realsrc ]]; then
425 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
426 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
427 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
430 # attempt to install any programs specified in a udev rule
431 inst_rule_programs() {
434 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
435 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
436 if [ -x /lib/udev/$_prog ]; then
437 _bin=/lib/udev/$_prog
439 _bin=$(find_binary "$_prog") || {
440 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
445 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
446 dracut_install "$_bin"
451 # udev rules always get installed in the same place, so
452 # create a function to install them to make life simpler.
454 local _target=/etc/udev/rules.d _rule _found
456 inst_dir "/lib/udev/rules.d"
458 for _rule in "$@"; do
459 if [ "${rule#/}" = "$rule" ]; then
460 for r in /lib/udev/rules.d /etc/udev/rules.d; do
461 if [[ -f $r/$_rule ]]; then
463 inst_simple "$_found"
464 inst_rule_programs "$_found"
468 for r in '' ./ $dracutbasedir/rules.d/; do
469 if [[ -f ${r}$_rule ]]; then
471 inst_simple "$_found" "$_target/${_found##*/}"
472 inst_rule_programs "$_found"
475 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
479 # general purpose installation function
480 # Same args as above.
486 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
487 [[ $initdir = $2 ]] && set $1;;
488 3) [[ -z $initdir ]] && export initdir=$2
490 *) dfatal "inst only takes 1 or 2 or 3 arguments"
493 for _x in inst_symlink inst_script inst_binary inst_simple; do
499 # install any of listed files
501 # If first argument is '-d' and second some destination path, first accessible
502 # source is installed into this path, otherwise it will installed in the same
503 # path as source. If none of listed files was installed, function return 1.
504 # On first successful installation it returns with 0 status.
508 # inst_any -d /bin/foo /bin/bar /bin/baz
510 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
515 [[ $1 = '-d' ]] && to="$2" && shift 2
519 [[ $to ]] && inst "$f" "$to" && return 0
520 inst "$f" && return 0
527 # dracut_install [-o ] <file> [<file> ... ]
528 # Install <file> to the initramfs image
529 # -o optionally install the <file> and don't fail, if it is not there
532 if [[ $1 = '-o' ]]; then
537 if ! inst "$1" ; then
538 if [[ $_optional = yes ]]; then
539 dinfo "Skipping program $1 as it cannot be found and is" \
540 "flagged to be optional"
542 dfatal "Failed to install $1"
551 # inst_libdir_file [-n <pattern>] <file> [<file>...]
552 # Install a <file> located on a lib directory to the initramfs image
553 # -n <pattern> install non-matching files
555 if [[ "$1" == "-n" ]]; then
558 for _dir in $libdirs; do
560 for _f in "$_dir"/$_i; do
561 [[ "$_i" =~ $_pattern ]] || continue
562 [[ -e "$_i" ]] && dracut_install "$_i"
567 for _dir in $libdirs; do
569 for _f in "$_dir"/$_i; do
570 [[ -e "$_f" ]] && dracut_install "$_f"
578 [[ $UID != "0" ]] && exit 0
579 command -v qemu-kvm &>/dev/null || exit 0
581 [[ $libdir ]] || for libdir in /lib64 /lib; do
582 [[ -d $libdir ]] && libdirs+=" $libdir" && break
585 [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
586 [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
594 echo "TEST RUN: $TEST_DESCRIPTION"
597 if [ $ret -eq 0 ]; then
598 echo "TEST RUN: $TEST_DESCRIPTION [OK]"
600 echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
604 echo "TEST SETUP: $TEST_DESCRIPTION"
608 echo "TEST CLEANUP: $TEST_DESCRIPTION"
614 echo -n "TEST: $TEST_DESCRIPTION ";
616 test_setup && test_run
622 ) </dev/null >test.log 2>&1
624 if [ $ret -eq 0 ]; then
629 echo "see $(pwd)/test.log"