#!/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