Skip to content

Commit

Permalink
zfsbootmenu: add zbm.waitfor argument handler
Browse files Browse the repository at this point in the history
With keyfiles being stored on USB drives, the boot process can be
non-deterministic due to intermittently missing /dev entries. Users can
now provide one or more devices that must be present before the pool
import process can start. Unlike dracut, you can simply hit the escape
key to cancel this check.
  • Loading branch information
zdykstra committed Jan 15, 2024
1 parent ad127f3 commit 075cba0
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
8 changes: 8 additions & 0 deletions docs/man/zfsbootmenu.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ These options are set on the kernel command line when booting the initramfs or U

Enable automatic font resizing of the kernel console to normalize the apparent resolution for both low resolution and high resolution displays. This option is enabled by default.

**zbm.waitfor=device,device,...**

Ensure that one or more devices are present before starting the pool import process. Devices may be specified as full paths to device nodes (*e.g.*, **/dev/sda** or **/dev/disk/by-id/wwn-0x500a07510ee65912**) or, for convenience, as a typed indicator of the form **TYPE=VALUE**, which will be expanded internally as

**/dev/disk/by-TYPE/VALUE**

The use of full device paths other than descendants of **/dev/disk/** is fragile and should be avoided.

Deprecated Parameters
---------------------

Expand Down
54 changes: 54 additions & 0 deletions zfsbootmenu/libexec/zfsbootmenu-init
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,60 @@ elif [ ! -e /etc/hostid ]; then
write_hostid "${default_hostid}"
fi

# Wait for devices to show up

if [ -n "${zbm_waitfor_devices}" ]; then
IFS=',' read -r -a user_devices <<<"${zbm_waitfor_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 3 -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

# Import ZBM hooks from an external root, if they exist
if [ -n "${zbm_hook_root}" ]; then
import_zbm_hooks "${zbm_hook_root}"
Expand Down
5 changes: 5 additions & 0 deletions zfsbootmenu/pre-init/zfsbootmenu-parse-commandline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ if zbm_prefer_pool=$( get_zbm_arg zbm.prefer ) ; then
zinfo "preferring ${zbm_prefer_pool} for bootfs"
fi

zbm_waitfor_devices=
if zbm_waitfor_devices=$( get_zbm_arg zbm.waitfor ) ; then
zinfo "system will wait for ${zbm_waitfor_devices}"
fi

# pool! : this pool must be imported before all others
# pool!!: this pool, and no others, must be imported

Expand Down
1 change: 1 addition & 0 deletions zfsbootmenu/pre-init/zfsbootmenu-preinit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export zbm_sort='${zbm_sort}'
export zbm_set_hostid='${zbm_set_hostid}'
export zbm_import_delay='${zbm_import_delay}'
export zbm_hook_root='${zbm_hook_root}'
export zbm_waitfor_devices='${zbm_waitfor_devices}'
export control_term='${control_term}'
# END additions by zfsbootmenu-preinit.sh
EOF
Expand Down

0 comments on commit 075cba0

Please sign in to comment.