|
| 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