Skip to content

Commit 7eb7780

Browse files
committed
Allow imports of hooks from external sources at runtime
Setting `zbm.hookroot=<filesystem>//<path>` will cause ZBM to mount the named filesystem and copy any hooks (files) in the path <path>/{early-setup,setup,teardown}.d into the corresponding hook directory under `/libexec`.
1 parent 57b46a2 commit 7eb7780

File tree

9 files changed

+239
-4
lines changed

9 files changed

+239
-4
lines changed

docs/man/zfsbootmenu.7

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42)
1+
.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43)
22
.\"
33
.\" Standard preamble:
44
.\" ========================================================================
@@ -133,7 +133,7 @@
133133
.\" ========================================================================
134134
.\"
135135
.IX Title "zfsbootmenu 7"
136-
.TH zfsbootmenu 7 "2022-06-24" "2.0.0" "ZFSBootMenu"
136+
.TH zfsbootmenu 7 "2022-10-28" "2.0.0" "ZFSBootMenu"
137137
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
138138
.\" way too many mistakes in technical documents.
139139
.if n .ad l
@@ -149,9 +149,22 @@ These options are set on the kernel command line when booting the initramfs or \
149149
.IP "\fBspl_hostid=<hostid>\fR" 4
150150
.IX Item "spl_hostid=<hostid>"
151151
When creating an initramfs or \s-1UEFI\s0 bundle, the \fI/etc/hostid\fR from the system is copied into the target. If this image will be used on another system with a different hostid, replace \fB<hostid>\fR with the desired hostid, as an eight-digit hexadecimal number, to override the value contained within the image.
152-
.IP "\fBzbm.prefer=<pool>\fR" 4
152+
.IP "\fBzbm.prefer\fR" 4
153+
.IX Item "zbm.prefer"
154+
ZFSBootMenu will attempt to import as many pools as possible to identify boot environments and will, by default, look for the \fIbootfs\fR property on the first imported pool (sorted alphabetically) to select the default boot environment. This option controls this behavior.
155+
.RS 4
156+
.IP "\fBzbm.prefer=<pool>\fR" 2
153157
.IX Item "zbm.prefer=<pool>"
154-
ZFSBootMenu will attempt to import as many pools as possible to identify boot environments and will, by default, look for the \fIbootfs\fR property on the first imported pool (sorted alphabetically) to select the default boot environment. If you have multiple pools, replace \fB<pool>\fR with the name of your preferred pool to override the default. If \fB<pool>\fR is the name of a pool followed by a literal \fI!\fR character, ZFSBootMenu will insist on successfully importing the named pool before attempting to import any others.
158+
The simplest form attempts to import \fB<pool>\fR before any other pool. The \fIbootfs\fR value from this pool will control the default boot environment.
159+
.IP "\fBzbm.prefer=<pool>\f(BI!\fB\fR" 2
160+
.IX Item "zbm.prefer=<pool>!"
161+
If a literal \fI!\fR has been appended to the pool name, ZFSBootMenu will insist on successfully importing the named pool before attempting to import any others.
162+
.IP "\fBzbm.prefer=<pool>\f(BI!!\fB\fR" 2
163+
.IX Item "zbm.prefer=<pool>!!"
164+
If a literal \fI!!\fR has been appended to the pool name, ZFSBootMenu will insist on successfully importing the named pool and no others.
165+
.RE
166+
.RS 4
167+
.RE
155168
.IP "\fBzbm.import_delay=<time>\fR" 4
156169
.IX Item "zbm.import_delay=<time>"
157170
Should ZFSBootMenu fail to successfully import any pool, it will repeat import attempts indefinitely until at least one pool can be imported or the user chooses to drop to a recovery shell. Each subsequent attempt will proceed after a delay of \fB<time>\fR seconds. When \fB<time>\fR is unspecified or is anything other than a positive integer, a default value of 5 seconds will be used.
@@ -214,6 +227,31 @@ Display a countdown timer for the specified number of seconds before booting the
214227
.RE
215228
.RS 4
216229
.RE
230+
.IP "\fBzbm.hookroot=<hookspec>\fR" 4
231+
.IX Item "zbm.hookroot=<hookspec>"
232+
Tell ZFSBootMenu to attempt to read any early-setup, setup or teardown hooks from the path specified by \fIhookspec\fR in addition to any included directly in the image.
233+
.Sp
234+
The \fIhookspec\fR parameter takes the form
235+
.Sp
236+
.Vb 1
237+
\& device//path
238+
.Ve
239+
.Sp
240+
where \fIdevice\fR is either a regular device node (e.g., \fI/dev/sda\fR) or other partition identifier recognized by \fBmount\fR(8) (e.g., \fILABEL=<label>\fR o \fIUUID=<uuid>\fR). The \fIpath\fR component following \fI//\fR represents the location of a directory with respect to the root of the filesystem on \fIdevice\fR. For example, if a partition with a \s-1UUID\s0 of \fIDEAD-BEEF\fR is mounted at \fI/boot/efi\fR on a running system and the hook root should refer to the path
241+
.Sp
242+
.Vb 1
243+
\& /boot/efi/EFI/zfsbootmenu/hooks,
244+
.Ve
245+
.Sp
246+
the corresponding hook specification should be
247+
.Sp
248+
.Vb 1
249+
\& zbm.hookroot=UUID=DEAD\-BEEF//EFI/zfsbootmenu/hooks
250+
.Ve
251+
.Sp
252+
on the ZFSBootMenu command line. Note that any kernel modules necessary to mount the specified filesystem must be present in the ZFSBootMenu image. (For example, mounting a \s-1FAT32\s0 filesystem may require that \fIvfat.ko\fR, \fIfat.ko\fR, \fInls_cp437.ko\fR and \fInls_iso8859_1.ko\fR be added to the image.)
253+
.Sp
254+
Within the hook root, create subdirectories \fIearly\-setup.d\fR, \fIsetup.d\fR or \fIteardown.d\fR to hold hooks for the respective stages of hook execution (early-setup, setup and teardown). ZFSBootMenu will mount the device named by the hook specification, look for the individual hook directories, and copy any files found therein into its own memory-backed root filesystem. The copy is not recursive and further subdirectorie are ignored. Note that, because ZFSBootMenu copies these scripts into its standard hook paths at each boot, it is possible to \*(L"mask\*(R" a script explicitly included in the ZFSBootMenu image by including an external hook script with the same name in the appropriate directory.
217255
.SH "Deprecated Command-Line Parameters"
218256
.IX Header "Deprecated Command-Line Parameters"
219257
.IP "\fBtimeout\fR" 4

docs/pod/zfsbootmenu.7.pod

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,26 @@ Display a countdown timer for the specified number of seconds before booting the
112112

113113
=back
114114

115+
=item B<zbm.hookroot=E<lt>hookspecE<gt>>
116+
117+
Tell ZFSBootMenu to attempt to read any early-setup, setup or teardown hooks from the path specified by I<hookspec> in addition to any included directly in the image.
118+
119+
The I<hookspec> parameter takes the form
120+
121+
device//path
122+
123+
where I<device> is either a regular device node (e.g., I</dev/sda>) or other partition identifier recognized by B<mount>(8) (e.g., I<LABEL=E<lt>labelE<gt>> o I<UUID=E<lt>uuidE<gt>>). The I<path> component following I<//> represents the location of a directory with respect to the root of the filesystem on I<device>. For example, if a partition with a UUID of I<DEAD-BEEF> is mounted at I</boot/efi> on a running system and the hook root should refer to the path
124+
125+
/boot/efi/EFI/zfsbootmenu/hooks,
126+
127+
the corresponding hook specification should be
128+
129+
zbm.hookroot=UUID=DEAD-BEEF//EFI/zfsbootmenu/hooks
130+
131+
on the ZFSBootMenu command line. Note that any kernel modules necessary to mount the specified filesystem must be present in the ZFSBootMenu image. (For example, mounting a FAT32 filesystem may require that I<vfat.ko>, I<fat.ko>, I<nls_cp437.ko> and I<nls_iso8859_1.ko> be added to the image.)
132+
133+
Within the hook root, create subdirectories I<early-setup.d>, I<setup.d> or I<teardown.d> to hold hooks for the respective stages of hook execution (early-setup, setup and teardown). ZFSBootMenu will mount the device named by the hook specification, look for the individual hook directories, and copy any files found therein into its own memory-backed root filesystem. The copy is not recursive and further subdirectorie are ignored. Note that, because ZFSBootMenu copies these scripts into its standard hook paths at each boot, it is possible to "mask" a script explicitly included in the ZFSBootMenu image by including an external hook script with the same name in the appropriate directory.
134+
115135
=back
116136

117137
=head1 Deprecated Command-Line Parameters

zfsbootmenu/help-files/132/zfsbootmenu.7.pod

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,36 @@ general systems to boot without setting any values.
9494
zbm.timeout=<positive integer>
9595
Display a countdown timer for the specified number of seconds before booting the configured bootfs boot environment.
9696

97+
zbm.hookroot=<hookspec>
98+
Tell ZFSBootMenu to attempt to read any early-setup, setup or teardown hooks from the path specified by hookspec in addition to
99+
any included directly in the image.
100+
101+
The hookspec parameter takes the form
102+
103+
device//path
104+
105+
where device is either a regular device node (e.g., /dev/sda) or other partition identifier recognized by mount(8) (e.g.,
106+
LABEL=<label> o UUID=<uuid>). The path component following // represents the location of a directory with respect to the root of
107+
the filesystem on device. For example, if a partition with a UUID of DEAD-BEEF is mounted at /boot/efi on a running system and
108+
the hook root should refer to the path
109+
110+
/boot/efi/EFI/zfsbootmenu/hooks,
111+
112+
the corresponding hook specification should be
113+
114+
zbm.hookroot=UUID=DEAD-BEEF//EFI/zfsbootmenu/hooks
115+
116+
on the ZFSBootMenu command line. Note that any kernel modules necessary to mount the specified filesystem must be present in the
117+
ZFSBootMenu image. (For example, mounting a FAT32 filesystem may require that vfat.ko, fat.ko, nls_cp437.ko and nls_iso8859_1.ko
118+
be added to the image.)
119+
120+
Within the hook root, create subdirectories early-setup.d, setup.d or teardown.d to hold hooks for the respective stages of hook
121+
execution (early-setup, setup and teardown). ZFSBootMenu will mount the device named by the hook specification, look for the
122+
individual hook directories, and copy any files found therein into its own memory-backed root filesystem. The copy is not
123+
recursive and further subdirectorie are ignored. Note that, because ZFSBootMenu copies these scripts into its standard hook
124+
paths at each boot, it is possible to "mask" a script explicitly included in the ZFSBootMenu image by including an external hook
125+
script with the same name in the appropriate directory.
126+
97127
Deprecated Command-Line Parameters
98128

99129
timeout

zfsbootmenu/help-files/52/zfsbootmenu.7.pod

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,56 @@ without setting any values.
156156
number of seconds before booting the
157157
configured bootfs boot environment.
158158

159+
zbm.hookroot=<hookspec>
160+
Tell ZFSBootMenu to attempt to read any
161+
early-setup, setup or teardown hooks from the
162+
path specified by hookspec in addition to any
163+
included directly in the image.
164+
165+
The hookspec parameter takes the form
166+
167+
device//path
168+
169+
where device is either a regular device node
170+
(e.g., /dev/sda) or other partition identifier
171+
recognized by mount(8) (e.g., LABEL=<label> o
172+
UUID=<uuid>). The path component following //
173+
represents the location of a directory with
174+
respect to the root of the filesystem on device.
175+
For example, if a partition with a UUID of
176+
DEAD-BEEF is mounted at /boot/efi on a running
177+
system and the hook root should refer to the
178+
path
179+
180+
/boot/efi/EFI/zfsbootmenu/hooks,
181+
182+
the corresponding hook specification should be
183+
184+
zbm.hookroot=UUID=DEAD-BEEF//EFI/zfsbootmenu/hooks
185+
186+
on the ZFSBootMenu command line. Note that any
187+
kernel modules necessary to mount the specified
188+
filesystem must be present in the ZFSBootMenu
189+
image. (For example, mounting a FAT32 filesystem
190+
may require that vfat.ko, fat.ko, nls_cp437.ko
191+
and nls_iso8859_1.ko be added to the image.)
192+
193+
Within the hook root, create subdirectories
194+
early-setup.d, setup.d or teardown.d to hold
195+
hooks for the respective stages of hook
196+
execution (early-setup, setup and teardown).
197+
ZFSBootMenu will mount the device named by the
198+
hook specification, look for the individual hook
199+
directories, and copy any files found therein
200+
into its own memory-backed root filesystem. The
201+
copy is not recursive and further subdirectorie
202+
are ignored. Note that, because ZFSBootMenu
203+
copies these scripts into its standard hook
204+
paths at each boot, it is possible to "mask" a
205+
script explicitly included in the ZFSBootMenu
206+
image by including an external hook script with
207+
the same name in the appropriate directory.
208+
159209
Deprecated Command-Line Parameters
160210

161211
timeout

zfsbootmenu/help-files/92/zfsbootmenu.7.pod

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,40 @@ Default options were chosen to allow general systems to boot without setting any
111111
Display a countdown timer for the specified number of seconds before booting the
112112
configured bootfs boot environment.
113113

114+
zbm.hookroot=<hookspec>
115+
Tell ZFSBootMenu to attempt to read any early-setup, setup or teardown hooks from the
116+
path specified by hookspec in addition to any included directly in the image.
117+
118+
The hookspec parameter takes the form
119+
120+
device//path
121+
122+
where device is either a regular device node (e.g., /dev/sda) or other partition
123+
identifier recognized by mount(8) (e.g., LABEL=<label> o UUID=<uuid>). The path
124+
component following // represents the location of a directory with respect to the root
125+
of the filesystem on device. For example, if a partition with a UUID of DEAD-BEEF is
126+
mounted at /boot/efi on a running system and the hook root should refer to the path
127+
128+
/boot/efi/EFI/zfsbootmenu/hooks,
129+
130+
the corresponding hook specification should be
131+
132+
zbm.hookroot=UUID=DEAD-BEEF//EFI/zfsbootmenu/hooks
133+
134+
on the ZFSBootMenu command line. Note that any kernel modules necessary to mount the
135+
specified filesystem must be present in the ZFSBootMenu image. (For example, mounting a
136+
FAT32 filesystem may require that vfat.ko, fat.ko, nls_cp437.ko and nls_iso8859_1.ko be
137+
added to the image.)
138+
139+
Within the hook root, create subdirectories early-setup.d, setup.d or teardown.d to hold
140+
hooks for the respective stages of hook execution (early-setup, setup and teardown).
141+
ZFSBootMenu will mount the device named by the hook specification, look for the
142+
individual hook directories, and copy any files found therein into its own memory-backed
143+
root filesystem. The copy is not recursive and further subdirectorie are ignored. Note
144+
that, because ZFSBootMenu copies these scripts into its standard hook paths at each
145+
boot, it is possible to "mask" a script explicitly included in the ZFSBootMenu image by
146+
including an external hook script with the same name in the appropriate directory.
147+
114148
Deprecated Command-Line Parameters
115149

116150
timeout

zfsbootmenu/hook/zfsbootmenu-parse-commandline.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,14 @@ else
193193
zinfo "defaulting sort key order to ${zbm_sort}"
194194
fi
195195

196+
# Allow hooks to be imported from another filesystem
197+
198+
# shellcheck disable=SC2034
199+
zbm_hook_root=
200+
if zbm_hook_root=$( get_zbm_arg zbm.hookroot ) ; then
201+
zinfo "setting user hook root to ${zbm_hook_root}"
202+
fi
203+
196204
# shellcheck disable=SC2034
197205
if [ "${BYTE_ORDER}" = "be" ]; then
198206
zbm_set_hostid=0

zfsbootmenu/hook/zfsbootmenu-preinit.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export default_hostid=00bab10c
3333
export zbm_sort="${zbm_sort}"
3434
export zbm_set_hostid="${zbm_set_hostid}"
3535
export zbm_import_delay="${zbm_import_delay}"
36+
export zbm_hook_root="${zbm_hook_root}"
3637
export control_term="${control_term}"
3738
# END additions by zfsbootmenu-preinit.sh
3839
EOF

zfsbootmenu/lib/zfsbootmenu-core.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,3 +1815,52 @@ zreport() {
18151815
echo -e "\n# zfs list"
18161816
zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource
18171817
}
1818+
1819+
# arg1: hook root spec, as <device>//<path>
1820+
# prints: nothing
1821+
# returns: 0 iff device and path are valid
1822+
1823+
import_zbm_hooks() {
1824+
local hook_root hook_fs hook_path hook_mount hdir hsrc hfile
1825+
1826+
hook_root="${1}"
1827+
if [ -z "${hook_root}" ]; then
1828+
zerror "hook root is not defined"
1829+
return 1
1830+
fi
1831+
1832+
hook_fs="${hook_root%//*}"
1833+
if [ -z "${hook_fs}" ] || [ "${hook_fs}" = "${hook_root}" ]; then
1834+
zerror "unable to find hook device: '${hook_root}' is malformed"
1835+
return 1
1836+
fi
1837+
1838+
hook_path="${hook_root##*//}"
1839+
if [ -z "${hook_path}" ] || [ "${hook_path}" = "${hook_root}" ]; then
1840+
zerror "unable to find hook path: '${hook_root}' is malformed"
1841+
return 1
1842+
fi
1843+
1844+
hook_mount="${BASE}/.external_hooks"
1845+
mkdir -p "${hook_mount}"
1846+
if ! mount -r "${hook_fs}" "${hook_mount}"; then
1847+
zerror "failed to mount hook filesystem ${hook_fs}"
1848+
return 1
1849+
fi
1850+
1851+
for hdir in early-setup.d setup.d teardown.d; do
1852+
hsrc="${hook_mount}/${hook_path}/${hdir}"
1853+
[ -d "${hsrc}" ] || continue
1854+
mkdir -p "/libexec/${hdir}"
1855+
for hfile in "${hsrc}"/*; do
1856+
[ -f "${hfile}" ] || continue
1857+
if ! cp "${hfile}" "/libexec/${hdir}" >/dev/null 2>&1; then
1858+
zwarn "failed to copy user hook ${hfile}"
1859+
fi
1860+
done
1861+
done
1862+
1863+
umount "${hook_mount}"
1864+
1865+
return 0
1866+
}

zfsbootmenu/libexec/zfsbootmenu-init

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ elif [ ! -e /etc/hostid ]; then
7575
write_hostid "${default_hostid}"
7676
fi
7777

78+
# Import ZBM hooks from an external root, if they exist
79+
if [ -n "${zbm_hook_root}" ]; then
80+
import_zbm_hooks "${zbm_hook_root}"
81+
fi
82+
7883
# Run early setup hooks, if they exist
7984
if [ -d /libexec/early-setup.d ]; then
8085
tput clear

0 commit comments

Comments
 (0)