Skip to content

Commit

Permalink
init: split initialization into quasi-idempotent parts
Browse files Browse the repository at this point in the history
  • Loading branch information
ahesford committed Mar 1, 2024
1 parent 0b08977 commit 8dfb713
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 244 deletions.
36 changes: 36 additions & 0 deletions zfsbootmenu/init.d/10-kmods
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

[ "${ZFSBOOTMENU_INITIALIZATION}" = "yes" ] || return 0

# Attempt to load spl normally
if ! _modload="$( modprobe spl 2>&1 )" ; then
zdebug "${_modload}"

# Capture the filename for spl.ko
_modfilename="$( modinfo -F filename spl )"

if [ -n "${_modfilename}" ] ; then
zinfo "loading ${_modfilename}"

# Load with a hostid of 0, so that /etc/hostid takes precedence and
# invalid spl.spl_hostid values are ignored

# There's a race condition between udev and insmod spl
# insmod failures are no longer a hard failure - they can be because
# 1. spl.ko is already loaded because of the race condition
# 2. there's an invalid parameter or value for spl.ko

if ! _modload="$( insmod "${_modfilename}" "spl_hostid=0" 2>&1 )" ; then
zwarn "${_modload}"
zwarn "unable to load SPL kernel module; attempting to load ZFS anyway"
fi
fi
fi

if ! _modload="$( modprobe zfs 2>&1 )" ; then
zerror "${_modload}"
emergency_shell "unable to load ZFS kernel modules"
fi

udevadm settle
21 changes: 21 additions & 0 deletions zfsbootmenu/init.d/20-hostid
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

[ "${ZFSBOOTMENU_INITIALIZATION}" = "yes" ] || return 0

# Write out a default or overridden hostid
if [ -n "${spl_hostid}" ] ; then
if write_hostid "${spl_hostid}" ; then
zinfo "writing /etc/hostid from command line: ${spl_hostid}"
else
# write_hostid logs an error for us, just note the new value
# shellcheck disable=SC2154
write_hostid "${default_hostid}"
zinfo "defaulting hostid to ${default_hostid}"
fi
elif [ ! -e /etc/hostid ]; then
zinfo "no hostid found on kernel command line or /etc/hostid"
# shellcheck disable=SC2154
zinfo "defaulting hostid to ${default_hostid}"
write_hostid "${default_hostid}"
fi
57 changes: 57 additions & 0 deletions zfsbootmenu/init.d/30-device-wait
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

[ "${ZFSBOOTMENU_INITIALIZATION}" = "yes" ] || return 0

# Wait for devices to show up
if [ -n "${zbm_wait_for_devices}" ]; then
IFS=',' read -r -a user_devices <<<"${zbm_wait_for_devices}"
while true; do
FOUND=0
EXPECTED=0
missing=()

for device in "${user_devices[@]}"; do
case "${device}" in
/dev/*)
((EXPECTED=EXPECTED+1))
if [ -e "${device}" ] ; then
((FOUND=FOUND+1))
else
missing+=( "$device" )
fi
;;
*=*)
((EXPECTED=EXPECTED+1))
path_prefix="/dev/disk/by-${device%=*}"
checkfor="${path_prefix,,}/${device##*=}"
if [ -e "${checkfor}" ] ; then
((FOUND=FOUND+1))
else
missing+=( "$device" )
fi
;;
*)
zerror "malformed device: '${device}'"
;;
esac
done

if [ ${FOUND} -eq ${EXPECTED} ]; then
break
else
if ! timed_prompt -d "${zbm_retry_delay:-5}" \
-e "to cancel" -m "" \
-m "$( colorize red "One or more required devices are missing" )" \
-p "retrying in $( colorize yellow "%0.2d" ) seconds" ; then
for dev in "${missing[@]}" ; do
zerror "required device '${dev}' not found"
done

break
fi
fi
done

unset FOUND EXPECTED device path_prefix checkfor
fi
31 changes: 31 additions & 0 deletions zfsbootmenu/init.d/40-early-hooks
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

[ "${ZFSBOOTMENU_INITIALIZATION}" = "yes" ] || return 0

# Import ZBM hooks from an external root, if they exist
if [ -n "${zbm_hook_root}" ]; then
import_zbm_hooks "${zbm_hook_root}"
fi

# Remove the executable bit from any hooks in the skip list
if zbm_skip_hooks="$( get_zbm_arg zbm.skip_hooks )" && [ -n "${zbm_skip_hooks}" ]; then
zdebug "processing hook skip directives: ${zbm_skip_hooks}"
IFS=',' read -r -a zbm_skip_hooks <<<"${zbm_skip_hooks}"
for _skip in "${zbm_skip_hooks[@]}"; do
[ -n "${_skip}" ] || continue

for _hook in /libexec/hooks/*.d/*; do
[ -e "${_hook}" ] || continue
if [ "${_skip}" = "${_hook##*/}" ]; then
zinfo "Disabling hook: ${_hook}"
chmod 000 "${_hook}"
fi
done
done
unset _hook _skip
fi

# Run early setup hooks, if they exist
tput clear
/libexec/zfsbootmenu-run-hooks -once "early-setup.d"
117 changes: 117 additions & 0 deletions zfsbootmenu/init.d/50-import-pools
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab

[ "${ZFSBOOTMENU_INITIALIZATION}" = "yes" ] || return 0

# If a boot pool is specified, that will be tried first
# shellcheck disable=SC2154
try_pool="${zbm_prefer_pool}"
zbm_import_attempt=0

while true; do
if [ -n "${try_pool}" ]; then
zdebug "attempting to import preferred pool ${try_pool}"
fi

read_write='' import_pool "${try_pool}"

# shellcheck disable=SC2154
if check_for_pools; then
if [ "${zbm_require_pool}" = "only" ]; then
zdebug "only importing ${try_pool}"
break
elif [ -n "${try_pool}" ]; then
# If a single pool was requested and imported, try importing others
try_pool=""
continue
else
# Otherwise, all possible pools were imported, nothing more to try
break
fi
elif [ "${import_policy}" == "hostid" ] && poolmatch="$( match_hostid "${try_pool}" )"; then
zdebug "match_hostid returned: ${poolmatch}"

spl_hostid="${poolmatch##*;}"

export spl_hostid

# Store the hostid to use for for KCL overrides
echo -n "$spl_hostid" > "${BASE}/spl_hostid"

# If match_hostid succeeds, it has imported *a* pool...
if [ -n "${try_pool}" ] && [ "${zbm_require_pool}" = "only" ]; then
# In "only" pool mode, the import was the sole pool desired; nothing more to do
break
else
# Otherwise, try one more pass to pick up other pools matching this hostid
try_pool=""
continue
fi
elif [ -n "${try_pool}" ] && [ -z "${zbm_require_pool}" ]; then
# If a specific pool was tried unsuccessfully but is not a requirement,
# allow another pass to try any other importable pools
try_pool=""
continue
fi

zbm_import_attempt="$((zbm_import_attempt + 1))"
zinfo "unable to import a pool on attempt ${zbm_import_attempt}"

# Just keep retrying after a delay until the user presses ESC
if timed_prompt -d "${zbm_retry_delay:-5}" \
-p "Unable to import $( colorize magenta "${try_pool:-pool}" ), retrying in $( colorize yellow "%0.2d" ) seconds" \
-r "to retry immediately" \
-e "for a recovery shell"; then
continue
fi

log_unimportable
# Allow the user to attempt recovery
emergency_shell "unable to successfully import a pool"
done

# restrict read-write access to any unhealthy pools
while IFS=$'\t' read -r _pool _health; do
if [ "${_health}" != "ONLINE" ]; then
echo "${_pool}" >> "${BASE}/degraded"
zerror "prohibiting read/write operations on ${_pool}"
fi
done <<<"$( zpool list -H -o name,health )"
unset _pool _health

zdebug && zdebug "$( zreport )"

unsupported=0
while IFS=$'\t' read -r _pool _property; do
if [[ "${_property}" =~ "unsupported@" ]]; then
zerror "unsupported property: ${_property}"
if ! grep -q "${_pool}" "${BASE}/degraded" >/dev/null 2>&1 ; then
echo "${_pool}" >> "${BASE}/degraded"
fi
unsupported=1
fi
done <<<"$( zpool get all -H -o name,property )"

if [ "${unsupported}" -ne 0 ]; then
zerror "Unsupported features detected, Upgrade ZFS modules in ZFSBootMenu with generate-zbm"
timed_prompt -m "$( colorize red 'Unsupported features detected')" \
-m "$( colorize red 'Upgrade ZFS modules in ZFSBootMenu with generate-zbm')"
fi
unset unsupported

# Attempt to find the bootfs property
# shellcheck disable=SC2086
while read -r _bootfs; do
if [ "${_bootfs}" = "-" ]; then
BOOTFS=
else
BOOTFS="${_bootfs}"
break
fi
done <<<"$( zpool list -H -o bootfs "${zbm_prefer_pool:---}" )"
unset _bootfs

if [ -n "${BOOTFS}" ]; then
export BOOTFS
echo "${BOOTFS}" > "${BASE}/bootfs"
fi
4 changes: 4 additions & 0 deletions zfsbootmenu/install-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ install_zbm_core() {
done
done

for cfile in "${zfsbootmenu_module_root}/init.d"/*; do
zbm_install_file "${cfile}" "/libexec/init.d/${cfile##*/}" || ret=$?
done

return $ret
}

Expand Down
Loading

0 comments on commit 8dfb713

Please sign in to comment.