zfsbootmenu/dracut/module-setup.sh

233 lines
7.6 KiB
Bash
Raw Normal View History

2019-07-19 05:44:35 +08:00
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab
2019-07-19 05:44:35 +08:00
check() {
# Do not include this module by default; it must be requested
2019-11-03 05:49:59 +08:00
return 255
2019-07-19 05:44:35 +08:00
}
depends() {
echo bash udev-rules
2019-11-03 05:49:59 +08:00
return 0
2019-07-19 05:44:35 +08:00
}
installkernel() {
local mod
# shellcheck disable=SC2154
for mod in "${zfsbootmenu_essential_modules[@]}"; do
if ! instmods -c "${mod}" ; then
dfatal "Required kernel module '${mod}' is missing, aborting image creation!"
exit 1
fi
done
# shellcheck disable=SC2154
for mod in "${zfsbootmenu_optional_modules[@]}"; do
instmods "${mod}"
done
2019-07-19 05:44:35 +08:00
}
install() {
: "${zfsbootmenu_module_root:=/usr/share/zfsbootmenu}"
# shellcheck disable=SC1091
if ! source "${zfsbootmenu_module_root}/install-helpers.sh" ; then
dfatal "Unable to source ${zfsbootmenu_module_root}/install-helpers.sh"
exit 1
fi
# BUILDROOT is an initcpio-ism
# shellcheck disable=SC2154,2034
BUILDROOT="${initdir}"
# shellcheck disable=SC2034
BUILDSTYLE="dracut"
local _rule _exec _ret
# shellcheck disable=SC2154
for _rule in "${zfsbootmenu_udev_rules[@]}"; do
if ! inst_rules "${_rule}"; then
dfatal "failed to install udev rule '${_rule}'"
exit 1
fi
done
# shellcheck disable=SC2154
for _exec in "${zfsbootmenu_essential_binaries[@]}"; do
if ! dracut_install "${_exec}"; then
dfatal "failed to install essential executable '${_exec}'"
exit 1
fi
done
# shellcheck disable=SC2154
for _exec in "${zfsbootmenu_optional_binaries[@]}"; do
if ! dracut_install "${_exec}"; then
dwarning "optional component '${_exec}' not found, omitting from image"
fi
done
2019-11-03 05:49:59 +08:00
# Workaround for zfsonlinux/zfs#4749 by ensuring libgcc_s.so(.1) is included
_ret=0
# If zpool requires libgcc_s.so*, dracut will track and include it
if ! ldd "$( command -v zpool )" | grep -qF 'libgcc_s.so'; then
# On systems with gcc-config (Gentoo, Funtoo, etc.), use it to find libgcc_s
if command -v gcc-config >/dev/null 2>&1; then
dracut_install "/usr/lib/gcc/$(s=$(gcc-config -c); echo "${s%-*}/${s##*-}")/libgcc_s.so.1"
_ret=$?
# Otherwise, use dracut's library installation function to find the right one
elif ! inst_libdir_file "libgcc_s.so*"; then
# If all else fails, just try looking for some gcc arch directory
dracut_install /usr/lib/gcc/*/*/libgcc_s.so*
_ret=$?
fi
fi
if [ ${_ret} -ne 0 ]; then
dfatal "Unable to install libgcc_s.so"
exit 1
2019-11-03 05:49:59 +08:00
fi
# shellcheck disable=SC2154
while read -r doc ; do
relative="${doc//${zfsbootmenu_module_root}\//}"
inst_simple "${doc}" "/usr/share/docs/${relative}"
done <<<"$( find "${zfsbootmenu_module_root}/help-files" -type f )"
compat_dirs=( "/etc/zfs/compatibility.d" "/usr/share/zfs/compatibility.d/" )
for compat_dir in "${compat_dirs[@]}"; do
# shellcheck disable=2164
[ -d "${compat_dir}" ] && tar -cf - "${compat_dir}" | ( cd "${initdir}" ; tar xfp - )
done
_ret=0
# Core ZFSBootMenu functionality
# shellcheck disable=SC2154
for _lib in "${zfsbootmenu_module_root}"/lib/*; do
2021-12-30 02:33:26 +08:00
inst_simple "${_lib}" "/lib/$( basename "${_lib}" )" || _ret=$?
done
# Helper tools not intended for direct human consumption
for _libexec in "${zfsbootmenu_module_root}"/libexec/*; do
2021-12-30 02:33:26 +08:00
inst_simple "${_libexec}" "/libexec/$( basename "${_libexec}" )" || _ret=$?
done
# User-facing utilities, useful for running in a recovery shell
for _bin in "${zfsbootmenu_module_root}"/bin/*; do
2021-12-30 02:33:26 +08:00
inst_simple "${_bin}" "/bin/$( basename "${_bin}" )" || _ret=$?
done
# Hooks necessary to initialize ZBM
inst_hook cmdline 95 "${zfsbootmenu_module_root}/hook/zfsbootmenu-parse-commandline.sh" || _ret=$?
inst_hook pre-mount 90 "${zfsbootmenu_module_root}/hook/zfsbootmenu-preinit.sh" || _ret=$?
# Hooks to force the dracut event loop to fire at least once
# Things like console configuration are done in optional event-loop hooks
inst_hook initqueue/settled 99 "${zfsbootmenu_module_root}/hook/zfsbootmenu-ready-set.sh" || _ret=$?
inst_hook initqueue/finished 99 "${zfsbootmenu_module_root}/hook/zfsbootmenu-ready-chk.sh" || _ret=$?
# optionally enable early Dracut profiling
if [ -n "${dracut_trace_enable}" ]; then
inst_hook cmdline 00 "${zfsbootmenu_module_root}/profiling/profiling-lib.sh"
Enable profiling framework in core ZFSBootMenu tools Create /lib/profiling-lib.sh, which can be sourced by any Bash script. Once sourced, a debug trap is installed that dumps the stack (but not the command being executed) to a serial port, which collects the data on the host side into a log file. After the VM exits, the performance data is converted into a format expected by flamegraph.pl (https://github.com/brendangregg/FlameGraph) and then both a flamegraph and a flamechart are created. If profiling isn't enabled, a no-op /lib/profiling-lib.sh is created. The profiling library must always exist, and must always be sourceable. Optimizations/performance changes include: - setting the loglevel as from the KCL as early as possible, to preserve all log messages > zwarn - validating that the loglevel is an integer, otherwise defaulting to 4 - validating that zbm.timeout/timeout is an integer, otherwise defaulting to 10 - adding re-import guards to libraries - removing the zlog() function and doing the logging work in the zerror/zwarn/... function instead. This means that everything but zdebug can be as lean as possible. - gating a couple serial zdebug calls behind a single zdebug check - added a fast-fail to timed_prompt to return 0 if delay isn't greater than 0. Any default values for delay/prompt are assigned after this test. - add a short-circuit to get_zbm_arg that uses bashre to check if the kcl option string is present at all in the KCL. If it's not, the next option passed to get_zbm_arg is evaluated. - Avoid pre-computing a return value until it's actually needed - cut i18n out of testing builds unless a graphical interface is requested - cut crypt-ssh and network-legacy out of testing builds unless SSH access is requested - cut out 'nvdimm fs-lib rootfs-block dm dmraid lunmask' from testing builds. These can likely be cut from release builds as well. Closes #245
2022-01-09 05:57:24 +08:00
fi
# Install "early setup" hooks
# shellcheck disable=SC2154
if [ -n "${zfsbootmenu_early_setup}" ]; then
for _exec in ${zfsbootmenu_early_setup}; do
if [ -x "${_exec}" ]; then
inst_simple "${_exec}" "/libexec/early-setup.d/$(basename "${_exec}")" || _ret=$?
else
dwarning "setup script (${_exec}) missing or not executable; cannot install"
fi
done
fi
# Install "setup" hooks
# shellcheck disable=SC2154
if [ -n "${zfsbootmenu_setup}" ]; then
for _exec in ${zfsbootmenu_setup}; do
if [ -x "${_exec}" ]; then
inst_simple "${_exec}" "/libexec/setup.d/$(basename "${_exec}")" || _ret=$?
else
dwarning "setup script (${_exec}) missing or not executable; cannot install"
fi
done
fi
# Install "teardown" hooks
# shellcheck disable=SC2154
if [ -n "${zfsbootmenu_teardown}" ]; then
for _exec in ${zfsbootmenu_teardown}; do
if [ -x "${_exec}" ]; then
inst_simple "${_exec}" "/libexec/teardown.d/$(basename "${_exec}")" || _ret=$?
else
dwarning "teardown script (${_exec}) missing or not executable; cannot install"
fi
done
fi
if [ ${_ret} -ne 0 ]; then
dfatal "Unable to install core ZFSBootMenu functions"
exit 1
fi
2019-07-19 05:44:35 +08:00
# vdev_id.conf and hostid files are host-specific
# and do not belong in public release images
if [ -z "${release_build}" ]; then
if [ -e /etc/zfs/vdev_id.conf ]; then
inst /etc/zfs/vdev_id.conf
type mark_hostonly >/dev/null 2>&1 && mark_hostonly /etc/zfs/vdev_id.conf
Add support to discover and assume a hostid, as well as fix arguments passed to a BE (#147) A persistent problem with ZFS and now with ZFSBootMenu is getting spl.spl_hostid lined up on all pool imports. The contents of an initramfs and the kernel command line can influence this value, making it difficult to determine exactly where and how to change it to get a system to boot. To help combat this recurring issue, ZBM now accepts the following options to control importing behavior. * zbm.import_policy=strict: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, drop to a recovery shell. * zbm.import_policy=hostid: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, attempt to scrape the hostid used to import the pool defined in root=zfsbootmenu:POOL=<value> and import the pool. If that pool is missing or can't be imported, any other available pool is scraped for a hostid and attempted to be imported. * zbm.import_policy=force: YOLO mode, just force import a pool. Replaces `zbm.force_import'. Right now, when undefined, zbm.import_policy is set to strict. With this option available, ZBM can now in most cases import any pool - regardless of the hostid of the system that built the initramfs or the EFI executable. Next is to ensure that the boot environment imports the pool correctly. Some ZFS initramfs modules can use spl_hostid to write out /etc/hostid in the initramfs before loading SPL/ZFS modules. Others ... like Arch ... not only do not by default include /etc/hostid in the initramfs, they do not have any methods to line up spl_hostid on the kernel command line with /etc/hostid. However, spl.spl_hostid takes precedence over /etc/hostid, so we can simply set this on the kernel command line. To this end, we now: Suppress spl_hostid if it's part of the discovered command line for a boot environment Suppress spl.spl_hostid if it's part of the discovered command line for a boot environment Insert spl.spl_hostid=0x<hostid> on the command line for each boot environment. (when hostid is 0, insert spl_hostid=0x00000000, to help dracut. spl.spl_hostid=0 is a no-op). Since we know the hostid used to import a pool - either discovered in zfsbootmenu-parse-commandline.sh, or discovered when zbm.import_policy=hostid is set. Suppression / rewriting can be disabled via zbm.set_hostid=0 when booting ZBM. Co-authored-by: Zach Dykstra <dykstra.zachary@gmail.com> Co-authored-by: Andrew J. Hesford <ajh@sideband.org>
2021-02-26 08:29:10 +08:00
fi
# Try to synchronize hostid between host and ZFSBootMenu
#
# DEPRECATION NOTICE: on musl systems, zfs < 2.0 produced a bad hostid in
# dracut images. Unfortunately, this should be replicated for now to ensure
# those images are bootable. After some time, remove this version check.
ZVER="$( zfs version | head -n1 | sed 's/zfs-\(kmod-\)\?//' )"
if [ -n "${ZVER}" ] && printf '%s\n' "${ZVER}" "2.0" | sort -VCr; then
NEWZFS=yes
else
NEWZFS=""
fi
if [ -n "${NEWZFS}" ] && [ -e /etc/hostid ]; then
# With zfs >= 2.0, prefer the hostid file if it exists
inst /etc/hostid
elif HOSTID="$( hostid 2>/dev/null )"; then
# Fall back to `hostid` output when it is nonzero or with zfs < 2.0
if [ -z "${NEWZFS}" ]; then
# In zfs < 2.0, zgenhostid does not provide necessary behavior
echo -ne "\\x${HOSTID:6:2}\\x${HOSTID:4:2}\\x${HOSTID:2:2}\\x${HOSTID:0:2}" > "${initdir}/etc/hostid"
elif [ "${HOSTID}" != "00000000" ]; then
# In zfs >= 2.0, zgenhostid writes the output, but only with nonzero hostid
# shellcheck disable=SC2154
zgenhostid -o "${initdir}/etc/hostid" "${HOSTID}"
Add support to discover and assume a hostid, as well as fix arguments passed to a BE (#147) A persistent problem with ZFS and now with ZFSBootMenu is getting spl.spl_hostid lined up on all pool imports. The contents of an initramfs and the kernel command line can influence this value, making it difficult to determine exactly where and how to change it to get a system to boot. To help combat this recurring issue, ZBM now accepts the following options to control importing behavior. * zbm.import_policy=strict: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, drop to a recovery shell. * zbm.import_policy=hostid: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, attempt to scrape the hostid used to import the pool defined in root=zfsbootmenu:POOL=<value> and import the pool. If that pool is missing or can't be imported, any other available pool is scraped for a hostid and attempted to be imported. * zbm.import_policy=force: YOLO mode, just force import a pool. Replaces `zbm.force_import'. Right now, when undefined, zbm.import_policy is set to strict. With this option available, ZBM can now in most cases import any pool - regardless of the hostid of the system that built the initramfs or the EFI executable. Next is to ensure that the boot environment imports the pool correctly. Some ZFS initramfs modules can use spl_hostid to write out /etc/hostid in the initramfs before loading SPL/ZFS modules. Others ... like Arch ... not only do not by default include /etc/hostid in the initramfs, they do not have any methods to line up spl_hostid on the kernel command line with /etc/hostid. However, spl.spl_hostid takes precedence over /etc/hostid, so we can simply set this on the kernel command line. To this end, we now: Suppress spl_hostid if it's part of the discovered command line for a boot environment Suppress spl.spl_hostid if it's part of the discovered command line for a boot environment Insert spl.spl_hostid=0x<hostid> on the command line for each boot environment. (when hostid is 0, insert spl_hostid=0x00000000, to help dracut. spl.spl_hostid=0 is a no-op). Since we know the hostid used to import a pool - either discovered in zfsbootmenu-parse-commandline.sh, or discovered when zbm.import_policy=hostid is set. Suppression / rewriting can be disabled via zbm.set_hostid=0 when booting ZBM. Co-authored-by: Zach Dykstra <dykstra.zachary@gmail.com> Co-authored-by: Andrew J. Hesford <ajh@sideband.org>
2021-02-26 08:29:10 +08:00
fi
fi
fi
# shellcheck disable=SC2154
if [ -e "${initdir}/etc/hostid" ] && type mark_hostonly >/dev/null 2>&1; then
mark_hostonly /etc/hostid
fi
# Embed a kernel command line in the initramfs
# shellcheck disable=SC2154
if [ -n "${embedded_kcl}" ]; then
echo "export embedded_kcl=\"${embedded_kcl}\"" >> "${initdir}/etc/zfsbootmenu.conf"
fi
# Force rd.hostonly=0 in the KCL for releases, this will purge itself after 99base/init.sh runs
# shellcheck disable=SC2154
if [ -n "${release_build}" ]; then
echo "rd.hostonly=0" > "${initdir}/etc/cmdline.d/hostonly.conf"
fi
create_zbm_conf
create_zbm_profiles
create_zbm_traceconf
2019-07-19 05:44:35 +08:00
}