Skip to content

Commit

Permalink
Refactor user runtime hooks installation and processing
Browse files Browse the repository at this point in the history
- All hooks are installed in subdirectories of /libexec/hooks
- User hooks are recognized in level-specific subdirectories of a
  zfsbootmenu_hook_root defined for dracut or mkinitcpio
- Deprecate zfsbootmenu_{early_setup,setup,teardown} variables
- Take advantage of zfsbootmenu_hook_root to simplify hooks in container
  builds via zbm-builder.sh
- Simplify zbm.hookdir overrides
  • Loading branch information
ahesford committed Sep 12, 2023
1 parent b4ce00f commit f1a8c20
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 188 deletions.
20 changes: 3 additions & 17 deletions docs/guides/general/container-building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,9 @@ The default behavior of ``zbm-builder.sh`` will:
Custom ZFSBootMenu Hooks
~~~~~~~~~~~~~~~~~~~~~~~~

ZFSBootMenu supports :ref:`custom hooks <zbm-dracut-options>` in three stages:

1. ``early_setup`` hooks run after the ``zfs`` kernel driver has been loaded, but before ZFSBootMenu attempts to import
any pools.
2. ``setup`` hooks run after pools are imported, right before ZFSBootMenu will either boot a default environment or
present a menu.
3. ``teardown`` hooks run immediately before ZFSBootMenu will ``kexec`` the kernel for the selected environment.

When ``zbm-builder.sh`` runs, it will identify custom hooks as executable files in the respective subdirectories of its
build directory:

1. ``hooks.early_setup.d``
2. ``hooks.setup.d``
3. ``hooks.teardown.d``

For each hook directory that contains at least one executable file, ``zbm-builder.sh`` will write custom configuration
snippets for ``dracut`` and ``mkinitcpio`` that will include these files in the output images.
ZFSBootMenu supports :ref:`custom hooks <zbm-dracut-options>` in several stages. When ``zbm-builder.sh`` runs, it will
configure the subdirectory ``hooks`` of the build directory, if it exists, as the value of ``zfsbootmenu_hook_root`` to
allow custom hooks to be included in ZFSBootMenu images.

Fully Customizing Images
~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
115 changes: 68 additions & 47 deletions docs/man/dist/man7/zfsbootmenu.7

Large diffs are not rendered by default.

102 changes: 58 additions & 44 deletions docs/man/zfsbootmenu.7.rst

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dracut/module-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ installkernel() {

install() {
: "${zfsbootmenu_module_root:=/usr/share/zfsbootmenu}"
: "${zfsbootmenu_hook_root:=/etc/zfsbootmenu/hooks}"

# shellcheck disable=SC1091
if ! source "${zfsbootmenu_module_root}/install-helpers.sh" ; then
Expand Down
11 changes: 3 additions & 8 deletions etc/zbm-builder/mkinitcpio.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ HOOKS=(base udev autodetect modconf block filesystems keyboard)
# dracut.conf.d
# mkinitcpio.conf.d
#
# of the build directory for any hook found in the subdirectories
#
# hooks.early_setup.d
# hooks.setup.d
# hooks.teardown.d
#
# of the same build directory. Support for mkinitcpio.conf.d mimics similar
# support for dracut.conf.d built directly into dracut.
# of the build directory for any hooks found in the hooks subdirectory of the
# build directory. Support for mkinitcpio.conf.d mimics similar support for
# dracut.conf.d built directly into dracut.
#
# Note that, inside the container, the build directory will be mounted at
# /build, so reference those paths here.
Expand Down
25 changes: 4 additions & 21 deletions etc/zfsbootmenu/mkinitcpio.conf
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,8 @@ HOOKS=(base udev autodetect modconf block filesystems keyboard zfsbootmenu)
# tested and may break some (non-essential) ZFSBootMenu features.
#zfsbootmenu_miser="no"

# zfsbootmenu_early_setup, zfsbootmenu_setup, zfsbootmenu_teardown
# zfsbootmenu_hook_root
# ZFSBootMenu supports user-provided hooks that may be run at various points in
# the boot process. Set each of these variables to an array of executable files
# that should be installed as user hooks in the provided image. Files that are
# not executable will be ignored. See the zfsbootmenu(7) manual page for more
# details about these hooks.
#
# 'zfsbootmenu_early_setup' hooks are run after ZFS modules are loaded, but
# before ZFSBootMenu attempts to import any pools. Hooks here, for example, can
# unlock LUKS volumes or otherwise manage devices that must be made available
# before a pool can be recognized.
#
# 'zfsbootmenu_setup' hooks are run after pools are imported and right before
# the menu is presented.
#
# 'zfsbootmenu_teardown' hooks are run immediately before a chosen boot
# environment is about to be launched. Any writable ZFS pools will have been
# exported, but read-only pools will generally still be available.
#
#zfsbootmenu_early_setup=()
#zfsbootmenu_setup=()
#zfsbootmenu_teardown=()
# the boot process. Set zfsbootmenu_hook_root to the path of the hooks tree
# that includes a subdirectory for each hook point.
#zfsbootmenu_hook_root="/etc/zfsbootmenu/hooks"
3 changes: 2 additions & 1 deletion initcpio/install/zfsbootmenu
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ create_zbm_entrypoint() {


build() {
local hooks relative _file
local _file

: "${zfsbootmenu_module_root:=/usr/share/zfsbootmenu}"
: "${zfsbootmenu_hook_root:=/etc/zfsbootmenu/hooks}"

# shellcheck disable=SC1091
source "${zfsbootmenu_module_root}/install-helpers.sh" || exit 1
Expand Down
27 changes: 6 additions & 21 deletions zbm-builder.sh
Original file line number Diff line number Diff line change
Expand Up @@ -239,32 +239,17 @@ if ! [ -r "${BUILD_DIRECTORY}"/config.yaml ]; then
fi

# Try to include ZBM hooks in the images by default
for stage in early_setup setup teardown; do
[ -d "${BUILD_DIRECTORY}/hooks.${stage}.d" ] || continue

# Only executable hooks are added to the image
hooks=()
for f in "${BUILD_DIRECTORY}/hooks.${stage}.d"/*; do
[ -x "${f}" ] || continue
hooks+=( "/build/hooks.${stage}.d/${f##*/}" )
done

[ "${#hooks[@]}" -gt 0 ] || continue

hconf="zbm-builder.${stage}.conf"

if [ -d "${BUILD_DIRECTORY}/hooks" ]; then
# Write a dracut configuration snippet
mkdir -p "${BUILD_DIRECTORY}/dracut.conf.d"
echo "zfsbootmenu_${stage}+=\" ${hooks[*]} \"" > "${BUILD_DIRECTORY}/dracut.conf.d/${hconf}"
echo "zfsbootmenu_hook_root=/build/hooks" \
> "${BUILD_DIRECTORY}/dracut.conf.d/user_hooks.conf"

# Write a mkinitcpio configuration snippet
mkdir -p "${BUILD_DIRECTORY}/mkinitcpio.conf.d"
echo "zfsbootmenu_${stage}=(" > "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
for hook in "${hooks[@]}"; do
echo " \"${hook}\"" >> "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
done
echo ")" >> "${BUILD_DIRECTORY}/mkinitcpio.conf.d/${hconf}"
done
echo "zfsbootmenu_hook_root=/build/hooks" \
> "${BUILD_DIRECTORY}/mkinitcpio.conf.d/user_hooks.conf"
fi

# Make `/build` the working directory so relative paths in configs make sense
exec "${PODMAN}" run \
Expand Down
39 changes: 17 additions & 22 deletions zfsbootmenu/install-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -247,31 +247,34 @@ populate_hook_dir() {
shift
[ "$#" -gt 0 ] || return 0

mkdir -p "${BUILDROOT}/libexec/${hlev}" || return 1
mkdir -p "${BUILDROOT}/libexec/hooks/${hlev}" || return 1

ret=0
for hfile in "$@"; do
[ -x "${hfile}" ] || continue
zbm_install_file "${hfile}" "/libexec/${hlev}/${hfile##*/}" || ret=$?
zbm_install_file "${hfile}" "/libexec/hooks/${hlev}/${hfile##*/}" || ret=$?
done

return $ret
}


install_zbm_hooks() {
local hdir hsrc hfile ret
local hdir hsrc hfile ret stages

ret=0

# Install system hooks first
for hdir in early-setup.d setup.d load-key.d boot-env.d teardown.d; do
stages=( early-setup.d setup.d load-key.d boot-sel.d teardown.d )

# Install system hooks first so user options can override them
for hdir in "${stages[@]}"; do
hsrc="${zfsbootmenu_module_root}/hooks/${hdir}"
[ -d "${hsrc}" ] || continue
populate_hook_dir "${hdir}" "${hsrc}"/* || ret=$?
done

# Next, install user hooks to allow them to override system versions
# Install user hooks via deprecated syntax first

# shellcheck disable=SC2154
if [[ "${zfsbootmenu_early_setup@a}" != *a* ]]; then
# shellcheck disable=SC2086
Expand All @@ -288,22 +291,6 @@ install_zbm_hooks() {
populate_hook_dir "setup.d" "${zfsbootmenu_setup[@]}" || ret=$?
fi

# shellcheck disable=SC2154
if [[ "${zfsbootmenu_load_key@a}" != *a* ]]; then
# shellcheck disable=SC2086
populate_hook_dir "load-key.d" ${zfsbootmenu_load_key} || ret=$?
else
populate_hook_dir "load-key.d" "${zfsbootmenu_load_key[@]}" || ret=$?
fi

# shellcheck disable=SC2154
if [[ "${zfsbootmenu_boot_env@a}" != *a* ]]; then
# shellcheck disable=SC2086
populate_hook_dir "boot-env.d" ${zfsbootmenu_boot_env} || ret=$?
else
populate_hook_dir "boot-env.d" "${zfsbootmenu_boot_env[@]}" || ret=$?
fi

# shellcheck disable=SC2154
if [[ "${zfsbootmenu_teardown@a}" != *a* ]]; then
# shellcheck disable=SC2086
Expand All @@ -312,6 +299,14 @@ install_zbm_hooks() {
populate_hook_dir "teardown.d" "${zfsbootmenu_teardown[@]}" || ret=$?
fi

# Install user hooks using the preferred zfsbootmenu_hook_root mechanism
for hdir in "${stages[@]}"; do
# shellcheck disable=SC2154
hsrc="${zfsbootmenu_hook_root}/${hdir}"
[ -d "${hsrc}" ] || continue
populate_hook_dir "${hdir}" "${hsrc}"/* || ret=$?
done

return $ret
}

Expand Down
15 changes: 10 additions & 5 deletions zfsbootmenu/lib/zfsbootmenu-core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ kexec_kernel() {
)

# Run boot-environment hooks, if they exist
env "${hook_envs[@]}" /libexec/zfsbootmenu-run-hooks "boot-env.d"
env "${hook_envs[@]}" /libexec/zfsbootmenu-run-hooks "boot-sel.d"

tput cnorm
tput clear
Expand Down Expand Up @@ -1883,13 +1883,18 @@ import_zbm_hooks() {
return 1
fi

for hdir in early-setup.d setup.d teardown.d; do
hsrc="${hook_mount}/${hook_path}/${hdir}"
for hsrc in "${hook_mount}/${hook_path}"/*; do
[ -d "${hsrc}" ] || continue
mkdir -p "/libexec/${hdir}"
hdir="${hsrc##*/}"

if ! mkdir -p "/libexec/hooks/${hdir}"; then
zwarn "failed to create hook directory ${hdir}"
continue;
fi

for hfile in "${hsrc}"/*; do
[ -f "${hfile}" ] || continue
if ! cp "${hfile}" "/libexec/${hdir}" >/dev/null 2>&1; then
if ! cp "${hfile}" "/libexec/hooks/${hdir}" >/dev/null 2>&1; then
zwarn "failed to copy user hook ${hfile}"
fi
done
Expand Down
4 changes: 2 additions & 2 deletions zfsbootmenu/libexec/zfsbootmenu-run-hooks
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ if [ -z "${hook_stage}" ]; then
exit 1
fi

if [ -r "/libexec/${hook_stage}" ]; then
if [ ! -d "/libexec/hooks/${hook_stage}" ]; then
zdebug "no hook directory for ${hook_stage}"
exit 1
fi

_ran_hook=
for _hook in /libexec/"${hook_stage}"/*; do
for _hook in "/libexec/hooks/${hook_stage}"/*; do
[ -x "${_hook}" ] || continue
zinfo "processing ${_hook}"
"${_hook}"
Expand Down

0 comments on commit f1a8c20

Please sign in to comment.