Skip to content

Commit bd736f1

Browse files
committed
Add a shell utility to modify and review KCL properties
Closes: #243.
1 parent 3e9c840 commit bd736f1

File tree

1 file changed

+271
-0
lines changed

1 file changed

+271
-0
lines changed

bin/zbm-kcl

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
#!/bin/bash
2+
# vim: softtabstop=2 shiftwidth=2 expandtab
3+
4+
zbm_load_lib() {
5+
lib="${1?no library specified for load}"
6+
7+
: "${ZBM_MODULEDIR:=/usr/lib/dracut/modules.d/90zfsbootmenu}"
8+
9+
if [ -r "${ZBM_MODULEDIR}/lib/${lib}" ]; then
10+
# shellcheck disable=SC1090
11+
source "${ZBM_MODULEDIR}/lib/${lib}"
12+
else
13+
echo "ERROR: ${ZBM_MODULEDIR}/lib/${lib} not found" >&2
14+
echo "Set ZBM_MODULEDIR to the root of the ZBM dracut module" >&2
15+
exit 1
16+
fi
17+
}
18+
19+
usage() {
20+
cat <<-EOF
21+
USAGE: $0 [-a <arg>] [-r <arg>] [-e] [-d] [-v] [bootenv]
22+
Review or update kernel command line (KCL) associated with <bootenv>
23+
When the boot environment is specified, the current root will be used
24+
25+
-a <arg>: Append an argument
26+
-r <arg>: Remove an argument
27+
-e: Open the KCL for editing in \$EDITOR
28+
-d: Remove entire command line
29+
-v: Enable verbose output
30+
EOF
31+
}
32+
33+
strip_kcl() {
34+
awk '
35+
BEGIN { first = 1; }
36+
37+
/^$/ { exit; }
38+
39+
{
40+
gsub("[[:space:]]*#.*", "");
41+
42+
if (length == 0) next;
43+
44+
if (first == 1) {
45+
first = 0;
46+
} else {
47+
printf " ";
48+
}
49+
50+
printf "%s", $0;
51+
}
52+
'
53+
}
54+
55+
delete() {
56+
local bootenv="${1}"
57+
58+
if ! zfs list -o name -H "${bootenv}" >/dev/null 2>&1; then
59+
zerror "no boot environment specified"
60+
usage
61+
return 1
62+
fi
63+
64+
if ! zfs inherit org.zfsbootmenu:commandline "${bootenv}"; then
65+
zerror "failed to remove KCL from ${bootenv}"
66+
return 1
67+
fi
68+
}
69+
70+
raw_kcl() {
71+
local bootenv="${1}"
72+
local kcl
73+
74+
kcl="$(zfs get -H -o value org.zfsbootmenu:commandline "${bootenv}")" || return 1
75+
[ "${kcl}" = "-" ] && return
76+
77+
echo "${kcl}"
78+
}
79+
80+
review() {
81+
local bootenv="${1}"
82+
local kcl
83+
84+
kcl="$(raw_kcl "${bootenv}")"
85+
echo "${kcl}"
86+
87+
if [[ "${kcl}" =~ "%{parent}" ]]; then
88+
echo "# With parent references, KCL expands to"
89+
echo "# $(read_kcl_prop "${bootenv}")"
90+
fi
91+
92+
return 0
93+
}
94+
95+
edit() {
96+
local bootenv kclfile oldsum newkcl newsum
97+
98+
bootenv="${1}"
99+
100+
if ! command -v "${EDITOR:=vi}" >/dev/null 2>&1; then
101+
zerror "define \$EDITOR to edit"
102+
return 1
103+
fi
104+
105+
if ! kclfile="$(mktemp)"; then
106+
zerror "failed to create temporary file"
107+
return 1
108+
fi
109+
110+
trap 'rm -f "${kclfile}"' RETURN
111+
112+
if ! review "${bootenv}" > "${kclfile}"; then
113+
zerror "unable to read KCL for ${bootenv}"
114+
return 1
115+
fi
116+
117+
cat >> "${kclfile}" <<-EOF
118+
119+
# KCL processing ends with the first line that contains no text.
120+
# Anything starting with # is considered a comment and will be ignored.
121+
# Multiple lines will be concatenated with spaces to form a single line.
122+
EOF
123+
124+
if ! oldsum="$(strip_kcl < "${kclfile}" | md5sum | awk '{print $1}')"; then
125+
zerror "unable to compute checksum for existing KCL"
126+
return 1
127+
fi
128+
129+
if ! "${EDITOR}" "${kclfile}"; then
130+
zerror "failed to edit KCL for ${bootenv}"
131+
return 1
132+
fi
133+
134+
if ! newkcl="$(strip_kcl < "${kclfile}")"; then
135+
zerror "failed to parse new KCL for ${bootenv}"
136+
return 1
137+
fi
138+
139+
if ! newsum="$(echo -n "${newkcl}" | md5sum | awk '{print $1}')"; then
140+
zerror "failed to compute checksum for new KCL"
141+
return 1
142+
fi
143+
144+
if [ "${newsum}" = "${oldsum}" ]; then
145+
znotice "modified KCL appears the same as original, will not change"
146+
return 0
147+
fi
148+
149+
if ! zfs set org.zfsbootmenu:commandline="${newkcl}" "${bootenv}"; then
150+
zerror "failed to set ZBM KCL"
151+
return 1
152+
fi
153+
}
154+
155+
getbootenv() {
156+
local dev mntpt fs opts
157+
158+
# shellcheck disable=SC2034
159+
while read -r dev mntpt fs opts; do
160+
[ "${mntpt}" = "/" ] || continue
161+
162+
if [ "${fs}" != "zfs" ]; then
163+
return 1
164+
fi
165+
166+
echo "${dev}"
167+
return 0
168+
done < /proc/mounts
169+
}
170+
171+
172+
delkcl=
173+
editkcl=
174+
havemods=
175+
appends=()
176+
removes=()
177+
loglevel=4
178+
179+
while getopts "ha:r:edv" opt; do
180+
case "${opt}" in
181+
a)
182+
appends+=( "${OPTARG}" )
183+
;;
184+
r)
185+
removes+=( "${OPTARG}" )
186+
;;
187+
e)
188+
editkcl="yes"
189+
;;
190+
d)
191+
delkcl="yes"
192+
;;
193+
v)
194+
loglevel="$((loglevel + 1))"
195+
;;
196+
h)
197+
usage
198+
exit
199+
;;
200+
*)
201+
usage
202+
exit 1
203+
;;
204+
esac
205+
done
206+
207+
shift $((OPTIND-1))
208+
209+
zbm_load_lib "zfsbootmenu-core.sh"
210+
zbm_load_lib "echo-log-lib.sh"
211+
212+
bootenv="${1}"
213+
if [ -z "${bootenv}" ]; then
214+
if ! bootenv="$(getbootenv)"; then
215+
zerror "root does not appear to be ZFS" >&2
216+
exit 1
217+
fi
218+
fi
219+
220+
if ! zfs list -o name -H "${bootenv}" >/dev/null 2>&1; then
221+
zerror "boot environment ${bootenv} does not exist"
222+
exit 1
223+
fi
224+
225+
if [ "${editkcl}" = "yes" ]; then
226+
edit "${bootenv}"
227+
exit
228+
fi
229+
230+
if (( ${#removes[@]} != 0 || ${#appends[@]} != 0 )); then
231+
havemods="yes"
232+
fi
233+
234+
if [ "${delkcl}" ]; then
235+
if [ "${havemods}" = "yes" ]; then
236+
zerror "-d is incompatible with -a or -r"
237+
exit 1
238+
fi
239+
240+
delete "${bootenv}"
241+
exit
242+
fi
243+
244+
if [ "${havemods}" != "yes" ]; then
245+
review "${bootenv}"
246+
exit 0
247+
fi
248+
249+
# Act on raw KCL argument for append/remove operations
250+
if ! kcl="$(raw_kcl "${bootenv}")"; then
251+
zerror "failed to read KCL for ${bootenv}"
252+
exit 1
253+
fi
254+
255+
# Strip all unwanted arguments from the KCL
256+
for rem in "${removes[@]}"; do
257+
kcl="$(suppress_kcl_arg "${rem}" "${kcl}")" || exit 1
258+
done
259+
260+
for add in "${appends[@]}"; do
261+
if [ -z "${kcl}" ]; then
262+
kcl="${add}"
263+
else
264+
kcl+=" ${add}"
265+
fi
266+
done
267+
268+
if ! zfs set org.zfsbootmenu:commandline="${kcl}" "${bootenv}"; then
269+
zerror "failed to set ZBM KCL"
270+
exit 1
271+
fi

0 commit comments

Comments
 (0)