diff --git a/90zfsbootmenu/module-setup.sh b/90zfsbootmenu/module-setup.sh index 38767b08..c60eafd2 100755 --- a/90zfsbootmenu/module-setup.sh +++ b/90zfsbootmenu/module-setup.sh @@ -82,6 +82,7 @@ install() { "ps" "env" "chmod" + "od" ) for _exec in "${essential_execs[@]}"; do @@ -202,6 +203,20 @@ install() { type mark_hostonly >/dev/null 2>&1 && mark_hostonly /etc/zfs/vdev_id.conf fi + # Determine platform endianness, defaulting to le + ival="$( echo -n 3 | od -tx2 -N2 -An | tr -d '[:space:]' )" + if [ "x${ival}" = "x3300" ]; then + endian="be" + else + if [ "x${ival}" != "x0033" ]; then + warn "unable to determine platform endianness; assuming little-endian" + fi + endian="le" + fi + + # shellcheck disable=SC2154 + echo -n "${endian}" > "${initdir}/etc/byte-order" + # Try to synchronize hostid between host and ZFSBootMenu # # DEPRECATION NOTICE: on musl systems, zfs < 2.0 produced a bad hostid in @@ -222,7 +237,11 @@ install() { if [ -z "${NEWZFS}" ]; then # In zfs < 2.0, zgenhostid does not provide necessary behavior # shellcheck disable=SC2154 - echo -ne "\\x${HOSTID:6:2}\\x${HOSTID:4:2}\\x${HOSTID:2:2}\\x${HOSTID:0:2}" > "${initdir}/etc/hostid" + if [ "${endian}" = "be" ] ; then + echo -ne "\\x${HOSTID:0:2}\\x${HOSTID:2:2}\\x${HOSTID:4:2}\\x${HOSTID:6:2}" > "${initdir}/etc/hostid" + else + echo -ne "\\x${HOSTID:6:2}\\x${HOSTID:4:2}\\x${HOSTID:2:2}\\x${HOSTID:0:2}" > "${initdir}/etc/hostid" + fi elif [ "${HOSTID}" != "00000000" ]; then # In zfs >= 2.0, zgenhostid writes the output, but only with nonzero hostid # shellcheck disable=SC2154 diff --git a/90zfsbootmenu/zfsbootmenu-countdown.sh b/90zfsbootmenu/zfsbootmenu-countdown.sh index 08f1b2dd..de375af1 100755 --- a/90zfsbootmenu/zfsbootmenu-countdown.sh +++ b/90zfsbootmenu/zfsbootmenu-countdown.sh @@ -13,36 +13,15 @@ fi mkdir -p "${BASE}" -# Attempt to import all pools read-only -read_write='' all_pools=yes import_pool - -# Make sure at least one pool can be imported; if not, -# drop to an emergency shell to allow the user to attempt recovery -import_success=0 -while true; do - while IFS=$'\t' read -r _pool _health; do - [ -n "${_pool}" ] || continue - - import_success=1 - if [ "${_health}" != "ONLINE" ]; then - echo "${_pool}" >> "${BASE}/degraded" - fi - done <<<"$( zpool list -H -o name,health )" - - if [ "${import_success}" -ne 1 ]; then - emergency_shell "unable to successfully import a pool" - else - zdebug "$( - echo "zpool list" ; \ - zpool list - )" - zdebug "$( - echo "zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource" ;\ - zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource - )" - break - fi -done +# Write out a default or overridden hostid +if [ -n "${spl_hostid}" ] ; then + zinfo "ZFSBootMenu: writing /etc/hostid from command line: ${spl_hostid}" + write_hostid "${spl_hostid}" +elif [ ! -e /etc/hostid ]; then + zinfo "ZFSBootMenu: no hostid found on kernel command line or /etc/hostid" + zinfo "ZFSBootMenu: defaulting hostid to 00000000" + write_hostid 0 +fi # Prefer a specific pool when checking for a bootfs value # shellcheck disable=SC2154 @@ -52,11 +31,71 @@ else boot_pool="${root}" fi -# Make sure the preferred pool was imported -if [ -n "${boot_pool}" ] && ! zpool list -H -o name "${boot_pool}" >/dev/null 2>&1; then - emergency_shell "\nCannot import requested pool '${boot_pool}'\nType 'exit' to try booting anyway" +# Do a dedicated pass for the preferred pool if one was provided +if [ -n "${boot_pool}" ]; then + first_pass=0 +else + first_pass=1 fi +while true; do + if [ "${first_pass}" -eq 0 ]; then + # Try the preferred pool, exactly once + zdebug "attempting to import preferred pool ${boot_pool}" + try_pool="${boot_pool}" + else + try_pool="" + fi + + first_pass=1 + + read_write='' import_pool "${try_pool}" + + # shellcheck disable=SC2154 + if check_for_pools; then + if [ -n "${try_pool}" ]; then + # If a single pool was requested and imported, try again for the others + 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" + + # Retry the cycle with a matched hostid + continue + fi + + # 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 )" + +zdebug "$( + echo "zpool list" ; \ + zpool list +)" + +zdebug "$( + echo "zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource" ;\ + zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource +)" + unsupported=0 while IFS=$'\t' read -r _pool _property; do if [[ "${_property}" =~ "unsupported@" ]]; then diff --git a/90zfsbootmenu/zfsbootmenu-exec.sh b/90zfsbootmenu/zfsbootmenu-exec.sh index 641d985c..8d7a78a8 100755 --- a/90zfsbootmenu/zfsbootmenu-exec.sh +++ b/90zfsbootmenu/zfsbootmenu-exec.sh @@ -1,12 +1,14 @@ #!/bin/bash # vim: softtabstop=2 shiftwidth=2 expandtab +export endian export spl_hostid -export force_import +export import_policy export menu_timeout export loglevel export root export zbm_sort +export zbm_set_hostid # Disable all kernel messages to the console echo 0 > /proc/sys/kernel/printk diff --git a/90zfsbootmenu/zfsbootmenu-lib.sh b/90zfsbootmenu/zfsbootmenu-lib.sh index 0261e00c..f157bb09 100755 --- a/90zfsbootmenu/zfsbootmenu-lib.sh +++ b/90zfsbootmenu/zfsbootmenu-lib.sh @@ -22,7 +22,7 @@ zlog() { _func="${FUNCNAME[2]}" WIDTH="$( tput cols )" - + # Only add script/function tracing to debug messages if [ "${1}" -eq 7 ]; then echo -e "<${1}>ZBM:\033[0;33m${_script}[$$]\033[0;31m:${_func}()\033[0m: ${2}" | fold -s -w "${WIDTH}" > /dev/kmsg @@ -44,6 +44,7 @@ zinfo() { } zwarn() { + : > "${BASE}/have_warnings" zlog 4 "$@" } @@ -105,6 +106,152 @@ center_string() { printf "%*s" $(( (${#1} + _WIDTH ) / 2)) "${1}" } +# arg1: hostid, as hex number without leading "0x" +# prints: nothing +# returns: 0 on successful write, 1 on error + +write_hostid() { + local hostid ret + + # Normalize the hostid + if ! hostid="$( printf "%08x" "0x${1:-0}" 2>/dev/null )"; then + zerror "invalid hostid $1" + return 1 + fi + + # shellcheck disable=SC2154 + if [ "${endian}" = "be" ]; then + # Write in big-endian format + zdebug "writing hostid ${hostid} to /etc/hostid (big-endian)" + echo -ne "\\x${hostid:0:2}\\x${hostid:2:2}\\x${hostid:4:2}\\x${hostid:6:2}" > "/etc/hostid" + ret=$? + else + zdebug "writing hostid ${hostid} to /etc/hostid (little-endian)" + echo -ne "\\x${hostid:6:2}\\x${hostid:4:2}\\x${hostid:2:2}\\x${hostid:0:2}" > "/etc/hostid" + ret=$? + fi + + return ${ret} +} + +# args: no arguments +# prints: hostid used by the SPL kmod, as hex with 0x prefix +# returns: 0 on successful read, 1 on failure + +get_spl_hostid() { + local spl_hostid + + # Prefer the module parameter if it exists and is nonzero + if [ -r /sys/module/spl/parameters/spl_hostid ]; then + read -r spl_hostid < /sys/module/spl/parameters/spl_hostid + if [ "${spl_hostid}" -ne 0 ]; then + # Value is decimal, convert to hex for consistency + zdebug "hostid from spl.spl_hostid: ${spl_hostid}" + printf "0x%08x" "${spl_hostid}" + return 0 + fi + fi + + # Otherwise look to /etc/hostid, if possible + if [ -r /etc/hostid ] && command -v od >/dev/null 2>&1; then + spl_hostid="$( od -tx4 -N4 -An /etc/hostid 2>/dev/null | tr -d '[:space:]' )" + if [ -n "${spl_hostid}" ]; then + zdebug "hostid from /etc/hostid: ${spl_hostid}" + echo -n "0x${spl_hostid}" + return 0 + fi + fi + + # Finally, fall back to ${BASE}/spl_hostid if a host match was performed + if [ -r "${BASE}/spl_hostid" ]; then + read -r spl_hostid < "${BASE}/spl_hostid" + if [ -n "${spl_hostid}" ]; then + zdebug "hostid from ${BASE}/spl_hostid: ${spl_hostid}" + echo -n "0x${spl_hostid}" + return 0 + fi + fi + + return 1 +} + + +# arg1: optional specific pool to inspect +# prints: ; +# returns: 0 on successful pool import, 1 on failure + +match_hostid() { + local importable pool state hostid single + importable=() + + single="${1}" + + if [ -n "${single}" ]; then + importable+=( "${single}" ) + else + while read -r line; do + case "$line" in + pool*) + pool="${line#pool: }" + ;; + state*) + state="${line#state: }" + # shellcheck disable=SC2154 + if [ "${state}" == "ONLINE" ] && [ -n "${pool}" ] && [ "${pool}" != "${root}" ]; then + importable+=("${pool}") + pool="" + fi + ;; + esac + done <<<"$( zpool import )" + fi + + zdebug "importable pools: ${importable[*]}" + + for pool in "${importable[@]}"; do + zdebug "trying to import: ${pool}" + hostid="$( zpool import -o readonly=on -N "${pool}" 2>&1 | grep -E -o "hostid=[A-Za-z0-9]{1,8}")" + + if [ -n "${hostid}" ]; then + hostid="${hostid##*=}" + zdebug "discovered pool owner hostid: ${hostid}" + else + zdebug "unable to scrape hostid for ${pool}, skipping" + continue + fi + + if ! write_hostid "${hostid}"; then + zdebug "failed to set hostid ${hostid}, skipping import of pool ${pool}" + continue + fi + + if read_write='' import_pool "${pool}"; then + zdebug "successfully imported ${pool}" + + zwarn "imported ${pool} with assumed hostid ${hostid}" + zwarn "set spl_hostid=${hostid} on ZBM KCL or regenerate with corrected /etc/hostid" + + echo "${pool};${hostid}" + return 0 + fi + done + + # no pools could be imported, we failed to match a hostid + return 1 +} + +# args: none +# prints: nothing +# returns: 0 if at least one pool is available + +check_for_pools() { + while read -r _pool ; do + [ -n "${_pool}" ] && return 0 + done <<<"$( zpool list -H -o name )" + + return 1 +} + # arg1: ZFS filesystem name # prints: mountpoint # returns: 0 on success @@ -276,7 +423,7 @@ draw_be() { zdebug "using environment file: ${env}" - header="$( header_wrap "[ENTER] boot" "[ESC] refresh view" "[CTRL+H] help" "[CTRL+L] error log" "" \ + header="$( header_wrap "[ENTER] boot" "[ESC] refresh view" "[CTRL+H] help" "[CTRL+L] view logs" "" \ "[CTRL+E] edit kcl" "[CTRL+K] kernels" "[CTRL+D] set bootfs" "[CTRL+S] snapshots" "" \ "[CTRL+I] interactive chroot" "[CTRL+R] recovery shell" "[CTRL+P] pool status" )" @@ -319,7 +466,7 @@ draw_kernel() { zdebug "using kernels file: ${_kernels}" header="$( header_wrap \ - "[ENTER] boot" "[ESC] back" "" "[CTRL+D] set default" "[CTRL+H] help" "[CTRL+L] error log" )" + "[ENTER] boot" "[ESC] back" "" "[CTRL+D] set default" "[CTRL+H] help" "[CTRL+L] view logs" )" expects="--expect=alt-d" @@ -356,7 +503,7 @@ draw_snapshots() { sort_key="$( get_sort_key )" header="$( header_wrap \ - "[ENTER] duplicate" "[ESC] back" "[CTRL+H] help" "[CTRL+L] error log" "" \ + "[ENTER] duplicate" "[ESC] back" "[CTRL+H] help" "[CTRL+L] view logs" "" \ "[CTRL+X] clone and promote" "[CTRL+C] clone only" "" \ "[CTRL+I] interactive chroot" "[CTRL+D] show diff" )" @@ -436,7 +583,7 @@ draw_pool_status() { # Wrap to half width to avoid the preview window hdr_width="$(( ( $( tput cols ) / 2 ) - 4 ))" header="$( wrap_width="$hdr_width" header_wrap \ - "[ESC] back" "" "[CTRL+R] rewind checkpoint" "" "[CTRL+H] help" "[CTRL+L] error log" )" + "[ESC] back" "" "[CTRL+R] rewind checkpoint" "" "[CTRL+H] help" "[CTRL+L] view logs" )" if ! selected="$( zpool list -H -o name | HELP_SECTION=POOL ${FUZZYSEL} \ @@ -981,6 +1128,54 @@ preload_be_cmdline() { fi } +# arg1: key(and associated value) to suppress from KCL +# arg2..argN: kernel command line +# prints: supressed kernel command line +# returns: 0 on success + +suppress_kcl_arg() { + arg=$1 + shift + + if [ -z "${arg}" ]; then + echo "$*" + return 0 + fi + + awk <<< "$*" ' + BEGIN { + quot = 0; + supp = 0; + ORS = " "; + } + + { + for (i=1; i <= NF; i++) { + if ( quot == 0 ) { + # If unquoted, determine if output should be suppressed + if ( $(i) ~ /^'"${arg}"'=/ ) { + # Suppress unwanted argument + supp = 1; + } else { + # Nothing else is suppressed + supp = 0; + } + } + + # If output is not suppressed, print the field + if ( supp == 0 && length($(i)) > 0 ) { + print $(i); + } + + # If an odd number of quotes are in this field, toggle quoting + if ( gsub(/"/, "\"", $(i)) % 2 == 1 ) { + quot = (quot + 1) % 2; + } + } + } + ' +} + # arg1: ZFS filesystem # prints: kernel command line arguments # returns: nothing @@ -1012,70 +1207,64 @@ load_be_cmdline() { if [ -e "${BASE}/noresume" ]; then zdebug "${BASE}/noresume set, processing ${zfsbe_args}" # Must replace resume= arguments and append a noresume - zfsbe_args="$( awk <<< "${zfsbe_args}" ' - BEGIN { - quot = 0; - supp = 0; - ORS = " "; - } - - { - for (i=1; i <= NF; i++) { - if ( quot == 0 ) { - # If unquoted, determine if output should be suppressed - if ( $(i) ~ /^resume=/ ) { - # Argument starts with "resume=", suppress - supp = 1; - } else { - # Nothing else is suppressed - supp = 0; - } - } - - # If output is not suppressed, print the field - if ( supp == 0 && length($(i)) > 0 ) { - print $(i); - } + zfsbe_args="$( suppress_kcl_arg resume "${zfsbe_args}" ) noresume" + fi - # If an odd number of quotes are in this field, toggle quoting - if ( gsub(/"/, "\"", $(i)) % 2 == 1 ) { - quot = (quot + 1) % 2; - } - } - printf "noresume"; - } - ' )" + # shellcheck disable=SC2154 + if [ "${zbm_set_hostid}" -eq 1 ] && spl_hostid="$( get_spl_hostid )"; then + zdebug "overriding spl_hostid and spl.spl_hostid in: ${zfsbe_args}" + zfsbe_args="$( suppress_kcl_arg spl_hostid "${zfsbe_args}" )" + zfsbe_args="$( suppress_kcl_arg spl.spl_hostid "${zfsbe_args}" )" + + if [ "x${spl_hostid}" = "x0x00000000" ]; then + # spl.spl_hostid=0 is a no-op; imports fall back to /etc/hostid. + # Dracut writes spl_hostid to /etc/hostid. to yield expected results. + # Others (initramfs-tools, mkinitcpio) ignore this, but there isn't much + # else that can be done with those systems. + zfsbe_args+=" spl_hostid=00000000" + else + # Using spl.spl_hostid will set a module parameter which takes precedence + # over any /etc/hostid and should produce expected behavior in all systems + zfsbe_args+=" spl.spl_hostid=${spl_hostid}" + fi fi zdebug "processed commandline: ${zfsbe_args}" echo "${zfsbe_args}" } -# arg1: pool name +# arg1: pool name, empty to import all # prints: nothing # returns: 0 on success, 1 on failure # Accepted environment variables -# force_import=1: enable force importing of a pool +# import_policy=force: enable force importing of a pool # read_write=1: import read-write, defaults to read-only # rewind_to_checkpoint=1: enable --rewind-to-checkpoint -# all_pools=1: import all pools instead of a single specific pool import_pool() { local pool import_args pool="${1}" + + #shellcheck disable=SC2154 if [ -n "${pool}" ]; then zdebug "pool set to ${pool}" + elif [ -n "${rewind_to_checkpoint}" ]; then + zerror "rewind only works on a specific pool" + return 1 + else + zdebug "attempting to import all pools" + pool="-a" fi # Import /never/ mounts filesystems import_args=( "-N" ) # shellcheck disable=SC2154 - if [ -n "${force_import}" ]; then + if [ "${import_policy}" == "force" ]; then import_args+=( "-f" ) - zdebug "force_import set: ${force_import}" + zdebug "import_policy set: ${import_policy}" fi # shellcheck disable=SC2154 @@ -1093,19 +1282,18 @@ import_pool() { zdebug "rewind_to_checkpoint set: ${rewind_to_checkpoint}" fi - # shellcheck disable=SC2154 - if [ -n "${all_pools}" ]; then - import_args+=( "-a" ) - pool='' - zdebug "all_pools set: ${all_pools}" - fi - zdebug "zpool import arguments: ${import_args[*]} ${pool}" - # shellcheck disable=SC2086 - status="$( zpool import "${import_args[@]}" ${pool} >/dev/null 2>&1 )" + + zpool import "${import_args[@]}" "${pool}" >/dev/null 2>&1 ret=$? - zdebug "import process return: ${ret}" + if [ "$ret" -eq 0 ]; then + zdebug "successful pool import" + else + spl_hostid="$( get_spl_hostid )" + zdebug "import process failed with code ${ret}, apparent hostid ${spl_hostid:-unknown}" + fi + return ${ret} } @@ -1774,7 +1962,7 @@ emergency_shell() { echo -n "Launching emergency shell: " echo -e "${message}\n" - /bin/bash --rcfile <( test -f /lib/zfsbootmenu-lib.sh && echo "source /lib/zfsbootmenu-lib.sh" ) + /bin/bash --rcfile <( test -f /lib/zfsbootmenu-lib.sh && echo "source /lib/zfsbootmenu-lib.sh" ) } # prints: nothing diff --git a/90zfsbootmenu/zfsbootmenu-parse-commandline.sh b/90zfsbootmenu/zfsbootmenu-parse-commandline.sh index 833b6f80..6467a20e 100755 --- a/90zfsbootmenu/zfsbootmenu-parse-commandline.sh +++ b/90zfsbootmenu/zfsbootmenu-parse-commandline.sh @@ -4,17 +4,18 @@ # shellcheck disable=SC1091 . /lib/dracut-lib.sh +if [ -r "/etc/byte-order" ]; then + read -r endian < "/etc/byte-order" +fi + +if [ -z "${endian}" ]; then + warn "unable to determine platform endianness; assuming little-endian" + endian="le" +fi + # Let the command line override our host id. +# shellcheck disable=SC2034 spl_hostid=$(getarg spl_hostid=) -if [ -n "${spl_hostid}" ] ; then - info "ZFSBootMenu: using hostid from command line: ${spl_hostid}" - echo -ne "\\x${spl_hostid:6:2}\\x${spl_hostid:4:2}\\x${spl_hostid:2:2}\\x${spl_hostid:0:2}" >/etc/hostid -elif [ -f "/etc/hostid" ] ; then - info "ZFSBootMenu: using hostid from /etc/hostid: $(hostid)" -else - warn "ZFSBootMenu: no hostid found on kernel command line or /etc/hostid" - warn "ZFSBootMenu: pools may not import correctly" -fi # Use the last defined console= to control menu output control_term=$( getarg console=) @@ -27,20 +28,49 @@ else fi # Use loglevel to determine logging to /dev/kmsg +min_logging=4 loglevel=$( getarg loglevel=) if [ -n "${loglevel}" ]; then - # minimum log level of 3, so we never lose error messages - [ "${loglevel}" -ge 3 ] || loglevel=3 + # minimum log level of 4, so we never lose error or warning messages + [ "${loglevel}" -ge ${min_logging} ] || loglevel=${min_logging} info "ZFSBootMenu: setting log level from command line: ${loglevel}" else - loglevel=3 + loglevel=${min_logging} fi -# Force import pools only when explicitly told to do so -if getargbool 0 zbm.force_import -d force_import ; then - # shellcheck disable=SC2034 - force_import="yes" - info "ZFSBootMenu: enabling force import of ZFS pools" +# hostid - discover the hostid used to import a pool on failure, assume it +# force - append -f to zpool import +# strict - legacy behavior, drop to an emergency shell on failure + +import_policy=$( getarg zbm.import_policy ) +if [ -n "${import_policy}" ]; then + case "${import_policy}" in + hostid) + if [ "${endian}" = "be" ]; then + info "ZFSBootMenu: invalid option for big endian systems" + info "ZFSBootMenu: setting import_policy to strict" + import_policy="strict" + else + info "ZFSBootMenu: setting import_policy to hostid matching" + fi + ;; + force) + info "ZFSBootMenu: setting import_policy to force" + ;; + strict) + info "ZFSBootMenu: setting import_policy to strict" + ;; + *) + info "ZFSBootMenu: unknown import policy ${import_policy}, defaulting to strict" + import_policy="strict" + ;; + esac +elif getargbool 0 zbm.force_import -d force_import ; then + import_policy="force" + info "ZFSBootMenu: setting import_policy to force" +else + info "ZFSBootMenu: defaulting import_policy to strict" + import_policy="strict" fi # zbm.timeout= overrides timeout= @@ -98,10 +128,28 @@ fi # Turn on tmux integrations # shellcheck disable=SC2034 if getargbool 0 zbm.tmux ; then - zbm_tmux="yes" + zbm_tmux=1 info "ZFSBootMenu: enabling tmux integrations" fi +# shellcheck disable=SC2034 +if [ "${endian}" = "be" ]; then + zbm_set_hostid=0 + info "ZFSBootMenu: big endian detected, disabling automatic replacement of spl_hostid" +elif getargbool 0 zbm.set_hostid ; then + zbm_set_hostid=0 + info "ZFSBootMenu: disabling automatic replacement of spl_hostid" +else + zbm_set_hostid=1 + info "ZFSBootMenu: defaulting automatic replacement of spl_hostid to on" +fi + +# rewrite root= +prefer=$( getarg zbm.prefer ) +if [ -n "${prefer}" ]; then + root="zfsbootmenu:POOL=${prefer}" +fi + wait_for_zfs=0 case "${root}" in ""|zfsbootmenu|zfsbootmenu:) diff --git a/90zfsbootmenu/zfsbootmenu-preview.sh b/90zfsbootmenu/zfsbootmenu-preview.sh index 7d781ce5..7109cdb4 100755 --- a/90zfsbootmenu/zfsbootmenu-preview.sh +++ b/90zfsbootmenu/zfsbootmenu-preview.sh @@ -35,6 +35,9 @@ selected_env_str="$( center_string "${ENV} (${_DEFAULT}${_readonly}) - ${selecte if [ -f "${BASE}/have_errors" ]; then selected_env_str="${selected_env_str:3}" colorize "red" "[!]" +elif [ -f "${BASE}/have_warnings" ]; then + selected_env_str="${selected_env_str:3}" + colorize "yellow" "[!]" fi colorize "${_COLOR}" "${selected_env_str}\n" diff --git a/90zfsbootmenu/zfsbootmenu.sh b/90zfsbootmenu/zfsbootmenu.sh index d002f1d4..58b6b0d7 100755 --- a/90zfsbootmenu/zfsbootmenu.sh +++ b/90zfsbootmenu/zfsbootmenu.sh @@ -61,9 +61,9 @@ fuzzy_default_options=( "--ansi" "--no-clear" "--bind" '"alt-h:execute[ /libexec/zfsbootmenu-help -L ${HELP_SECTION:-MAIN} ]"' "--bind" '"ctrl-h:execute[ /libexec/zfsbootmenu-help -L ${HELP_SECTION:-MAIN} ]"' "--bind" '"ctrl-alt-h:execute[ /libexec/zfsbootmenu-help -L ${HELP_SECTION:-MAIN} ]"' - "--bind" '"alt-l:execute[ /bin/zlogtail -l err -F user -c ]+refresh-preview"' - "--bind" '"ctrl-l:execute[ /bin/zlogtail -l err -F user -c ]+refresh-preview"' - "--bind" '"ctrl-alt-l:execute[ /bin/zlogtail -l err -F user -c ]+refresh-preview"' ) + "--bind" '"alt-l:execute[ /bin/zlogtail -l warn -F user,daemon -c ]+refresh-preview"' + "--bind" '"ctrl-l:execute[ /bin/zlogtail -l warn -F user,daemon -c ]+refresh-preview"' + "--bind" '"ctrl-alt-l:execute[ /bin/zlogtail -l warn -F user,daemon -c ]+refresh-preview"' ) if command -v fzf >/dev/null 2>&1; then zdebug "using fzf for pager" export FUZZYSEL=fzf diff --git a/90zfsbootmenu/zlogtail.sh b/90zfsbootmenu/zlogtail.sh index a4ce41e1..ac0a829c 100755 --- a/90zfsbootmenu/zlogtail.sh +++ b/90zfsbootmenu/zlogtail.sh @@ -6,7 +6,7 @@ trap 'rm -f ${PID_FILE}' EXIT #shellcheck disable=SC2154 LOG_LEVEL="0,1,2,3,4,5,6,7" -FACILITY="kern,user" +FACILITY="kern,user,daemon" FOLLOW="" ALLOW_EXIT=1 while getopts "cfnl:F:" opt; do @@ -22,6 +22,7 @@ while getopts "cfnl:F:" opt; do ;; c) [ -f "${BASE}/have_errors" ] && rm "${BASE}/have_errors" + [ -f "${BASE}/have_warnings" ] && rm "${BASE}/have_warnings" ;; F) FACILITY="${OPTARG}" diff --git a/pod/zfsbootmenu.7.pod b/pod/zfsbootmenu.7.pod index 1241ce8a..ae3db803 100644 --- a/pod/zfsbootmenu.7.pod +++ b/pod/zfsbootmenu.7.pod @@ -12,25 +12,39 @@ These options are set on the kernel command line when booting the initramfs or U =over 4 -=item BpoolE> +=item BhostidE> -By default, ZFSBootMenu will look for the I property on the first pool it imports to select the default boot environment. If you have multiple pools, substitute the name of your preferred pool for BpoolE> in the argument BpoolE>. +When creating an initramfs or UEFI bundle, the I from the system is copied into the target. If this image will be used on another system with a different hostid, replace BhostidE> with the desired hostid, as an eight-digit hexadecimal number, to override the value contained within the image. -=item BhostidE> +=item BpoolE> -When creating an initramfs or UEFI bundle, the I from the system is copied into the target. If this image will be used on another system with a different I, it can be overridden with this option. +By default, ZFSBootMenu will look for the I property on the first imported pool (sorted alphabetically) to select the default boot environment. If you have multiple pools, replace BpoolE> with the name of your preferred pool to override the default. -Replace BhostidE> with an eight-digit hexadecimal number. +=item B -=item B +This option controls how the pool import process should take place. + +=over 2 + +=item B + +Set this option to allow run-time reconfiguration of the SPL hostid. If a pool is preferred via B and the pool can not be imported with a preconfigured hostid, the system will attempt to adopt the hostid of the system that last imported the pool. If a preferred pool is not set and no pools can be imported using a preconfigured hostid, the system will adopt the hostid of the first otherwise-importable pool. After adopting a detected hostid, ZFSBootMenu will subsequently attempt to import as many pools as possible. This option is forbidden on big-endian systems. + +=item B + +Set this option to only import pools that match the SPL hostid configured in ZFSBootMenu. If none can be imported, an emergency shell will be invoked. The I policy is consistent with the behavior of earlier versions of ZFSBootMenu and is the default import policy. + +=item B Set this option to attempt to force pool imports. When set, this invokes I in place of the regular I command, which will attempt to import a pool that's potentially in use on another system. Use this option with caution! -Omit this option or explicitly specify B to disable forced imports. +=back -=item B +=item B + +On little-endian systems, setting this option will cause ZFSBootMenu to set the I command-line parameter for the selected boot environment to the hostid used to import its pool. The SPL kernel module will use this value as the hostid of the booted environment regardless of the contents of I. As a special case, if the hostid to be set is zero, ZFSBootMenu will instead set I, which should be used by dracut-based initramfs images to write an all-zero I in the initramfs prior to importing the boot pool. -Deprecated; use B. +This option has no effect on big-endian systems. =item B @@ -54,8 +68,7 @@ Sort the lists by size I. =item B -This option accepts numeric values that control whether and when the -boot-environment menu should be displayed. +This option accepts numeric values that control whether and when the boot-environment menu should be displayed. =over 2 @@ -73,13 +86,25 @@ Display a countdown timer for the specified number of seconds before booting the =back +=item B + +Indicate that ZFSBootMenu should be run under B in the initramfs. With this enabled, debug-level logging for the ZFSBootMenu scripts can be easily viewed. The tooling must also be installed with an additional dracut configuration option listed below. + =item B Deprecated; use B. -=item B +=item BpoolE> -Indicate that ZFSBootMenu should be run under B in the initramfs. With this enabled, debug-level logging for the ZFSBootMenu scripts can be easily viewed. The tooling must also be installed with an additional dracut configuration option listed below. +Deprecated; use B. + +=item B + +Deprecated; use B. + +=item B + +Deprecated; use B. =back