Skip to content

Commit 1fe8c4c

Browse files
committed
Add support for mkinitcpio
A mkinitcpio hook (and its install script) under the `initcpio` directory now allows a ZBM image to be created without dracut. This provides a couple of benefits over the dracut dependency: - As dracut gets further centered on systemd, ZBM needs an alternative - The mkinitcpio process is greatly simplified and more configurable - Arch users can build images with their native generator
1 parent e1bd708 commit 1fe8c4c

File tree

2 files changed

+289
-0
lines changed

2 files changed

+289
-0
lines changed

initcpio/hooks/zfsbootmenu

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
run_hook() {
4+
# This hook is run in busybox ash; just jump into the bash entrypoint
5+
exec bash /libexec/zfsbootmenu-initcpio
6+
}

initcpio/install/zfsbootmenu

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
#!/bin/bash
2+
3+
add_terminfo() {
4+
# Populate some basic terminfo databases
5+
# Shamelessly ripped from dracut
6+
7+
local tt troot
8+
9+
# Find the first path that contains a linux terminfo
10+
for tt in /etc /usr/share /lib; do
11+
if [[ -r "${tt}/terminfo/l/linux" ]]; then
12+
troot="${tt}/terminfo"
13+
break
14+
fi
15+
done
16+
17+
[[ -d "${troot}" ]] || return
18+
19+
# At this point, at least l/linux is guaranteed to exist
20+
for tt in "l/linux" "v/vt100" "v/vt102" "v/vt220"; do
21+
[[ -r "${troot}/${tt}" ]] || continue
22+
add_file "${troot}/${tt}" "/usr/share/terminfo/${tt}"
23+
done
24+
}
25+
26+
add_optional_module() {
27+
# Add a kernel module to the initcpio image only if the module
28+
# actually exists as a loadable file. Otherwise, ignore the module.
29+
30+
# Without a version, no module is added
31+
[[ $KERNELVERSION == none ]] && return 0
32+
33+
# Strip any extension, normalize name
34+
local target="${1%.ko*}"
35+
target="${target//-/_}"
36+
37+
# Try to determine path to module, if there is one
38+
local kfile
39+
kfile="$( modinfo -k "${KERNELVERSION}" -n "${target}" 2>/dev/null )" || return 0
40+
41+
# If module has a valid path, try to add it properly
42+
case "${kfile}" in
43+
/*) add_module "${target}" ;;
44+
*) return 0 ;;
45+
esac
46+
}
47+
48+
add_zbm_binaries() {
49+
local binaries mustcopy
50+
51+
binaries=(
52+
zfs zpool zdb mount.zfs
53+
kexec hostid od mount lsblk blkid dmesg
54+
mkdir mktemp chmod ps env stty tput
55+
less head tail cat tac sort sed grep tr awk fold
56+
insmod modinfo depmod lsmod fzf bash setsid
57+
)
58+
59+
# Optional mbuffer binary
60+
command -v mbuffer >/dev/null 2>&1 && add_binary mbuffer
61+
62+
# Hard requirements
63+
case "${zfsbootmenu_miser}" in
64+
1|[Yy]|[Yy][Ee][Ss]|[Oo][Nn])
65+
# Try to figure out what busybox provides
66+
;;
67+
*)
68+
# Don't be a miser, use system versions
69+
map add_binary "${binaries[@]}"
70+
return
71+
;;
72+
esac
73+
74+
# Figure out which binaries busybox does *not* provide
75+
readarray -t mustcopy < <(comm -23 \
76+
<(printf "%s\n" "${binaries[@]}" | sort) \
77+
<(/usr/lib/initcpio/busybox --list | sort))
78+
79+
# Copy the missing
80+
map add_binary "${mustcopy[@]}"
81+
}
82+
83+
create_zbm_conf() {
84+
# Create core ZBM configuration file
85+
86+
local endian
87+
local ival="$( echo -n 3 | od -tx2 -N2 -An | tr -d '[:space:]' )"
88+
if [ "${ival}" = "3300" ]; then
89+
endian="be"
90+
else
91+
if [ "${ival}" != "0033" ]; then
92+
warning "unable to determine platform endianness; assuming little-endian"
93+
fi
94+
endian="le"
95+
fi
96+
97+
local has_refresh
98+
if echo "abc" | fzf -f "abc" --bind "alt-l:refresh-preview" --exit-0 >/dev/null 2>&1; then
99+
has_refresh=1
100+
fi
101+
102+
cat > "${BUILDROOT}/etc/zfsbootmenu.conf" <<-'EOF'
103+
# Include guard
104+
[ -n "${_ETC_ZFSBOOTMENU_CONF}" ] && return
105+
readonly _ETC_ZFSBOOTMENU_CONF=1
106+
EOF
107+
108+
cat >> "${BUILDROOT}/etc/zfsbootmenu.conf" <<-EOF
109+
export BYTE_ORDER="${endian:-le}"
110+
export HAS_REFRESH="${has_refresh}"
111+
EOF
112+
}
113+
114+
create_zbm_profiles() {
115+
# Create shell profiles for ZBM
116+
117+
cat > "${BUILDROOT}/etc/profile" <<-EOF
118+
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
119+
export TERM=linux
120+
export HOME=/root
121+
EOF
122+
123+
mkdir -p "${BUILDROOT}/root"
124+
cat > "${BUILDROOT}/root/.bashrc" <<-EOF
125+
source /etc/zfsbootmenu.conf >/dev/null 2>&1
126+
source /lib/kmsg-log-lib.sh >/dev/null 2>&1
127+
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1
128+
source /lib/zfsbootmenu-kcl.sh >/dev/null 2>&1
129+
[ -f /etc/profile ] && source /etc/profile
130+
[ -f /lib/zfsbootmenu-completions.sh ] && source /lib/zfsbootmenu-completions.sh
131+
132+
export PS1="\033[0;33mzfsbootmenu\033[0m \w > "
133+
134+
alias clear="tput clear"
135+
alias reset="tput reset"
136+
alias zbm="zfsbootmenu"
137+
alias logs="ztrace"
138+
alias trace="ztrace"
139+
alias debug="ztrace"
140+
alias help="/libexec/zfsbootmenu-help -L recovery-shell"
141+
142+
zdebug "sourced /root/.bashrc" || true
143+
EOF
144+
}
145+
146+
create_zbm_entrypoint() {
147+
# Create an entrypoint to initialize the ZBM environment
148+
149+
mkdir -p "${BUILDROOT}/libexec"
150+
cat > "${BUILDROOT}/libexec/zfsbootmenu-initcpio" <<-'EOF'
151+
#!/bin/bash
152+
hooks=(
153+
/lib/zfsbootmenu-parse-commandline.sh
154+
/lib/zfsbootmenu-preinit.sh
155+
)
156+
157+
for hook in "${hooks[@]}"; do
158+
[ -r "${hook}" ] && source "${hook}" && continue
159+
160+
echo "ERROR: failed to load hook "${hook}"; good luck..."
161+
exec /bin/bash
162+
done
163+
EOF
164+
165+
chmod 755 "${BUILDROOT}/libexec/zfsbootmenu-initcpio"
166+
}
167+
168+
create_zbm_traceconf() {
169+
local zbm_prof_lib
170+
171+
# Enable performance profiling if configured and available
172+
case "${zfsbootmenu_trace_enable}" in
173+
1|[Yy]|[Yy][Ee][Ss]|[Oo][Nn])
174+
zbm_prof_lib="${zfsbootmenu_module_root}/profiling/profiling-lib.sh"
175+
;;
176+
*)
177+
;;
178+
esac
179+
180+
if ! [ -r "${zbm_prof_lib}" ]; then
181+
echo "return 0" > "${BUILDROOT}/lib/profiling-lib.sh"
182+
return
183+
fi
184+
185+
add_file "${zbm_prof_lib}" "/lib/profiling-lib.sh"
186+
187+
cat > "${BUILDROOT}/etc/profiling.conf" <<-EOF
188+
export zfsbootmenu_trace_term=${zfsbootmenu_trace_term}
189+
export zfsbootmenu_trace_baud=${zfsbootmenu_trace_baud}
190+
EOF
191+
}
192+
193+
# TODO: confirm proper return code on installation failures
194+
build() {
195+
local hooks relative _file
196+
197+
# TODO: fix dracut-specific path
198+
: ${zfsbootmenu_module_root:=/usr/lib/dracut/modules.d/90zfsbootmenu}
199+
200+
# Modules required for ZBM operation
201+
map add_module zfs zcommon znvpair zavl zunicode zlua icp spl
202+
203+
# Optional modules
204+
map add_optional_module zlib_deflate zlib_inflate
205+
206+
# Binaries required for ZBM operation
207+
add_zbm_binaries
208+
209+
# Necessary udev rules
210+
map add_file \
211+
/usr/lib/udev/rules.d/60-zvol.rules \
212+
/usr/lib/udev/rules.d/69-vdev.rules \
213+
/usr/lib/udev/rules.d/90-zfs.rules
214+
215+
# This helper tends to be used by the udev rules
216+
[[ -f /usr/lib/udev/vdev_id ]] && add_file /usr/lib/udev/vdev_id
217+
218+
# TODO: figure out if libgcc_s.so.1 is necessary and add it
219+
220+
# On-line documentation
221+
while read -r doc; do
222+
relative="${doc#"${zfsbootmenu_module_root}/"}"
223+
[ "${relative}" = "${doc}" ] && continue
224+
add_file "${doc}" "/usr/share/docs/${relative}"
225+
done <<< "$( find "${zfsbootmenu_module_root}/help-files" -type f )"
226+
227+
# Install core ZBM functionality
228+
for _file in "${zfsbootmenu_module_root}"/lib/*; do
229+
add_file "${_file}" "/lib/${_file##*/}"
230+
done
231+
232+
for _file in "${zfsbootmenu_module_root}"/libexec/*; do
233+
add_file "${_file}" "/libexec/${_file##*/}"
234+
done
235+
236+
for _file in "${zfsbootmenu_module_root}"/bin/*; do
237+
add_file "${_file}" "/bin/${_file##*/}"
238+
done
239+
240+
hooks=( zfsbootmenu-{parse-commandline,preinit}.sh )
241+
for _file in "${hooks[@]}"; do
242+
add_file "${zfsbootmenu_module_root}/hook/${_file}" "/lib/${_file}"
243+
done
244+
245+
# allow mount(8) to "autodetect" ZFS
246+
echo 'zfs' >>"${BUILDROOT}/etc/filesystems"
247+
248+
for _file in "${zfsbootmenu_early_setup[@]}"; do
249+
[ -x "${_file}" ] || continue
250+
add_file "${_file}" "/libexec/early-setup.d/${_file##*/}"
251+
done
252+
253+
for _file in "${zfsbootmenu_setup[@]}"; do
254+
[ -x "${_file}" ] || continue
255+
add_file "${_file}" "/libexec/setup.d/${_file##*/}"
256+
done
257+
258+
for _file in "${zfsbootmenu_teardown[@]}"; do
259+
[ -x "${_file}" ] || continue
260+
add_file "${_file}" "/libexec/teardown.d/${_file##*/}"
261+
done
262+
263+
# Copy host-specific ZFS configs
264+
[[ -f /etc/hostid ]] && add_file "/etc/hostid"
265+
[[ -f /etc/zfs/zpool.cache ]] && add_file "/etc/zfs/zpool.cache"
266+
[[ -f /etc/zfs/vdev_id.conf ]] && add_file "/etc/zfs/vdev_id.conf"
267+
[[ -f /etc/modprobe.d/zfs.conf ]] && add_file "/etc/modprobe.d/zfs.conf"
268+
269+
add_terminfo
270+
271+
create_zbm_conf
272+
create_zbm_profiles
273+
create_zbm_traceconf
274+
create_zbm_entrypoint
275+
276+
add_runscript
277+
}
278+
279+
help() {
280+
echo "This hook turns the initramfs into a ZFSBootMenu image"
281+
}
282+
283+
# vim: set ts=4 sw=4 ft=sh et:

0 commit comments

Comments
 (0)