Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ee62c17
Start of a hostid alignment helper
zdykstra Feb 21, 2021
4d1f43a
Fix read-only errors
zdykstra Feb 21, 2021
8002c0a
Only operate on a single BE at a time
zdykstra Feb 21, 2021
cfca80e
Rewrite org.zfsbootmenu:commandline
zdykstra Feb 21, 2021
abaad18
Silence shellcheck error
zdykstra Feb 21, 2021
99aaa67
Only write temporary commandline if needed
zdykstra Feb 21, 2021
5ebec9c
/etc/hostid is read on demand
zdykstra Feb 21, 2021
6e00c2c
Add match_hostid chameleon function
zdykstra Feb 21, 2021
7d50d65
prefer pool set on kcl
zdykstra Feb 21, 2021
baa858d
Fail to shell if we changing our hostid fails
zdykstra Feb 21, 2021
96285eb
Return imported pool, throw error
zdykstra Feb 21, 2021
1323da8
Support busybox grep
zdykstra Feb 21, 2021
5facb64
Prohibit r/w on matched hostid pool imports
zdykstra Feb 21, 2021
0e47704
Huge hack to rewrite spl_hostid on KCL of bootfs
zdykstra Feb 21, 2021
84c4f61
Integrate kcl supressor, import_policy with force_import, automatic s…
zdykstra Feb 22, 2021
3f76096
document match_hostid, allow r/w ops on pool imported via matched hostid
zdykstra Feb 22, 2021
ad185b5
Check for empty hostid when scraping a pool import
zdykstra Feb 22, 2021
8b0cf23
Fix whitespacing
zdykstra Feb 22, 2021
73b678d
Fix whitespacing
zdykstra Feb 22, 2021
6c0c547
Attempt to import everything again after cloning a discovered hostid
zdykstra Feb 22, 2021
3f6cde1
dracut log functions use the daemon facility
zdykstra Feb 22, 2021
5efdeb5
Rearrange import loop
ahesford Feb 22, 2021
c862e84
Always import readonly and disable mounts when scrping hostid
ahesford Feb 22, 2021
7d7408b
Make sure $spl_hostid and /etc/hostid match on startup
zdykstra Feb 22, 2021
df17d75
Default to legacy import behavior, suppress spl_hostid, rewrite spl.s…
zdykstra Feb 23, 2021
adf20f7
Purge this branches namesake
zdykstra Feb 23, 2021
b9cfb72
Fix typo in log message
ahesford Feb 23, 2021
e63bcd8
Fall back to matched hostid in kcl rewrite when spl_hostid is not in …
ahesford Feb 23, 2021
3006bc7
Rename import_policy=legacy to strict
zdykstra Feb 23, 2021
c4e996b
Try harder to get the right hostid when forcing the KCL
ahesford Feb 23, 2021
347d372
Improve logging with get_spl_hostid
ahesford Feb 23, 2021
8fb2860
Make hostid writes aware of endianness
ahesford Feb 24, 2021
cbc0bc3
Move hostid writes to zfsbootmenu-countdown where zfsbootmenu-lib is …
ahesford Feb 24, 2021
2c309ef
Determine LE/BE at creation time, load at startup
zdykstra Feb 24, 2021
187c64b
Make every attempt to import boot_pool first, then anything else we can
zdykstra Feb 24, 2021
e69fead
Make every attempt to import boot_pool first, then anything else we can
zdykstra Feb 25, 2021
ba4b564
WIP: import loop restructure
ahesford Feb 25, 2021
aabd349
Fix check_for_pools, shellcheck
zdykstra Feb 25, 2021
446ad84
Add zbm.prefer=<pool> shorthand
zdykstra Feb 25, 2021
b6601e6
Fall back to writing spl_hostid when spl.spl_hostid=0 would be used
ahesford Feb 25, 2021
d95b264
First pass at updating zfsbootmenu documentation
zdykstra Feb 25, 2021
07b8fcb
Second pass at zfsbootmenu.7.pod
ahesford Feb 25, 2021
2f3ad9d
Add warning flag, set min log level to warn, warn on hostid matched i…
zdykstra Feb 25, 2021
2711843
Pass 3 on zfsbootmenu.7.pod
ahesford Feb 25, 2021
fbc1e91
Move to a new line to clarify the scope of 'this option'
zdykstra Feb 25, 2021
c66e2ee
Change log hotkey text to be more generic
zdykstra Feb 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions 90zfsbootmenu/zfsbootmenu-countdown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@ 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
# Attempt to import all pools read-only
read_write='' all_pools=yes import_pool

# Make sure at least one was imported
import_success=0
while IFS=$'\t' read -r _pool _health; do
[ -n "${_pool}" ] || continue

import_success=1

if [ "${_health}" != "ONLINE" ]; then
echo "${_pool}" >> "${BASE}/degraded"
zerror "prohibiting read/write operations on ${_pool}"
fi
done <<<"$( zpool list -H -o name,health )"

if [ "${import_success}" -ne 1 ]; then
emergency_shell "unable to successfully import a pool"
else
if [ "${import_success}" -eq 1 ]; then
# With at least one imported pool, just move on
zdebug "$(
echo "zpool list" ; \
zpool list
Expand All @@ -42,6 +41,28 @@ while true; do
)"
break
fi

# shellcheck disable=SC2154
if [ "${import_policy}" == "hostid" ] && masked="$( match_hostid )"; then
zdebug "match_hostid returned: ${masked}"

pool="${masked%%;*}"
spl_hostid="${masked##*;}"

export spl_hostid

zerror "imported ${pool} with assumed hostid ${spl_hostid}"
zerror "set spl_hostid=${spl_hostid} on ZBM KCL or regenerate with corrected /etc/hostid"

# potentially useful later?
: > "${BASE}/masked_hostid"

# Retry the import cycle with the masked hostid
continue
fi

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

# Prefer a specific pool when checking for a bootfs value
Expand Down
3 changes: 2 additions & 1 deletion 90zfsbootmenu/zfsbootmenu-exec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
# vim: softtabstop=2 shiftwidth=2 expandtab

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
Expand Down
159 changes: 123 additions & 36 deletions 90zfsbootmenu/zfsbootmenu-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,69 @@ center_string() {
printf "%*s" $(( (${#1} + _WIDTH ) / 2)) "${1}"
}


# args: no arguments
# prints: <imported pool>;<hostid>
# returns: 0 on successful pool import, 1 on failure

match_hostid() {
local importable pool state hostid
importable=()

# If root is defined and not zfsbootmenu, prefer that pool for masked import
# shellcheck disable=SC2154
if [ -n "${root}" ] && [ "${root}" != "zfsbootmenu" ]; then
pool="${root%%/*}"
importable+=( "${pool}" )
fi

while read -r line; do
case "$line" in
pool*)
pool="${line#pool: }"
;;
state*)
state="${line#state: }"
if [ "${state}" == "ONLINE" ] && [ -n "${pool}" ]; then
importable+=("${pool}")
pool=""
fi
;;
esac
done <<<"$( zpool import )"

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 [ "${hostid}" == "0" ]; then
hostid="00000000"
fi

zdebug "setting hostid to: ${hostid}"
echo -ne "\\x${hostid:6:2}\\x${hostid:4:2}\\x${hostid:2:2}\\x${hostid:0:2}" > "/etc/hostid"

if read_write='' import_pool "${pool}"; then
zdebug "successfully imported ${pool}"
echo "${pool};${hostid}"
return 0
fi
done

# no pools could be imported, we failed to match a hostid
return 1
}

# arg1: ZFS filesystem name
# prints: mountpoint
# returns: 0 on success
Expand Down Expand Up @@ -981,6 +1044,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
Expand Down Expand Up @@ -1012,39 +1123,15 @@ 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 ]; then
zdebug "removing spl_hostid in: ${zfsbe_args}"
zfsbe_args="$( suppress_kcl_arg spl_hostid "${zfsbe_args}" )"
zdebug "rewriting spl.spl_hostid in ${zfsbe_args}"
zfsbe_args="spl.spl_hostid=0x${spl_hostid} $( suppress_kcl_arg spl.spl_hostid "${zfsbe_args}" )"
fi

zdebug "processed commandline: ${zfsbe_args}"
Expand All @@ -1056,7 +1143,7 @@ load_be_cmdline() {
# 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
Expand All @@ -1073,9 +1160,9 @@ import_pool() {
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
Expand Down Expand Up @@ -1774,7 +1861,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
Expand Down
48 changes: 40 additions & 8 deletions 90zfsbootmenu/zfsbootmenu-parse-commandline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ 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)"
spl_hostid="$(hostid)"
info "ZFSBootMenu: using hostid from /etc/hostid: ${spl_hostid}"
else
warn "ZFSBootMenu: no hostid found on kernel command line or /etc/hostid"
warn "ZFSBootMenu: pools may not import correctly"
warn "ZFSBootMenu: defaulting hostid to 00000000"
echo -ne "\\x00\\x00\\x00\\x00" >/etc/hostid
spl_hostid="00000000"
fi

# Use the last defined console= to control menu output
Expand All @@ -36,11 +39,29 @@ else
loglevel=3
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"
import_policy=$( getarg zbm.import_policy )
if [ -n "${import_policy}" ]; then
case "${import_policy}" in
hostid)
info "ZFSBootMenu: setting import_policy to hostid matching"
;;
force)
info "ZFSBootMenu: setting import_policy to force"
;;
legacy)
info "ZFSBootMenu: setting import_policy to legacy"
;;
*)
info "ZFSBootMenu: unknown import policy ${import_policy}, defaulting to legacy"
import_policy="legacy"
;;
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 hostid matching"
import_policy="legacy"
fi

# zbm.timeout= overrides timeout=
Expand Down Expand Up @@ -98,10 +119,21 @@ 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

# Do not automatically set spl_hostid on the BE KCL
# shellcheck disable=SC2034
if 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


wait_for_zfs=0
case "${root}" in
""|zfsbootmenu|zfsbootmenu:)
Expand Down
2 changes: 1 addition & 1 deletion 90zfsbootmenu/zlogtail.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down