#!/bin/sh # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh export DRACUT_SYSTEMD export NEWROOT if [ -n "$NEWROOT" ]; then [ -d $NEWROOT ] || mkdir -p -m 0755 $NEWROOT fi 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 debug_off() { set +x } debug_on() { [ "$RD_DEBUG" = "yes" ] && set -x } # returns OK if $1 contains $2 strstr() { [ "${1#*$2*}" != "$1" ] } # returns OK if $1 contains $2 at the beginning str_starts() { [ "${1#$2*}" != "$1" ] } # returns OK if $1 contains $2 at the end str_ends() { [ "${1%*$2}" != "$1" ] } trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters echo -n "$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 [ "$DRACUT_QUIET" != "yes" ] && \ echo "dracut: $*" >&2 } else warn() { echo "Warning: $*" >&2 } info() { check_quiet [ "$DRACUT_QUIET" != "yes" ] && \ echo "$*" >&2 } fi vwarn() { while read line || [ -n "$line" ]; do warn $line; done } vinfo() { while read 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 _t 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 strstr "$(ls -l -- "/proc/$_pid" "/proc/$_pid/fd" 2>/dev/null)" "$1" && kill -9 "$_pid" done } 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; do CMDLINE_ETC="$CMDLINE_ETC $_line"; done ", we want the exact match if [ "$_o" = "$1" ]; then _val="1"; unset _doecho fi continue fi if [ "${_o#*=}" = "$_o" ]; then # if cmdline argument has no "=", we assume "=1" _val="1"; unset _doecho continue fi _val="${_o#*=}" _doecho=1 fi done if [ -n "$_val" ]; then [ "x$_doecho" != "x" ] && echo "$_val"; return 0; fi return 1; } getarg() { debug_off local _deprecated _newoption while [ $# -gt 0 ]; do case $1 in -d) _deprecated=1; shift;; -y) if _dogetarg $2 >/dev/null; then if [ "$_deprecated" = "1" ]; then [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption' instead." || warn "Option '$2' is deprecated." fi echo 1 debug_on return 0 fi _deprecated=0 shift 2;; -n) if _dogetarg $2 >/dev/null; then echo 0; if [ "$_deprecated" = "1" ]; then [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption=0' instead." || warn "Option '$2' is deprecated." fi debug_on return 1 fi _deprecated=0 shift 2;; *) if [ -z "$_newoption" ]; then _newoption="$1" fi if _dogetarg $1; then if [ "$_deprecated" = "1" ]; then [ -n "$_newoption" ] && warn "Kernel command line option '$1' is deprecated, use '$_newoption' instead." || warn "Option '$1' is deprecated." 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 "$@") [ $? -ne 0 -a -z "$_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") [ $? -ne 0 -a -z "$_b" ] && _b=$_default if [ -n "$_b" ]; then isdigit "$_b" && _b=$(($_b)) && \ [ $_b -ge $_min ] && [ $_b -le $_max ] && echo $_b && return fi echo $_default } _dogetargs() { debug_off local _o _found _key unset _o unset _found CMDLINE=$(getcmdline) _key="$1" set -- for _o in $CMDLINE; do if [ "$_o" = "$_key" ]; then _found=1; elif [ "${_o%%=*}" = "${_key%=}" ]; then [ -n "${_o%%=*}" ] && set -- "$@" "${_o#*=}"; _found=1; fi done if [ -n "$_found" ]; then [ $# -gt 0 ] && printf '%s' "$*" return 0 fi return 1; } getargs() { debug_off local _val _i _args _gfound _deprecated unset _val unset _gfound _newoption="$1" _args="$@" set -- for _i in $_args; do if [ "$i" = "-d" ]; then _deprecated=1 continue fi _val="$(_dogetargs $_i)" if [ $? -eq 0 ]; then if [ "$_deprecated" = "1" ]; then [ -n "$_newoption" ] && warn "Option '$_i' is deprecated, use '$_newoption' instead." || warn "Option $_i is deprecated!" fi _gfound=1 fi [ -n "$_val" ] && set -- "$@" "$_val" _deprecated=0 done if [ -n "$_gfound" ]; then if [ $# -gt 0 ]; then printf '%s' "$*" else printf 1 fi 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 /etc/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 [ -e "$f" ] && . "$f" "$@"; 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 { [ -e "$f" ] && ( . "$f" ) ; } || return 1 done return 0 } source_conf() { local f [ "$1" ] && [ -d "/$1" ] || return 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 getargbool 0 "rd.debug=" && emergency_shell 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" if [ -z "$DRACUT_SYSTEMD" ]; then getarg quiet || DRACUT_QUIET="yes" a=$(getarg loglevel=) [ -n "$a" ] && [ $a -ge 28 ] && DRACUT_QUIET="yes" fi 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 dummy check; local file="$1"; local str="$2"; [ -z "$file" ] && return 1; [ -z "$str" ] && return 1; while read dummy check restofline; do if [ "$check" = "$str" ]; then debug_on return 0 fi done < $file debug_on return 1 } udevsettle() { [ -z "$UDEVVERSION" ] && export UDEVVERSION=$(udevadm --version) if [ $UDEVVERSION -ge 143 ]; then udevadm settle --exit-if-exists=$hookdir/initqueue/work $settle_exit_if_exists else udevadm settle --timeout=30 fi } udevproperty() { [ -z "$UDEVVERSION" ] && export UDEVVERSION=$(udevadm --version) if [ $UDEVVERSION -ge 143 ]; then for i in "$@"; do udevadm control --property=$i; done else for i in "$@"; do udevadm control --env=$i; done fi } find_mount() { local dev mnt etc wanted_dev wanted_dev="$(readlink -e -q $1)" while read dev mnt etc; 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 a m a; do [ "$m" = "$1" ] && return 0 done < /proc/mounts return 1 } fi # root=nfs:[:][:] # root=nfs4:[:][:] nfsroot_to_var() { # strip nfs[4]: local arg="$@:" nfs="${arg%%:*}" arg="${arg##$nfs:}" # check if we have a server if strstr "$arg" ':/*' ; then server="${arg%%:/*}" arg="/${arg##*:/}" fi path="${arg%%:*}" # rest are options options="${arg##$path}" # strip leading ":" options="${options##:}" # strip ":" options="${options%%:}" # Does it really start with '/'? [ -n "${path%%/*}" ] && path="error"; #Fix kernel legacy style separating path and options with ',' if [ "$path" != "${path#*,}" ] ; then options=${path#*,} path=${path%%,*} 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" # # TOOD: 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 } # 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" mkdir -p "$dest"; dest=$(readlink -e -q "$dest") ( cd "$src"; 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 [ "$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 ___="${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=*) 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 { 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