You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1154 lines
28 KiB

#!/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 <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 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 <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
_ctty=$(cat /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"
if type plymouth > /dev/null 2>&1; then
plymouth --hide-splash
elif [ -x /oldroot/bin/plymouth ]; then
/oldroot/bin/plymouth --hide-splash
fi
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)
grep -e "^MemFree" -e "^Cached" -e "^Slab" /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
}