summaryrefslogtreecommitdiffstats
path: root/modules.d/99base/dracut-lib.sh
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:54:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:54:25 +0000
commit9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a (patch)
tree2efb72864cc69e174c9c5ee33efb88a5f1553b48 /modules.d/99base/dracut-lib.sh
parentInitial commit. (diff)
downloaddracut-9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a.tar.xz
dracut-9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a.zip
Adding upstream version 060+5.upstream/060+5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules.d/99base/dracut-lib.sh')
-rwxr-xr-xmodules.d/99base/dracut-lib.sh1178
1 files changed, 1178 insertions, 0 deletions
diff --git a/modules.d/99base/dracut-lib.sh b/modules.d/99base/dracut-lib.sh
new file mode 100755
index 0000000..39609d8
--- /dev/null
+++ b/modules.d/99base/dracut-lib.sh
@@ -0,0 +1,1178 @@
+#!/bin/sh
+
+type wait_for_dev > /dev/null 2>&1 || . /lib/dracut-dev-lib.sh
+
+export DRACUT_SYSTEMD
+export NEWROOT
+if [ -n "$NEWROOT" ]; then
+ [ -d "$NEWROOT" ] || mkdir -p -m 0755 "$NEWROOT"
+fi
+
+# shellcheck disable=SC2153
+if [ -z "$PREFIX" ]; then
+ if ! [ -d /run/initramfs ]; then
+ mkdir -p -m 0755 /run/initramfs/log
+ ln -sfn /run/initramfs/log /var/log
+ fi
+
+ [ -d /run/lock ] || mkdir -p -m 0755 /run/lock
+ [ -d /run/log ] || mkdir -p -m 0755 /run/log
+fi
+
+debug_off() {
+ set +x
+}
+
+debug_on() {
+ [ "$RD_DEBUG" = "yes" ] && set -x
+}
+
+# returns OK if $1 contains literal string $2 (and isn't empty)
+strstr() {
+ [ "${1##*"$2"*}" != "$1" ]
+}
+
+# returns OK if $1 matches (completely) glob pattern $2
+# An empty $1 will not be considered matched, even if $2 is * which technically
+# matches; as it would match anything, it's not an interesting case.
+strglob() {
+ # shellcheck disable=SC2295
+ [ -n "$1" -a -z "${1##$2}" ]
+}
+
+# returns OK if $1 contains (anywhere) a match of glob pattern $2
+# An empty $1 will not be considered matched, even if $2 is * which technically
+# matches; as it would match anything, it's not an interesting case.
+strglobin() {
+ # shellcheck disable=SC2295
+ [ -n "$1" -a -z "${1##*$2*}" ]
+}
+
+# returns OK if $1 contains literal string $2 at the beginning, and isn't empty
+str_starts() {
+ [ "${1#"$2"*}" != "$1" ]
+}
+
+# returns OK if $1 contains literal string $2 at the end, and isn't empty
+str_ends() {
+ [ "${1%*"$2"}" != "$1" ]
+}
+
+trim() {
+ local var="$*"
+ var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
+ var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
+ printf "%s" "$var"
+}
+
+if [ -z "$DRACUT_SYSTEMD" ]; then
+
+ warn() {
+ check_quiet
+ echo "<28>dracut Warning: $*" > /dev/kmsg
+ echo "dracut Warning: $*" >&2
+ }
+
+ info() {
+ check_quiet
+ echo "<30>dracut: $*" > /dev/kmsg
+ if [ "$DRACUT_QUIET" != "yes" ]; then
+ echo "dracut: $*" >&2
+ fi
+ }
+
+else
+
+ warn() {
+ echo "Warning: $*" >&2
+ }
+
+ info() {
+ echo "$*"
+ }
+
+fi
+
+vwarn() {
+ while read -r line || [ -n "$line" ]; do
+ warn "$line"
+ done
+}
+
+vinfo() {
+ while read -r line || [ -n "$line" ]; do
+ info "$line"
+ done
+}
+
+killall_proc_mountpoint() {
+ local _pid
+ local _killed=0
+ for _pid in /proc/*; do
+ _pid=${_pid##/proc/}
+ case $_pid in
+ *[!0-9]*) continue ;;
+ esac
+ [ -e "/proc/$_pid/exe" ] || continue
+ [ -e "/proc/$_pid/root" ] || continue
+ if strstr "$(ls -l -- "/proc/$_pid" "/proc/$_pid/fd" 2> /dev/null)" "$1"; then
+ kill -9 "$_pid"
+ _killed=1
+ fi
+ done
+ return $_killed
+}
+
+getcmdline() {
+ local _line
+ local _i
+ local CMDLINE_ETC_D
+ local CMDLINE_ETC
+ local CMDLINE_PROC
+ unset _line
+
+ if [ -e /etc/cmdline ]; then
+ while read -r _line || [ -n "$_line" ]; do
+ CMDLINE_ETC="$CMDLINE_ETC $_line"
+ done < /etc/cmdline
+ fi
+ for _i in /etc/cmdline.d/*.conf; do
+ [ -e "$_i" ] || continue
+ while read -r _line || [ -n "$_line" ]; do
+ CMDLINE_ETC_D="$CMDLINE_ETC_D $_line"
+ done < "$_i"
+ done
+ if [ -e /proc/cmdline ]; then
+ while read -r _line || [ -n "$_line" ]; do
+ CMDLINE_PROC="$CMDLINE_PROC $_line"
+ done < /proc/cmdline
+ fi
+ CMDLINE="$CMDLINE_ETC_D $CMDLINE_ETC $CMDLINE_PROC"
+ printf "%s" "$CMDLINE"
+}
+
+getarg() {
+ debug_off
+ local _deprecated _newoption
+ CMDLINE=$(getcmdline)
+ export CMDLINE
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -d)
+ _deprecated=1
+ shift
+ ;;
+ -y)
+ if dracut-getarg "$2" > /dev/null; then
+ if [ "$_deprecated" = "1" ]; then
+ if [ -n "$_newoption" ]; then
+ warn "Kernel command line option '$2' is deprecated, use '$_newoption' instead."
+ else
+ warn "Option '$2' is deprecated."
+ fi
+ fi
+ echo 1
+ debug_on
+ return 0
+ fi
+ _deprecated=0
+ shift 2
+ ;;
+ -n)
+ if dracut-getarg "$2" > /dev/null; then
+ echo 0
+ if [ "$_deprecated" = "1" ]; then
+ if [ -n "$_newoption" ]; then
+ warn "Kernel command line option '$2' is deprecated, use '$_newoption=0' instead."
+ else
+ warn "Option '$2' is deprecated."
+ fi
+ fi
+ debug_on
+ return 1
+ fi
+ _deprecated=0
+ shift 2
+ ;;
+ *)
+ if [ -z "$_newoption" ]; then
+ _newoption="$1"
+ fi
+ if dracut-getarg "$1"; then
+ if [ "$_deprecated" = "1" ]; then
+ if [ -n "$_newoption" ]; then
+ warn "Kernel command line option '$1' is deprecated, use '$_newoption' instead."
+ else
+ warn "Option '$1' is deprecated."
+ fi
+ fi
+ debug_on
+ return 0
+ fi
+ _deprecated=0
+ shift
+ ;;
+ esac
+ done
+ debug_on
+ return 1
+}
+
+# getargbool <defaultval> <args...>
+# False if "getarg <args...>" returns "0", "no", or "off".
+# True if getarg returns any other non-empty string.
+# If not found, assumes <defaultval> - usually 0 for false, 1 for true.
+# example: getargbool 0 rd.info
+# true: rd.info, rd.info=1, rd.info=xxx
+# false: rd.info=0, rd.info=off, rd.info not present (default val is 0)
+getargbool() {
+ local _b
+ unset _b
+ local _default
+ _default="$1"
+ shift
+ _b=$(getarg "$@") || _b=${_b:-"$_default"}
+ if [ -n "$_b" ]; then
+ [ "$_b" = "0" ] && return 1
+ [ "$_b" = "no" ] && return 1
+ [ "$_b" = "off" ] && return 1
+ fi
+ return 0
+}
+
+isdigit() {
+ case "$1" in
+ *[!0-9]* | "") return 1 ;;
+ esac
+
+ return 0
+}
+
+# getargnum <defaultval> <minval> <maxval> <arg>
+# Will echo the arg if it's in range [minval - maxval].
+# If it's not set or it's not valid, will set it <defaultval>.
+# Note all values are required to be >= 0 here.
+# <defaultval> should be with [minval -maxval].
+getargnum() {
+ local _b
+ unset _b
+ local _default _min _max
+ _default="$1"
+ shift
+ _min="$1"
+ shift
+ _max="$1"
+ shift
+ _b=$(getarg "$1") || _b=${_b:-"$_default"}
+ if [ -n "$_b" ]; then
+ isdigit "$_b" && _b=$((_b)) \
+ && [ $_b -ge "$_min" ] && [ $_b -le "$_max" ] && echo $_b && return
+ fi
+ echo "$_default"
+}
+
+getargs() {
+ debug_off
+ CMDLINE=$(getcmdline)
+ export CMDLINE
+ local _val _i _gfound _deprecated
+ unset _val
+ unset _gfound
+ _newoption="$1"
+ for _i in "$@"; do
+ if [ "$_i" = "-d" ]; then
+ _deprecated=1
+ continue
+ fi
+
+ if _val="$(dracut-getargs "$_i")"; then
+ if [ "$_deprecated" = "1" ]; then
+ if [ -n "$_newoption" ]; then
+ warn "Option '$_i' is deprecated, use '$_newoption' instead."
+ else
+ warn "Option $_i is deprecated!"
+ fi
+ fi
+ if [ -n "$_val" ]; then
+ printf '%s\n' "$_val"
+ fi
+ _gfound=1
+ fi
+ _deprecated=0
+ done
+ if [ -n "$_gfound" ]; then
+ debug_on
+ return 0
+ fi
+ debug_on
+ return 1
+}
+
+# Prints value of given option. If option is a flag and it's present,
+# it just returns 0. Otherwise 1 is returned.
+# $1 = options separated by commas
+# $2 = option we are interested in
+#
+# Example:
+# $1 = cipher=aes-cbc-essiv:sha256,hash=sha256,verify
+# $2 = hash
+# Output:
+# sha256
+getoptcomma() {
+ local line=",$1,"
+ local opt="$2"
+ local tmp
+
+ case "${line}" in
+ *,${opt}=*,*)
+ tmp="${line#*,"${opt}"=}"
+ echo "${tmp%%,*}"
+ return 0
+ ;;
+ *,${opt},*) return 0 ;;
+ esac
+ return 1
+}
+
+# Splits given string 'str' with separator 'sep' into variables 'var1', 'var2',
+# 'varN'. If number of fields is less than number of variables, remaining are
+# not set. If number of fields is greater than number of variables, the last
+# variable takes remaining fields. In short - it acts similarly to 'read'.
+#
+# splitsep sep str var1 var2 varN
+#
+# example:
+# splitsep ':' 'foo:bar:baz' v1 v2
+# in result:
+# v1='foo', v2='bar:baz'
+#
+# TODO: ':' inside fields.
+splitsep() {
+ debug_off
+ local sep="$1"
+ local str="$2"
+ shift 2
+ local tmp
+
+ while [ -n "$str" -a "$#" -gt 1 ]; do
+ tmp="${str%%"$sep"*}"
+ eval "$1='${tmp}'"
+ str="${str#"$tmp"}"
+ str="${str#"$sep"}"
+ shift
+ done
+ [ -n "$str" -a -n "$1" ] && eval "$1='$str'"
+ debug_on
+ return 0
+}
+
+setdebug() {
+ [ -f /usr/lib/initrd-release ] || return
+ if [ -z "$RD_DEBUG" ]; then
+ if [ -e /proc/cmdline ]; then
+ RD_DEBUG=no
+ if getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then
+ RD_DEBUG=yes
+ [ -n "$BASH" ] \
+ && export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]-}): '
+ fi
+ fi
+ export RD_DEBUG
+ fi
+ debug_on
+}
+
+setdebug
+
+source_all() {
+ local f
+ local _dir
+ _dir=$1
+ shift
+ [ "$_dir" ] && [ -d "/$_dir" ] || return
+ for f in "/$_dir"/*.sh; do
+ if [ -e "$f" ]; then
+ # shellcheck disable=SC1090
+ # shellcheck disable=SC2240
+ . "$f" "$@"
+ fi
+ done
+}
+
+hookdir=/lib/dracut/hooks
+export hookdir
+
+source_hook() {
+ local _dir
+ _dir=$1
+ shift
+ source_all "/lib/dracut/hooks/$_dir" "$@"
+}
+
+check_finished() {
+ local f rc=0
+ for f in "$hookdir"/initqueue/finished/*.sh; do
+ [ "$f" = "$hookdir/initqueue/finished/*.sh" ] && return 0
+ # shellcheck disable=SC1090
+ if [ -e "$f" ] && (. "$f"); then
+ rm -f "$f"
+ else
+ rc=1
+ fi
+ done
+ return $rc
+}
+
+source_conf() {
+ local f
+ [ "$1" ] && [ -d "/$1" ] || return
+ # shellcheck disable=SC1090
+ for f in "/$1"/*.conf; do [ -e "$f" ] && . "$f"; done
+}
+
+die() {
+ {
+ echo "<24>dracut: FATAL: $*"
+ echo "<24>dracut: Refusing to continue"
+ } > /dev/kmsg
+
+ {
+ echo "warn dracut: FATAL: \"$*\""
+ echo "warn dracut: Refusing to continue"
+ } >> $hookdir/emergency/01-die.sh
+ [ -d /run/initramfs ] || mkdir -p -- /run/initramfs
+
+ : > /run/initramfs/.die
+
+ if getargbool 0 "rd.shell"; then
+ emergency_shell
+ else
+ source_hook "shutdown-emergency"
+ fi
+
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl --no-block --force halt
+ fi
+
+ exit 1
+}
+
+check_quiet() {
+ if [ -z "$DRACUT_QUIET" ]; then
+ DRACUT_QUIET="yes"
+ getargbool 0 rd.info -d -y rdinfo && DRACUT_QUIET="no"
+ getargbool 0 rd.debug -d -y rdinitdebug && DRACUT_QUIET="no"
+ getarg quiet || DRACUT_QUIET="yes"
+ a=$(getarg loglevel=)
+ [ -n "$a" ] && [ "$a" -ge 28 ] && DRACUT_QUIET="yes"
+ export DRACUT_QUIET
+ fi
+}
+
+check_occurances() {
+ # Count the number of times the character $ch occurs in $str
+ # Return 0 if the count matches the expected number, 1 otherwise
+ local str="$1"
+ local ch="$2"
+ local expected="$3"
+ local count=0
+
+ while [ "${str#*"$ch"}" != "${str}" ]; do
+ str="${str#*"$ch"}"
+ count=$((count + 1))
+ done
+
+ [ $count -eq "$expected" ]
+}
+
+incol2() {
+ debug_off
+ local check
+ local file="$1"
+ local str="$2"
+
+ [ -z "$file" ] && return 1
+ [ -z "$str" ] && return 1
+
+ while read -r _ check _ || [ -n "$check" ]; do
+ if [ "$check" = "$str" ]; then
+ debug_on
+ return 0
+ fi
+ done < "$file"
+ debug_on
+ return 1
+}
+
+udevsettle() {
+ # shellcheck disable=SC2086
+ udevadm settle --exit-if-exists=$hookdir/initqueue/work $settle_exit_if_exists
+}
+
+udevproperty() {
+ for i in "$@"; do
+ udevadm control --property="$i"
+ done
+}
+
+find_mount() {
+ local dev wanted_dev
+ wanted_dev="$(readlink -e -q "$1")"
+ while read -r dev _ || [ -n "$dev" ]; do
+ [ "$dev" = "$wanted_dev" ] && echo "$dev" && return 0
+ done < /proc/mounts
+ return 1
+}
+
+# usage: ismounted <mountpoint>
+# usage: ismounted /dev/<device>
+if command -v findmnt > /dev/null; then
+ ismounted() {
+ findmnt "$1" > /dev/null 2>&1
+ }
+else
+ ismounted() {
+ if [ -b "$1" ]; then
+ find_mount "$1" > /dev/null && return 0
+ return 1
+ fi
+
+ while read -r _ m _ || [ -n "$m" ]; do
+ [ "$m" = "$1" ] && return 0
+ done < /proc/mounts
+ return 1
+ }
+fi
+
+# Create udev rule match for a device with its device name, or the udev property
+# ID_FS_UUID or ID_FS_LABEL
+#
+# example:
+# udevmatch LABEL=boot
+# prints:
+# ENV{ID_FS_LABEL}="boot"
+#
+# TODO: symlinks
+udevmatch() {
+ case "$1" in
+ UUID=????????-????-????-????-???????????? | LABEL=* | PARTLABEL=* | PARTUUID=????????-????-????-????-????????????)
+ printf 'ENV{ID_FS_%s}=="%s"' "${1%%=*}" "${1#*=}"
+ ;;
+ UUID=*)
+ printf 'ENV{ID_FS_UUID}=="%s*"' "${1#*=}"
+ ;;
+ PARTUUID=*)
+ printf 'ENV{ID_FS_PARTUUID}=="%s*"' "${1#*=}"
+ ;;
+ /dev/?*) printf -- 'KERNEL=="%s"' "${1#/dev/}" ;;
+ *) return 255 ;;
+ esac
+}
+
+label_uuid_to_dev() {
+ local _dev
+ _dev="${1#block:}"
+ case "$_dev" in
+ LABEL=*)
+ echo "/dev/disk/by-label/$(echo "${_dev#LABEL=}" | sed 's,/,\\x2f,g;s, ,\\x20,g')"
+ ;;
+ PARTLABEL=*)
+ echo "/dev/disk/by-partlabel/$(echo "${_dev#PARTLABEL=}" | sed 's,/,\\x2f,g;s, ,\\x20,g')"
+ ;;
+ UUID=*)
+ echo "/dev/disk/by-uuid/${_dev#UUID=}"
+ ;;
+ PARTUUID=*)
+ echo "/dev/disk/by-partuuid/${_dev#PARTUUID=}"
+ ;;
+ *)
+ echo "$_dev"
+ ;;
+ esac
+}
+
+# Prints unique path for potential file inside specified directory. It consists
+# of specified directory, prefix and number at the end which is incremented
+# until non-existing file is found.
+#
+# funiq dir prefix
+#
+# example:
+# # ls /mnt
+# cdrom0 cdrom1
+#
+# # funiq /mnt cdrom
+# /mnt/cdrom2
+funiq() {
+ local dir="$1"
+ local prefix="$2"
+ local i=0
+
+ [ -d "${dir}" ] || return 1
+
+ while [ -e "${dir}/${prefix}$i" ]; do
+ i=$((i + 1)) || return 1
+ done
+
+ echo "${dir}/${prefix}$i"
+}
+
+# Creates unique directory and prints its path. It's using funiq to generate
+# path.
+#
+# mkuniqdir subdir new_dir_name
+mkuniqdir() {
+ local dir="$1"
+ local prefix="$2"
+ local retdir
+ local retdir_new
+
+ [ -d "${dir}" ] || mkdir -m 0755 -p "${dir}" || return 1
+
+ retdir=$(funiq "${dir}" "${prefix}") || return 1
+ until mkdir -m 0755 "${retdir}" 2> /dev/null; do
+ retdir_new=$(funiq "${dir}" "${prefix}") || return 1
+ [ "$retdir_new" = "$retdir" ] && return 1
+ retdir="$retdir_new"
+ done
+
+ echo "${retdir}"
+}
+
+# Copy the contents of SRC into DEST, merging the contents of existing
+# directories (kinda like rsync, or cpio -p).
+# Creates DEST if it doesn't exist. Overwrites files with the same names.
+#
+# copytree SRC DEST
+copytree() {
+ local src="$1" dest="$2"
+ [ -d "$src" ] || return 1
+ mkdir -p "$dest" || return 1
+ dest=$(readlink -e -q "$dest") || return 1
+ (
+ cd "$src" || exit 1
+ cp -af . -t "$dest"
+ )
+}
+
+# Evaluates command for UUIDs either given as arguments for this function or all
+# listed in /dev/disk/by-uuid. UUIDs doesn't have to be fully specified. If
+# beginning is given it is expanded to all matching UUIDs. To pass full UUID to
+# your command use '$___' as a place holder. Remember to escape '$'!
+#
+# foreach_uuid_until [ -p prefix ] command UUIDs
+#
+# prefix - string to put just before $___
+# command - command to be evaluated
+# UUIDs - list of UUIDs separated by space
+#
+# The function returns after *first successful evaluation* of the given command
+# with status 0. If evaluation fails for every UUID function returns with
+# status 1.
+#
+# Example:
+# foreach_uuid_until "mount -U \$___ /mnt; echo OK; umount /mnt" \
+# "01234 f512 a235567f-12a3-c123-a1b1-01234567abcb"
+foreach_uuid_until() (
+ cd /dev/disk/by-uuid || return 1
+
+ [ "$1" = -p ] && local prefix="$2" && shift 2
+ local cmd="$1"
+ shift
+ local uuids_list="$*"
+ local uuid
+ local full_uuid
+ local ___
+
+ [ -n "${cmd}" ] || return 1
+
+ for uuid in ${uuids_list:-*}; do
+ for full_uuid in "${uuid}"*; do
+ [ -e "${full_uuid}" ] || continue
+ # shellcheck disable=SC2034
+ ___="${prefix}${full_uuid}"
+ eval "${cmd}" && return 0
+ done
+ done
+
+ return 1
+)
+
+# Get kernel name for given device. Device may be the name too (then the same
+# is returned), a symlink (full path), UUID (prefixed with "UUID=") or label
+# (prefixed with "LABEL="). If just a beginning of the UUID is specified or
+# even an empty, function prints all device names which UUIDs match - every in
+# single line.
+#
+# NOTICE: The name starts with "/dev/".
+#
+# Example:
+# devnames UUID=123
+# May print:
+# /dev/dm-1
+# /dev/sdb1
+# /dev/sdf3
+devnames() {
+ local dev="$1"
+ local d
+ local names
+
+ case "$dev" in
+ UUID=*)
+ # shellcheck disable=SC2016
+ dev="$(foreach_uuid_until '! blkid -U $___' "${dev#UUID=}")" \
+ && return 255
+ [ -z "$dev" ] && return 255
+ ;;
+ LABEL=*) dev="$(blkid -L "${dev#LABEL=}")" || return 255 ;;
+ /dev/?*) ;;
+ *) return 255 ;;
+ esac
+
+ for d in $dev; do
+ names="$names
+$(readlink -e -q "$d")" || return 255
+ done
+
+ echo "${names#
+}"
+}
+
+usable_root() {
+ local _i
+
+ [ -d "$1" ] || return 1
+
+ for _i in "$1"/usr/lib*/ld-*.so "$1"/lib*/ld-*.so; do
+ [ -e "$_i" ] && return 0
+ done
+
+ for _i in proc sys dev; do
+ [ -e "$1"/$_i ] || return 1
+ done
+
+ return 0
+}
+
+inst_hook() {
+ local _hookname _unique _name _job _exe
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ --hook)
+ _hookname="/$2"
+ shift
+ ;;
+ --unique)
+ _unique="yes"
+ ;;
+ --name)
+ _name="$2"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+
+ if [ -z "$_unique" ]; then
+ _job="${_name}$$"
+ else
+ _job="${_name:-$1}"
+ _job=${_job##*/}
+ fi
+
+ _exe=$1
+ shift
+
+ [ -x "$_exe" ] || _exe=$(command -v "$_exe")
+
+ if [ -n "$onetime" ]; then
+ {
+ # shellcheck disable=SC2016
+ echo '[ -e "$_job" ] && rm -f -- "$_job"'
+ echo "$_exe $*"
+ } > "/tmp/$$-${_job}.sh"
+ else
+ echo "$_exe $*" > "/tmp/$$-${_job}.sh"
+ fi
+
+ mv -f "/tmp/$$-${_job}.sh" "$hookdir/${_hookname}/${_job}.sh"
+}
+
+# inst_mount_hook <mountpoint> <prio> <name> <script>
+#
+# Install a mount hook with priority <prio>,
+# which executes <script> as soon as <mountpoint> is mounted.
+inst_mount_hook() {
+ local _prio="$2" _jobname="$3" _script="$4"
+ local _hookname
+ _hookname="mount-$(str_replace "$1" '/' '\\x2f')"
+ [ -d "$hookdir/${_hookname}" ] || mkdir -p "$hookdir/${_hookname}"
+ inst_hook --hook "$_hookname" --unique --name "${_prio}-${_jobname}" "$_script"
+}
+
+# wait_for_mount <mountpoint>
+#
+# Installs a initqueue-finished script,
+# which will cause the main loop only to exit,
+# if <mountpoint> is mounted.
+wait_for_mount() {
+ local _name
+ _name="$(str_replace "$1" '/' '\\x2f')"
+ printf '. /lib/dracut-lib.sh\nismounted "%s"\n' "$1" \
+ >> "$hookdir/initqueue/finished/ismounted-${_name}.sh"
+ {
+ printf 'ismounted "%s" || ' "$1"
+ printf 'warn "\"%s\" is not mounted"\n' "$1"
+ } >> "$hookdir/emergency/90-${_name}.sh"
+}
+
+killproc() {
+ debug_off
+ local _exe
+ _exe="$(command -v "$1")"
+ local _sig="$2"
+ local _i
+ [ -x "$_exe" ] || return 1
+ for _i in /proc/[0-9]*; do
+ [ "$_i" = "/proc/1" ] && continue
+ if [ -e "$_i"/_exe ] && [ "$_i/_exe" -ef "$_exe" ]; then
+ kill "$_sig" "${_i##*/}"
+ fi
+ done
+ debug_on
+}
+
+need_shutdown() {
+ : > /run/initramfs/.need_shutdown
+}
+
+wait_for_loginit() {
+ [ "$RD_DEBUG" = "yes" ] || return
+ [ -e /run/initramfs/loginit.pipe ] || return
+ debug_off
+ echo "DRACUT_LOG_END"
+ exec 0<> /dev/console 1<> /dev/console 2<> /dev/console
+ # wait for loginit
+ i=0
+ while [ $i -lt 10 ]; do
+ if [ ! -e /run/initramfs/loginit.pipe ]; then
+ j=$(jobs)
+ [ -z "$j" ] && break
+ [ -z "${j##*Running*}" ] || break
+ fi
+ sleep 0.1
+ i=$((i + 1))
+ done
+
+ if [ $i -eq 10 ]; then
+ kill %1 > /dev/null 2>&1
+ kill "$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /run/initramfs/loginit.pid)"
+ fi
+
+ setdebug
+ rm -f -- /run/initramfs/loginit.pipe /run/initramfs/loginit.pid
+}
+
+# pidof version for root
+if ! command -v pidof > /dev/null 2> /dev/null; then
+ pidof() {
+ debug_off
+ local _cmd
+ local _exe
+ local _rl
+ local _ret=1
+ local i
+ _cmd="$1"
+ if [ -z "$_cmd" ]; then
+ debug_on
+ return 1
+ fi
+ _exe=$(command -v "$1")
+ for i in /proc/*/exe; do
+ [ -e "$i" ] || continue
+ if [ -n "$_exe" ]; then
+ [ "$i" -ef "$_exe" ] || continue
+ else
+ _rl=$(readlink -f "$i")
+ [ "${_rl%/"$_cmd"}" != "$_rl" ] || continue
+ fi
+ i=${i%/exe}
+ echo "${i##/proc/}"
+ _ret=0
+ done
+ debug_on
+ return $_ret
+ }
+fi
+
+_emergency_shell() {
+ local _name="$1"
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ : > /.console_lock
+ echo "PS1=\"$_name:\\\${PWD}# \"" > /etc/profile
+ systemctl start dracut-emergency.service
+ rm -f -- /etc/profile
+ rm -f -- /.console_lock
+ else
+ debug_off
+ source_hook "$hook"
+ echo
+ /sbin/rdsosreport
+ echo 'You might want to save "/run/initramfs/rdsosreport.txt" to a USB stick or /boot'
+ echo 'after mounting them and attach it to a bug report.'
+ if ! RD_DEBUG='' getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then
+ echo
+ echo 'To get more debug information in the report,'
+ echo 'reboot with "rd.debug" added to the kernel command line.'
+ fi
+ echo
+ echo 'Dropping to debug shell.'
+ echo
+ export PS1="$_name:\${PWD}# "
+ [ -e /.profile ] || : > /.profile
+
+ _ctty="$(RD_DEBUG='' getarg rd.ctty=)" && _ctty="/dev/${_ctty##*/}"
+ if [ -z "$_ctty" ]; then
+ _ctty=console
+ while [ -f /sys/class/tty/$_ctty/active ]; do
+ read -r _ctty < /sys/class/tty/$_ctty/active
+ _ctty=${_ctty##* } # last one in the list
+ done
+ _ctty=/dev/$_ctty
+ fi
+ [ -c "$_ctty" ] || _ctty=/dev/tty1
+ case "$(/usr/bin/setsid --help 2>&1)" in *--ctty*) CTTY="--ctty" ;; esac
+ setsid $CTTY /bin/sh -i -l 0<> $_ctty 1<> $_ctty 2<> $_ctty
+ fi
+}
+
+emergency_shell() {
+ local _ctty
+ set +e
+ local _rdshell_name="dracut" action="Boot" hook="emergency"
+ local _emergency_action
+
+ if [ "$1" = "-n" ]; then
+ _rdshell_name=$2
+ shift 2
+ elif [ "$1" = "--shutdown" ]; then
+ _rdshell_name=$2
+ action="Shutdown"
+ hook="shutdown-emergency"
+ shift 2
+ fi
+
+ echo
+ echo
+ warn "$*"
+ echo
+
+ _emergency_action=$(getarg rd.emergency)
+ [ -z "$_emergency_action" ] \
+ && [ -e /run/initramfs/.die ] \
+ && _emergency_action=halt
+
+ if getargbool 1 rd.shell -d -y rdshell || getarg rd.break -d rdbreak; then
+ _emergency_shell "$_rdshell_name"
+ else
+ source_hook "$hook"
+ warn "$action has failed. To debug this issue add \"rd.shell rd.debug\" to the kernel command line."
+ [ -z "$_emergency_action" ] && _emergency_action=halt
+ fi
+
+ case "$_emergency_action" in
+ reboot)
+ reboot || exit 1
+ ;;
+ poweroff)
+ poweroff || exit 1
+ ;;
+ halt)
+ halt || exit 1
+ ;;
+ esac
+}
+
+# Retain the values of these variables but ensure that they are unexported
+# This is a POSIX-compliant equivalent of bash's "export -n"
+export_n() {
+ local var
+ local val
+ for var in "$@"; do
+ eval val=\$$var
+ unset $var
+ [ -n "$val" ] && eval "$var=\"$val\""
+ done
+}
+
+# returns OK if list1 contains all elements of list2, i.e. checks if list2 is a
+# sublist of list1. An order and a duplication doesn't matter.
+#
+# $1 = separator
+# $2 = list1
+# $3 = list2
+# $4 = ignore values, separated by $1
+listlist() {
+ local _sep="$1"
+ local _list="${_sep}${2}${_sep}"
+ local _sublist="$3"
+ [ -n "$4" ] && local _iglist="${_sep}${4}${_sep}"
+ local IFS="$_sep"
+ local _v
+
+ [ "$_list" = "$_sublist" ] && return 0
+
+ for _v in $_sublist; do
+ if [ -n "$_v" ] && ! ([ -n "$_iglist" ] && strstr "$_iglist" "$_v"); then
+ strstr "$_list" "$_v" || return 1
+ fi
+ done
+
+ return 0
+}
+
+# returns OK if both lists contain the same values. An order and a duplication
+# doesn't matter.
+#
+# $1 = separator
+# $2 = list1
+# $3 = list2
+# $4 = ignore values, separated by $1
+are_lists_eq() {
+ listlist "$1" "$2" "$3" "$4" && listlist "$1" "$3" "$2" "$4"
+}
+
+setmemdebug() {
+ if [ -z "$DEBUG_MEM_LEVEL" ]; then
+ DEBUG_MEM_LEVEL=$(getargnum 0 0 5 rd.memdebug)
+ export DEBUG_MEM_LEVEL
+ fi
+}
+
+setmemdebug
+
+# parameters: func log_level prefix msg [trace_level:trace]...
+make_trace_mem() {
+ local log_level prefix msg msg_printed
+ local trace trace_level trace_in_higher_levels insert_trace
+
+ msg=$1
+ shift
+
+ prefix='[debug_mem]'
+ log_level=$DEBUG_MEM_LEVEL
+
+ if [ -z "$log_level" ] || [ "$log_level" -le 0 ]; then
+ return
+ fi
+
+ # FIXME? useless echo?
+ # shellcheck disable=SC2116
+ msg=$(echo "$msg")
+
+ msg_printed=0
+ while [ $# -gt 0 ]; do
+ trace=${1%%:*}
+ trace_level=${trace%%+}
+ [ "$trace" != "$trace_level" ] && trace_in_higher_levels="yes"
+ trace=${1##*:}
+
+ if [ -z "$trace_level" ]; then
+ trace_level=0
+ fi
+
+ insert_trace=0
+ if [ -n "$trace_in_higher_levels" ]; then
+ if [ "$log_level" -ge "$trace_level" ]; then
+ insert_trace=1
+ fi
+ else
+ if [ "$log_level" -eq "$trace_level" ]; then
+ insert_trace=1
+ fi
+ fi
+
+ if [ $insert_trace -eq 1 ]; then
+ if [ $msg_printed -eq 0 ]; then
+ echo "$prefix $msg"
+ msg_printed=1
+ fi
+ show_memstats "$trace"
+ fi
+ shift
+ done
+}
+
+# parameters: type
+show_memstats() {
+ case $1 in
+ shortmem)
+ while read -r line || [ -n "$line" ]; do
+ str_starts "$line" "MemFree" \
+ || str_starts "$line" "Cached" \
+ || str_starts "$line" "Slab" \
+ || continue
+ echo "$line"
+ done < /proc/meminfo
+ ;;
+ mem)
+ cat /proc/meminfo
+ ;;
+ slab)
+ cat /proc/slabinfo
+ ;;
+ iomem)
+ cat /proc/iomem
+ ;;
+ esac
+}
+
+remove_hostonly_files() {
+ rm -fr /etc/cmdline /etc/cmdline.d/*.conf "$hookdir/initqueue/finished"
+ if [ -f /lib/dracut/hostonly-files ]; then
+ while read -r line || [ -n "$line" ]; do
+ [ -e "$line" ] || [ -h "$line" ] || continue
+ rm -f "$line"
+ done < /lib/dracut/hostonly-files
+ fi
+}
+
+# parameter: kernel_module [filesystem_name]
+# returns OK if kernel_module is loaded
+# modprobe fails if /lib/modules is not available (--no-kernel use case)
+load_fstype() {
+ local - fs _fs="${2:-$1}"
+ set +x
+ while read -r d fs || [ "$d" ]; do
+ [ "${fs:-$d}" = "$_fs" ] && return 0
+ done < /proc/filesystems
+ modprobe "$1"
+}
+
+# parameter: size of live image
+# calls emergency shell if ram size is too small for the image
+check_live_ram() {
+ minmem=$(getarg rd.minmem)
+ minmem=${minmem:-1024}
+ imgsize=$1
+ memsize=$(($(sed -n 's/MemTotal: *\([[:digit:]]*\).*/\1/p' /proc/meminfo) / 1024))
+
+ if [ -z "$imgsize" ]; then
+ warn "Image size could not be determined"
+ return 0
+ fi
+
+ if [ $((memsize - imgsize)) -lt "$minmem" ]; then
+ sed -i "N;/and attach it to a bug report./s/echo$/echo\n\
+ echo \n\
+ echo 'Warning!!!'\n\
+ echo 'The memory size of your system is too small for this live image.'\n\
+ echo 'Expect killed processes due to out of memory conditions.'\n\
+ echo \n/" /usr/bin/dracut-emergency
+
+ emergency_shell
+ fi
+}