diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:54:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:54:25 +0000 |
commit | 9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a (patch) | |
tree | 2efb72864cc69e174c9c5ee33efb88a5f1553b48 /modules.d/99base | |
parent | Initial commit. (diff) | |
download | dracut-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')
-rwxr-xr-x | modules.d/99base/dracut-dev-lib.sh | 139 | ||||
-rwxr-xr-x | modules.d/99base/dracut-lib.sh | 1178 | ||||
-rwxr-xr-x | modules.d/99base/init.sh | 399 | ||||
-rwxr-xr-x | modules.d/99base/initqueue.sh | 72 | ||||
-rwxr-xr-x | modules.d/99base/loginit.sh | 23 | ||||
-rwxr-xr-x | modules.d/99base/module-setup.sh | 146 | ||||
-rwxr-xr-x | modules.d/99base/parse-root-opts.sh | 14 | ||||
-rwxr-xr-x | modules.d/99base/rdsosreport.sh | 61 |
8 files changed, 2032 insertions, 0 deletions
diff --git a/modules.d/99base/dracut-dev-lib.sh b/modules.d/99base/dracut-dev-lib.sh new file mode 100755 index 0000000..5779508 --- /dev/null +++ b/modules.d/99base/dracut-dev-lib.sh @@ -0,0 +1,139 @@ +#!/bin/sh + +# 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 [ "${in##*"$s"*}" != "$in" ]; do + chop="${in%%"$s"*}" + out="${out}${chop}$r" + in="${in#*"$s"}" + done + printf -- '%s' "${out}${in}" +} + +# get a systemd-compatible unit name from a path +# (mimics unit_name_from_path_instance()) +dev_unit_name() { + local dev="$1" + + if command -v systemd-escape > /dev/null; then + systemd-escape -p -- "$dev" + return $? + fi + + if [ "$dev" = "/" -o -z "$dev" ]; then + printf -- "-" + return 0 + fi + + dev="${1%%/}" + dev="${dev##/}" + # shellcheck disable=SC1003 + dev="$(str_replace "$dev" '\' '\x5c')" + dev="$(str_replace "$dev" '-' '\x2d')" + if [ "${dev##.}" != "$dev" ]; then + dev="\x2e${dev##.}" + fi + dev="$(str_replace "$dev" '/' '-')" + + printf -- "%s" "$dev" +} + +# set_systemd_timeout_for_dev [-n] <dev> [<timeout>] +# Set 'rd.timeout' as the systemd timeout for <dev> +set_systemd_timeout_for_dev() { + local _name + local _needreload + local _noreload + local _timeout + + [ -z "$DRACUT_SYSTEMD" ] && return 0 + + if [ "$1" = "-n" ]; then + _noreload=1 + shift + fi + + if [ -n "$2" ]; then + _timeout="$2" + else + _timeout=$(getarg rd.timeout) + fi + + _timeout=${_timeout:-0} + + _name=$(dev_unit_name "$1") + if ! [ -L "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" ]; then + [ -d "${PREFIX}"/etc/systemd/system/initrd.target.wants ] || mkdir -p "${PREFIX}"/etc/systemd/system/initrd.target.wants + ln -s ../"${_name}".device "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" + type mark_hostonly > /dev/null 2>&1 && mark_hostonly /etc/systemd/system/initrd.target.wants/"${_name}".device + _needreload=1 + fi + + if ! [ -f "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" ]; then + mkdir -p "${PREFIX}/etc/systemd/system/${_name}.device.d" + { + echo "[Unit]" + echo "JobTimeoutSec=$_timeout" + echo "JobRunningTimeoutSec=$_timeout" + } > "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" + type mark_hostonly > /dev/null 2>&1 && mark_hostonly /etc/systemd/system/"${_name}".device.d/timeout.conf + _needreload=1 + fi + + if [ -z "$PREFIX" ] && [ "$_needreload" = 1 ] && [ -z "$_noreload" ]; then + /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload + fi +} + +# wait_for_dev <dev> [<timeout>] +# +# Installs a initqueue-finished script, +# which will cause the main loop only to exit, +# if the device <dev> is recognized by the system. +wait_for_dev() { + local _name + local _noreload + + if [ "$1" = "-n" ]; then + _noreload=-n + shift + fi + + _name="$(str_replace "$1" '/' '\x2f')" + + type mark_hostonly > /dev/null 2>&1 && mark_hostonly "$hookdir/initqueue/finished/devexists-${_name}.sh" + + [ -e "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh" ] && return 0 + + printf '[ -e "%s" ]\n' "$1" \ + >> "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh" + { + printf '[ -e "%s" ] || ' "$1" + printf 'warn "\"%s\" does not exist"\n' "$1" + } >> "${PREFIX}$hookdir/emergency/80-${_name}.sh" + + set_systemd_timeout_for_dev $_noreload "$@" +} + +cancel_wait_for_dev() { + local _name + _name="$(str_replace "$1" '/' '\x2f')" + rm -f -- "$hookdir/initqueue/finished/devexists-${_name}.sh" + rm -f -- "$hookdir/emergency/80-${_name}.sh" + if [ -n "$DRACUT_SYSTEMD" ]; then + _name=$(dev_unit_name "$1") + rm -f -- "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" + rm -f -- "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" + /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload + fi +} 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 +} diff --git a/modules.d/99base/init.sh b/modules.d/99base/init.sh new file mode 100755 index 0000000..285059e --- /dev/null +++ b/modules.d/99base/init.sh @@ -0,0 +1,399 @@ +#!/bin/sh +# +# Licensed under the GPLv2 +# +# Copyright 2008-2010, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> +# Jeremy Katz <katzj@redhat.com> + +export -p > /tmp/export.orig + +NEWROOT="/sysroot" +[ -d $NEWROOT ] || mkdir -p -m 0755 $NEWROOT + +OLDPATH=$PATH +PATH=/usr/sbin:/usr/bin:/sbin:/bin +export PATH + +# mount some important things +if [ ! -d /proc/self ]; then + if ! mount -t proc -o nosuid,noexec,nodev proc /proc > /dev/null; then + echo "Cannot mount proc on /proc! Compile the kernel with CONFIG_PROC_FS!" + exit 1 + fi +fi + +if [ ! -d /sys/kernel ]; then + if ! mount -t sysfs -o nosuid,noexec,nodev sysfs /sys > /dev/null; then + echo "Cannot mount sysfs on /sys! Compile the kernel with CONFIG_SYSFS!" + exit 1 + fi +fi + +RD_DEBUG="" +. /lib/dracut-lib.sh + +setdebug + +if ! ismounted /dev; then + mount -t devtmpfs -o mode=0755,noexec,nosuid,strictatime devtmpfs /dev > /dev/null +fi + +if ! ismounted /dev; then + echo "Cannot mount devtmpfs on /dev! Compile the kernel with CONFIG_DEVTMPFS!" + exit 1 +fi + +# prepare the /dev directory +[ ! -h /dev/fd ] && ln -s /proc/self/fd /dev/fd > /dev/null 2>&1 +[ ! -h /dev/stdin ] && ln -s /proc/self/fd/0 /dev/stdin > /dev/null 2>&1 +[ ! -h /dev/stdout ] && ln -s /proc/self/fd/1 /dev/stdout > /dev/null 2>&1 +[ ! -h /dev/stderr ] && ln -s /proc/self/fd/2 /dev/stderr > /dev/null 2>&1 + +if ! ismounted /dev/pts; then + mkdir -m 0755 -p /dev/pts + mount -t devpts -o gid=5,mode=620,noexec,nosuid devpts /dev/pts > /dev/null +fi + +if ! ismounted /dev/shm; then + mkdir -m 0755 -p /dev/shm + mount -t tmpfs -o mode=1777,noexec,nosuid,nodev,strictatime tmpfs /dev/shm > /dev/null +fi + +if ! ismounted /run; then + mkdir -m 0755 -p /newrun + if ! str_starts "$(readlink -f /bin/sh)" "/run/"; then + mount -t tmpfs -o mode=0755,noexec,nosuid,nodev,strictatime tmpfs /newrun > /dev/null + else + # the initramfs binaries are located in /run, so don't mount it with noexec + mount -t tmpfs -o mode=0755,nosuid,nodev,strictatime tmpfs /newrun > /dev/null + fi + cp -a /run/* /newrun > /dev/null 2>&1 + mount --move /newrun /run + rm -fr -- /newrun +fi + +if command -v kmod > /dev/null 2> /dev/null; then + kmod static-nodes --format=tmpfiles 2> /dev/null \ + | while read -r type file mode _ _ _ majmin || [ -n "$type" ]; do + type=${type%\!} + case $type in + d) + mkdir -m "$mode" -p "$file" + ;; + c) + mknod -m "$mode" "$file" "$type" "${majmin%:*}" "${majmin#*:}" + ;; + esac + done +fi + +trap "emergency_shell Signal caught!" 0 + +export UDEVRULESD=/run/udev/rules.d +[ -d /run/udev ] || mkdir -p -m 0755 /run/udev +[ -d "$UDEVRULESD" ] || mkdir -p -m 0755 "$UDEVRULESD" + +if [ "$RD_DEBUG" = "yes" ]; then + mkfifo /run/initramfs/loginit.pipe + loginit "$DRACUT_QUIET" < /run/initramfs/loginit.pipe > /dev/console 2>&1 & + exec > /run/initramfs/loginit.pipe 2>&1 +else + exec 0<> /dev/console 1<> /dev/console 2<> /dev/console +fi + +[ -f /usr/lib/initrd-release ] && . /usr/lib/initrd-release +[ -n "$VERSION_ID" ] && info "$NAME-$VERSION_ID" + +source_conf /etc/conf.d + +if getarg "rd.cmdline=ask"; then + echo "Enter additional kernel command line parameter (end with ctrl-d or .)" + while read -r -p "> " ${BASH:+-e} line || [ -n "$line" ]; do + [ "$line" = "." ] && break + echo "$line" >> /etc/cmdline.d/99-cmdline-ask.conf + done +fi + +if ! getargbool 1 'rd.hostonly'; then + [ -f /etc/cmdline.d/99-cmdline-ask.conf ] && mv /etc/cmdline.d/99-cmdline-ask.conf /tmp/99-cmdline-ask.conf + remove_hostonly_files + [ -f /tmp/99-cmdline-ask.conf ] && mv /tmp/99-cmdline-ask.conf /etc/cmdline.d/99-cmdline-ask.conf +fi + +# run scriptlets to parse the command line +make_trace_mem "hook cmdline" '1+:mem' '1+:iomem' '3+:slab' +getarg 'rd.break=cmdline' -d 'rdbreak=cmdline' && emergency_shell -n cmdline "Break before cmdline" +source_hook cmdline + +[ -z "$root" ] && die "No or empty root= argument" +[ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + +export root rflags fstype netroot NEWROOT + +# pre-udev scripts run before udev starts, and are run only once. +make_trace_mem "hook pre-udev" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-udev' -d 'rdbreak=pre-udev' && emergency_shell -n pre-udev "Break before pre-udev" +source_hook pre-udev + +UDEV_LOG=err +getargbool 0 rd.udev.info -d -y rdudevinfo && UDEV_LOG=info +getargbool 0 rd.udev.debug -d -y rdudevdebug && UDEV_LOG=debug + +# start up udev and trigger cold plugs +UDEV_LOG=$UDEV_LOG "$systemdutildir"/systemd-udevd --daemon --resolve-names=never + +UDEV_QUEUE_EMPTY="udevadm settle --timeout=0" + +udevproperty "hookdir=$hookdir" + +make_trace_mem "hook pre-trigger" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-trigger' -d 'rdbreak=pre-trigger' && emergency_shell -n pre-trigger "Break before pre-trigger" +source_hook pre-trigger + +udevadm control --reload > /dev/null 2>&1 || : +# then the rest +udevadm trigger --type=subsystems --action=add > /dev/null 2>&1 +udevadm trigger --type=devices --action=add > /dev/null 2>&1 + +make_trace_mem "hook initqueue" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=initqueue' -d 'rdbreak=initqueue' && emergency_shell -n initqueue "Break before initqueue" + +RDRETRY=$(getarg rd.retry -d 'rd_retry=') +RDRETRY=${RDRETRY:-180} +RDRETRY=$((RDRETRY * 2)) +export RDRETRY +main_loop=0 +export main_loop +while :; do + + check_finished && break + + udevsettle + + check_finished && break + + if [ -f "$hookdir"/initqueue/work ]; then + rm -f -- "$hookdir"/initqueue/work + fi + + for job in "$hookdir"/initqueue/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + $UDEV_QUEUE_EMPTY > /dev/null 2>&1 || continue + + for job in "$hookdir"/initqueue/settled/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + $UDEV_QUEUE_EMPTY > /dev/null 2>&1 || continue + + # no more udev jobs and queues empty. + sleep 0.5 + + if [ $main_loop -gt $((2 * RDRETRY / 3)) ]; then + for job in "$hookdir"/initqueue/timeout/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + udevadm settle --timeout=0 > /dev/null 2>&1 || main_loop=0 + [ -f "$hookdir"/initqueue/work ] && main_loop=0 + done + fi + + main_loop=$((main_loop + 1)) + [ $main_loop -gt $RDRETRY ] \ + && { + flock -s 9 + emergency_shell "Could not boot." + } 9> /.console_lock +done +unset job +unset queuetriggered +unset main_loop +unset RDRETRY + +# pre-mount happens before we try to mount the root filesystem, +# and happens once. +make_trace_mem "hook pre-mount" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-mount' -d 'rdbreak=pre-mount' && emergency_shell -n pre-mount "Break before pre-mount" +source_hook pre-mount + +getarg 'rd.break=mount' -d 'rdbreak=mount' && emergency_shell -n mount "Break before mount" +# mount scripts actually try to mount the root filesystem, and may +# be sourced any number of times. As soon as one succeeds, no more are sourced. +_i_mount=0 +while :; do + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + umount "$NEWROOT" + fi + for f in "$hookdir"/mount/*.sh; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook" + umount "$NEWROOT" + rm -f -- "$f" + fi + done + + _i_mount=$((_i_mount + 1)) + [ $_i_mount -gt 20 ] \ + && { + flock -s 9 + emergency_shell "Can't mount root filesystem" + } 9> /.console_lock +done + +{ + printf "Mounted root filesystem " + while read -r dev mp _ || [ -n "$dev" ]; do [ "$mp" = "$NEWROOT" ] && echo "$dev"; done < /proc/mounts +} | vinfo + +# pre pivot scripts are sourced just before we doing cleanup and switch over +# to the new root. +make_trace_mem "hook pre-pivot" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-pivot' -d 'rdbreak=pre-pivot' && emergency_shell -n pre-pivot "Break before pre-pivot" +source_hook pre-pivot + +make_trace_mem "hook cleanup" '1:shortmem' '2+:mem' '3+:slab' +# pre pivot cleanup scripts are sourced just before we switch over to the new root. +getarg 'rd.break=cleanup' -d 'rdbreak=cleanup' && emergency_shell -n cleanup "Break before cleanup" +source_hook cleanup + +# By the time we get here, the root filesystem should be mounted. +# Try to find init. +for i in "$(getarg real_init=)" "$(getarg init=)" $(getargs rd.distroinit=) /sbin/init; do + [ -n "$i" ] || continue + + __p="${NEWROOT}/${i}" + if [ -h "$__p" ]; then + # relative links need to be left alone, + # while absolute links need to be resolved and prefixed. + __pt=$(readlink "$__p") + [ "${__pt#/}" = "$__pt" ] || __p="${NEWROOT}/$__pt" + fi + if [ -x "$__p" ]; then + INIT="$i" + break + fi +done + +[ "$INIT" ] || { + echo "Cannot find init!" + echo "Please check to make sure you passed a valid root filesystem!" + emergency_shell +} + +udevadm control --exit +udevadm info --cleanup-db + +debug_off # Turn off debugging for this section + +CAPSH=$(command -v capsh) +SWITCH_ROOT=$(command -v switch_root) + +# unexport some vars +export_n root rflags fstype netroot NEWROOT +unset CMDLINE + +# Clean up the environment +for i in $(export -p); do + i=${i#declare -x} + i=${i#export} + strstr "$i" "=" || continue + i=${i%%=*} + [ -z "$i" ] && continue + case $i in + root | PATH | HOME | TERM | PS4 | RD_*) + : + ;; + *) + unset "$i" + ;; + esac +done +. /tmp/export.orig 2> /dev/null || : +rm -f -- /tmp/export.orig + +initargs="" +read -r CLINE < /proc/cmdline +if getarg init= > /dev/null; then + ignoreargs="console BOOT_IMAGE" + # only pass arguments after init= to the init + CLINE=${CLINE#*init=} + # shellcheck disable=SC2086 + set -- $CLINE + shift # clear out the rest of the "init=" arg + for x in "$@"; do + for s in $ignoreargs; do + [ "${x%%=*}" = "$s" ] && continue 2 + done + initargs="$initargs $x" + done + unset CLINE +else + debug_off # Turn off debugging for this section + # shellcheck disable=SC2086 + set -- $CLINE + for x in "$@"; do + case "$x" in + [0-9] | s | S | single | emergency | auto) + initargs="$initargs $x" + ;; + esac + done +fi +debug_on + +if ! [ -d "$NEWROOT"/run ]; then + NEWRUN=/dev/.initramfs + mkdir -m 0755 -p "$NEWRUN" + mount --rbind /run/initramfs "$NEWRUN" +fi + +wait_for_loginit + +# remove helper symlink +[ -h /dev/root ] && rm -f -- /dev/root + +bv=$(getarg rd.break -d rdbreak) && [ -z "$bv" ] \ + && emergency_shell -n switch_root "Break before switch_root" +unset bv +info "Switching root" + +unset PS4 + +PATH=$OLDPATH +export PATH + +if [ -f /etc/capsdrop ]; then + . /etc/capsdrop + info "Calling $INIT with capabilities $CAPS_INIT_DROP dropped." + unset RD_DEBUG + exec "$CAPSH" --drop="$CAPS_INIT_DROP" -- \ + -c "exec \"$SWITCH_ROOT\" \"$NEWROOT\" \"$INIT\" $initargs" \ + || { + warn "Command:" + warn capsh --drop="$CAPS_INIT_DROP" -- -c exec "$SWITCH_ROOT" "$NEWROOT" "$INIT" "$initargs" + warn "failed." + emergency_shell + } +else + unset RD_DEBUG + # shellcheck disable=SC2086 + exec "$SWITCH_ROOT" "$NEWROOT" "$INIT" $initargs || { + warn "Something went very badly wrong in the initramfs. Please " + warn "file a bug against dracut." + emergency_shell + } +fi diff --git a/modules.d/99base/initqueue.sh b/modules.d/99base/initqueue.sh new file mode 100755 index 0000000..2c49079 --- /dev/null +++ b/modules.d/99base/initqueue.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# Licensed under the GPLv2+ +# +# Copyright 2008-2010, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +while [ $# -gt 0 ]; do + case "$1" in + --onetime) + onetime="yes" + ;; + --online) + qname="/online" + ;; + --settled) + qname="/settled" + ;; + --finished) + qname="/finished" + ;; + --timeout) + qname="/timeout" + ;; + --unique) + unique="yes" + ;; + --name) + name="$2" + shift + ;; + --env) + env="$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 [ -z "$exe" ]; then + echo "Invalid command" + exit 1 +fi + +{ + # shellcheck disable=SC2016 + [ -n "$onetime" ] && echo '[ -e "$job" ] && rm -f -- "$job"' + [ -n "$env" ] && echo "$env" + echo "$exe" "$@" +} > "/tmp/$$-${job}.sh" + +mv -f "/tmp/$$-${job}.sh" "$hookdir/initqueue${qname}/${job}.sh" +[ -z "$qname" ] && : >> "$hookdir"/initqueue/work +exit 0 diff --git a/modules.d/99base/loginit.sh b/modules.d/99base/loginit.sh new file mode 100755 index 0000000..cdb305b --- /dev/null +++ b/modules.d/99base/loginit.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# turn off debugging +set +x + +QUIET=$1 + +printf "%s" "$$" > /run/initramfs/loginit.pid + +# shellcheck disable=SC2015 +[ -e /dev/kmsg ] && exec 5> /dev/kmsg || exec 5> /dev/null +exec 6> /run/initramfs/init.log + +while read -r line || [ -n "$line" ]; do + if [ "$line" = "DRACUT_LOG_END" ]; then + rm -f -- /run/initramfs/loginit.pipe + exit 0 + fi + echo "<31>dracut: $line" >&5 + # if "quiet" is specified we output to /dev/console + [ -n "$QUIET" ] || echo "dracut: $line" + echo "$line" >&6 +done diff --git a/modules.d/99base/module-setup.sh b/modules.d/99base/module-setup.sh new file mode 100755 index 0000000..54b0deb --- /dev/null +++ b/modules.d/99base/module-setup.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +# called by dracut +check() { + return 0 +} + +# called by dracut +depends() { + echo udev-rules + return 0 +} + +# called by dracut +install() { + inst_multiple mount mknod mkdir sleep chown \ + sed ls flock cp mv dmesg rm ln rmmod mkfifo umount readlink setsid \ + modprobe chmod tr + + inst_multiple -o findmnt less kmod + + inst_binary "${dracutbasedir}/dracut-util" "/usr/bin/dracut-util" + + ln -s dracut-util "${initdir}/usr/bin/dracut-getarg" + ln -s dracut-util "${initdir}/usr/bin/dracut-getargs" + + if [ ! -e "${initdir}/bin/sh" ]; then + inst_multiple bash + (ln -s bash "${initdir}/bin/sh" || :) + fi + + # add common users in /etc/passwd, it will be used by nfs/ssh currently + # use password for hostonly images to facilitate secure sulogin in emergency console + [[ $hostonly ]] && pwshadow='x' + grep '^root:' "$initdir/etc/passwd" > /dev/null 2>&1 || echo "root:$pwshadow:0:0::/root:/bin/sh" >> "$initdir/etc/passwd" + grep '^nobody:' "$dracutsysrootdir"/etc/passwd >> "$initdir/etc/passwd" + + [[ $hostonly ]] && grep '^root:' "$dracutsysrootdir"/etc/shadow >> "$initdir/etc/shadow" + + # install our scripts and hooks + inst_script "$moddir/init.sh" "/init" + inst_script "$moddir/initqueue.sh" "/sbin/initqueue" + inst_script "$moddir/loginit.sh" "/sbin/loginit" + inst_script "$moddir/rdsosreport.sh" "/sbin/rdsosreport" + + [ -e "${initdir}/lib" ] || mkdir -m 0755 -p "${initdir}"/lib + mkdir -m 0755 -p "${initdir}"/lib/dracut + mkdir -m 0755 -p "${initdir}"/lib/dracut/hooks + + mkdir -p "${initdir}"/tmp + + inst_simple "$moddir/dracut-lib.sh" "/lib/dracut-lib.sh" + inst_simple "$moddir/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" + mkdir -p "${initdir}"/var + + if ! dracut_module_included "systemd"; then + inst_multiple switch_root || dfatal "Failed to install switch_root" + inst_hook cmdline 10 "$moddir/parse-root-opts.sh" + fi + + if [[ $realinitpath ]]; then + for i in $realinitpath; do + echo "rd.distroinit=$i" + done > "${initdir}/etc/cmdline.d/distroinit.conf" + fi + + ln -fs /proc/self/mounts "$initdir/etc/mtab" + if [[ $ro_mnt == yes ]]; then + echo ro >> "${initdir}/etc/cmdline.d/base.conf" + fi + + [ -e "${initdir}/usr/lib" ] || mkdir -m 0755 -p "${initdir}"/usr/lib + + local VERSION="" + local PRETTY_NAME="" + # Derive an os-release file from the host, if it exists + if [[ -e $dracutsysrootdir/etc/os-release ]]; then + # shellcheck disable=SC1090 + . "$dracutsysrootdir"/etc/os-release + grep -hE -ve '^VERSION=' -ve '^PRETTY_NAME' "$dracutsysrootdir"/etc/os-release > "${initdir}"/usr/lib/initrd-release + [[ -n ${VERSION} ]] && VERSION+=" " + [[ -n ${PRETTY_NAME} ]] && PRETTY_NAME+=" " + else + # Fall back to synthesizing one, since dracut is presently used + # on non-systemd systems as well. + { + echo "NAME=dracut" + echo "ID=dracut" + echo "VERSION_ID=\"$DRACUT_VERSION\"" + echo 'ANSI_COLOR="0;34"' + } > "${initdir}"/usr/lib/initrd-release + fi + VERSION+="dracut-$DRACUT_VERSION" + PRETTY_NAME+="dracut-$DRACUT_VERSION (Initramfs)" + { + echo "VERSION=\"$VERSION\"" + echo "PRETTY_NAME=\"$PRETTY_NAME\"" + # This addition is relatively new, intended to allow software + # to easily detect the dracut version if need be without + # having it mixed in with the real underlying OS version. + echo "DRACUT_VERSION=\"${DRACUT_VERSION}\"" + } >> "$initdir"/usr/lib/initrd-release + echo "dracut-$DRACUT_VERSION" > "$initdir/lib/dracut/dracut-$DRACUT_VERSION" + ln -sf ../usr/lib/initrd-release "$initdir"/etc/initrd-release + ln -sf initrd-release "$initdir"/usr/lib/os-release + ln -sf initrd-release "$initdir"/etc/os-release + + ## save host_devs which we need bring up + if [[ $hostonly_cmdline == "yes" ]]; then + if [[ -n ${host_devs[*]} ]]; then + dracut_need_initqueue + fi + if [[ -f $initdir/lib/dracut/need-initqueue ]] || ! dracut_module_included "systemd"; then + ( + if dracut_module_included "systemd"; then + export DRACUT_SYSTEMD=1 + fi + export PREFIX="$initdir" + export hookdir=/lib/dracut/hooks + + # shellcheck source=dracut-dev-lib.sh + . "$moddir/dracut-dev-lib.sh" + + for _dev in "${host_devs[@]}"; do + for _dev2 in "${root_devs[@]}"; do + [[ $_dev == "$_dev2" ]] && continue 2 + done + + # We only actually wait for real devs - swap is only needed + # for resume and udev rules generated when parsing resume= + # argument take care of the waiting for us + for _dev2 in "${swap_devs[@]}"; do + [[ $_dev == "$_dev2" ]] && continue 2 + done + + _pdev=$(get_persistent_dev "$_dev") + + case "$_pdev" in + /dev/?*) wait_for_dev "$_pdev" 0 ;; + *) ;; + esac + done + ) + fi + fi +} diff --git a/modules.d/99base/parse-root-opts.sh b/modules.d/99base/parse-root-opts.sh new file mode 100755 index 0000000..9525249 --- /dev/null +++ b/modules.d/99base/parse-root-opts.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# shellcheck disable=SC2034 +root=$(getarg root=) + +rflags="$(getarg rootflags=)" +getargbool 0 ro && rflags="${rflags},ro" +getargbool 0 rw && rflags="${rflags},rw" +rflags="${rflags#,}" + +fstype="$(getarg rootfstype=)" +if [ -z "$fstype" ]; then + fstype="auto" +fi diff --git a/modules.d/99base/rdsosreport.sh b/modules.d/99base/rdsosreport.sh new file mode 100755 index 0000000..dadf30b --- /dev/null +++ b/modules.d/99base/rdsosreport.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +echo 'Generating "/run/initramfs/rdsosreport.txt"' + +[ -d /run/initramfs ] || mkdir -p /run/initramfs + +exec > /run/initramfs/rdsosreport.txt 2>&1 + +PWFILTER='s/\(ftp:\/\/.*\):.*@/\1:*******@/g;s/\(cifs:\/\/.*\):.*@/\1:*******@/g;s/cifspass=[^ ]*/cifspass=*******/g;s/iscsi:.*@/iscsi:******@/g;s/rd.iscsi.password=[^ ]*/rd.iscsi.password=******/g;s/rd.iscsi.in.password=[^ ]*/rd.iscsi.in.password=******/g' +set -x +cat /lib/dracut/dracut-* + +echo "/proc/cmdline" +sed -e "$PWFILTER" /proc/cmdline + +if [ -f /etc/cmdline ]; then + echo "/etc/cmdline" + sed -e "$PWFILTER" /etc/cmdline +fi + +for _i in /etc/cmdline.d/*.conf; do + [ -f "$_i" ] || break + echo "$_i" + sed -e "$PWFILTER" "$_i" +done + +cat /proc/self/mountinfo +cat /proc/mounts + +blkid +blkid -o udev + +ls -l /dev/disk/by* + +for _i in /etc/conf.d/*.conf; do + [ -f "$_i" ] || break + echo "$_i" + sed -e "$PWFILTER" "$_i" +done + +if command -v lvm > /dev/null 2> /dev/null; then + lvm pvdisplay + lvm vgdisplay + lvm lvdisplay +fi + +command -v dmsetup > /dev/null 2> /dev/null && dmsetup ls --tree + +cat /proc/mdstat + +command -v ip > /dev/null 2> /dev/null && ip addr + +if command -v journalctl > /dev/null 2> /dev/null; then + journalctl -ab --no-pager -o short-monotonic | sed -e "$PWFILTER" +else + dmesg | sed -e "$PWFILTER" + if [ -f /run/initramfs/init.log ]; then + echo "/run/initramfs/init.log" + sed -e "$PWFILTER" /run/initramfs/init.log + fi +fi |