Skip to content

Commit bd1319f

Browse files
committed
zfsbootmenu: log errors for unimportable pools
Before landing at the emergency_shell in the main import loop, log the output of a failed zpool import for every pool that's marked UNAVAIL . In the emergency_shell function, write any log messages at the 'err' level directly to the active console. The 'print_kmsg_logs' helper has been added that accepts a comma-separated list of log levels (as understood by util-linux dmesg). If the feature flags supplied to dmesg fail, fall back to 'dmesg -r' which is understood by both Busybox and util-linux dmesg. Manually convert err,info,... log levels to a numeric code and grep for that at the start of each line.
1 parent 2762b7e commit bd1319f

File tree

4 files changed

+97
-8
lines changed

4 files changed

+97
-8
lines changed

zfsbootmenu/bin/zlogtail

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# shellcheck disable=SC1091
44
source /lib/zfsbootmenu-lib.sh >/dev/null 2>&1 || exit 1
5+
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
56

67
[ -f "${BASE}/have_errors" ] && rm "${BASE}/have_errors"
78
[ -f "${BASE}/have_warnings" ] && rm "${BASE}/have_warnings"
@@ -29,10 +30,4 @@ fi
2930

3031
export FZF_DEFAULT_OPTS="${fuzzy_default_options[*]}"
3132

32-
# Try to use feature flags found on dmesg from util-linux
33-
if output="$( dmesg --notime -f user -l err,warn 2>/dev/null )" ; then
34-
echo "${output}" | ${FUZZYSEL}
35-
else
36-
# fall back to manually parsing dmesg output; will show all ZBM generated logs up to zinfo level
37-
dmesg | awk '/ZFSBootMenu:/{ for (i=3; i<=NF; i++){ printf("%s ", $i)}; printf "\n" }' | ${FUZZYSEL}
38-
fi
33+
print_kmsg_logs "err,warn" | ${FUZZYSEL}

zfsbootmenu/lib/kmsg-log-lib.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,58 @@ zerror() {
8484
: > "${BASE}/have_errors"
8585
echo "<3>ZFSBootMenu: $1" > /dev/kmsg
8686
}
87+
88+
# arg1: comma-separated log levels to print
89+
# prints: all logs at that level
90+
# returns: nothing
91+
92+
print_kmsg_logs() {
93+
local levels levels_array grep_args
94+
95+
levels="${1}"
96+
if [ -z "${levels}" ]; then
97+
zerror "levels is undefined"
98+
return
99+
fi
100+
101+
# Try to use feature flags found in dmesg from util-linux
102+
if output="$( dmesg --notime -f user --color=always -l "${levels}" 2>/dev/null )" ; then
103+
echo -e "${output}"
104+
else
105+
# Both util-linux and Busybox dmesg support the -r flag. However, the log level that is
106+
# reported by Busybox dmesg is larger than that reported by util-linux dmesg. Busybox dmesg
107+
# is too bare-bones to do much of anything, so we just need to grep for both integers at
108+
# a given log level, then refly on matching ZFSBootMenu for info and lower, and ZBM for debug.
109+
110+
IFS=',' read -r -a levels_array <<<"${levels}"
111+
for level in "${levels_array[@]}"; do
112+
case "${level}" in
113+
err)
114+
grep_args+=( "-e" "^<11>" "-e" "^<3>" )
115+
;;
116+
warn)
117+
grep_args+=( "-e" "^<12>" "-e" "^<4>" )
118+
;;
119+
notice)
120+
grep_args+=( "-e" "^<13>" "-e" "^<5>" )
121+
;;
122+
info)
123+
grep_args+=( "-e" "^<14>" "-e" "^<6>" )
124+
;;
125+
debug)
126+
grep_args+=( "-e" "^<15>" "-e" "^<7>" )
127+
;;
128+
*)
129+
grep_args+=( "-e" "." )
130+
;;
131+
esac
132+
done
133+
134+
dmesg -r | grep "${grep_args[@]}" \
135+
| awk '
136+
/ZFSBootMenu:/{ for (i=3; i<=NF; i++){ printf("%s ", $i)}; printf "\n" }
137+
/ZBM:/{ for (i=3; i<=NF; i++){ printf("%s ", $i)}; printf "\n" }
138+
'
139+
fi
140+
}
141+

zfsbootmenu/lib/zfsbootmenu-core.sh

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,38 @@ match_hostid() {
196196
return 1
197197
}
198198

199+
# args: no arguments
200+
# prints: nothing
201+
# returns: nothing
202+
203+
log_unimportable() {
204+
local pool id state line error_line
205+
206+
while read -r line; do
207+
case "${line}" in
208+
pool*)
209+
pool="${line#pool: }"
210+
;;
211+
id*)
212+
id="${line#id: }"
213+
;;
214+
state*)
215+
state="${line#state: }"
216+
if [ "${state}" == "UNAVAIL" ]; then
217+
while read -r error_line; do
218+
zerror "${error_line}"
219+
done <<<"$( zpool import -N "${id}" 2>&1 )"
220+
fi
221+
state=
222+
pool=
223+
id=
224+
;;
225+
*)
226+
;;
227+
esac
228+
done <<<"$( zpool import )"
229+
}
230+
199231
# args: none
200232
# prints: nothing
201233
# returns: 0 if at least one pool is available
@@ -1815,7 +1847,12 @@ emergency_shell() {
18151847
18161848
EOF
18171849

1818-
command -v efibootmgr >/dev/null 2>&1 && mount_efivarfs "rw"
1850+
if [ -f "${BASE}/have_errors" ]; then
1851+
print_kmsg_logs "err"
1852+
echo ""
1853+
fi
1854+
1855+
command -v efibootmgr >/dev/null 2>&1 && mount_efivarfs "rw"
18191856

18201857
# -i (interactive) mode will source $HOME/.bashrc
18211858
/bin/bash -i

zfsbootmenu/libexec/zfsbootmenu-init

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ fi
116116
# If a boot pool is specified, that will be tried first
117117
try_pool="${boot_pool}"
118118
zbm_import_attempt=0
119+
119120
while true; do
120121
if [ -n "${try_pool}" ]; then
121122
zdebug "attempting to import preferred pool ${try_pool}"
@@ -173,6 +174,7 @@ while true; do
173174
continue
174175
fi
175176

177+
log_unimportable
176178
# Allow the user to attempt recovery
177179
emergency_shell "unable to successfully import a pool"
178180
done

0 commit comments

Comments
 (0)