#!/usr/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() {
[ -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() {
[ -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
}
# replaces all occurrences of 'search' in 'str' with 'replacement'
#
# str_replace str search replacement
#
# example:
# str_replace ' one two three ' ' ' '_'
str_replace() {
local in="$1"
local s="$2"
local r="$3"
local out=''
while strstr "${in}" "$s"; do
chop="${in%%"$s"*}"
out="${out}${chop}$r"
in="${in#*"$s"}"
done
echo "${out}${in}"
}
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
# False if "getarg " returns "0", "no", or "off".
# True if getarg returns any other non-empty string.
# If not found, assumes - 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
# Will echo the arg if it's in range [minval - maxval].
# If it's not set or it's not valid, will set it .
# Note all values are required to be >= 0 here.
# 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 similary 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
for f in "$hookdir"/initqueue/finished/*.sh; do
[ "$f" = "$hookdir/initqueue/finished/*.sh" ] && return 0
# shellcheck disable=SC1090
{ [ -e "$f" ] && (. "$f"); } || return 1
done
return 0
}
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
# usage: ismounted /dev/
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