Merge pull request #15 from zdykstra/cleaner-mount

Core update/feature bump
This commit is contained in:
Zach Dykstra 2020-01-11 19:41:38 -06:00 committed by GitHub
commit f2a8856a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 93 deletions

View File

@ -80,6 +80,7 @@ install() {
dracut_install /usr/bin/tail
dracut_install /usr/lib/udev/zvol_id
inst_simple "${moddir}/zfsbootmenu-lib.sh" "/lib/zfsbootmenu-lib.sh"
inst_simple "${moddir}/zfsbootmenu-preview.sh" "/bin/zfsbootmenu-preview.sh"
inst_hook cmdline 95 "${moddir}/zfsbootmenu-parse-commandline.sh"
inst_hook pre-mount 90 "${moddir}/zfsbootmenu.sh"

View File

@ -2,26 +2,6 @@
# ZFS boot menu functions
# arg1: zroot/ROOT/bootenvironment
# prints: zroot_ROOT_bootenvironment
# returns: No return value
underscore() {
local bepath
bepath="${1}"
echo ${bepath} | sed 's,/,_,g'
}
# arg1: zroot_ROOT_bootenvironment
# prints: zroot/ROOT/bootenvironment
# returns: No return value
slash() {
local bepath
bepath="${1}"
echo ${bepath} | sed 's,_,/,g'
}
# arg1: ZFS filesystem name
# arg2: mountpoint
# prints: No output
@ -31,31 +11,18 @@ mount_zfs() {
local fs mnt ret
fs="${1}"
mnt="${2}"
test -d ${mnt} || return 1
mount -o zfsutil -t zfs ${fs} ${mnt}
mnt="${BASE}/${fs}/mnt"
test -d "${mnt}" || mkdir -p "${mnt}"
mount -o zfsutil -t zfs "${fs}" "${mnt}"
ret=$?
echo "${mnt}"
return ${ret}
}
# arg1: mount point
# prints: No output
# returns: 0 on success
umount_zfs() {
local mnt ret
mnt="${1}"
umount ${mnt}
ret=$?
return ${ret}
}
# arg1: Path to file with header/options text
# arg2: Path to file with detected boot environments, 1 per line
# arg1: Path to file with detected boot environments, 1 per line
# prints: key pressed, boot environment
# returns: 130 on error, 0 otherwise
@ -63,13 +30,14 @@ draw_be() {
local env header selected ret
env="${1}"
header="${2}"
test -f ${env} || return 130
test -f ${header} || return 130
selected="$( cat ${header} ${env} | fzf -0 --prompt "BE > " \
--header-lines=2 --expect=alt-k,alt-s,alt-a,alt-r )"
selected="$( cat ${env} | fzf -0 --prompt "BE > " \
--expect=alt-k,alt-s,alt-a,alt-r,alt-d \
--preview-window=up:2 \
--header="[ENTER] boot [ALT+K] kernel [ALT+D] set bootfs [ALT+S] snapshots" \
--preview="zfsbootmenu-preview.sh ${BASE} {} ${BOOTFS}")"
ret=$?
while read -r line; do
if [ -z "$line" ]; then
@ -90,13 +58,8 @@ draw_kernel() {
benv="${1}"
# Set a pretty benv for our prompt
pretty="$( slash ${benv})"
pretty="${pretty#${BASE}/}"
selected="$( cat ${benv} | fzf --prompt "${pretty} > " --tac \
--with-nth=2 --header="[ENTER] boot
[ESC] back")"
selected="$( cat ${BASE}/${benv}/kernels | fzf --prompt "${benv} > " --tac \
--with-nth=2 --header="[ENTER] boot [ESC] back")"
ret=$?
echo "${selected}"
@ -113,8 +76,7 @@ draw_snapshots() {
benv="${1}"
selected="$( zfs list -t snapshot -H -o name ${benv} | fzf --prompt "Snapshot > " --tac \
--header="[ENTER] clone
[ESC] back" )"
--header="[ENTER] clone [ESC] back" )"
ret=$?
echo "${selected}"
return ${ret}
@ -136,19 +98,23 @@ kexec_kernel() {
# initramfs
IFS=' ' read fs kernel initramfs <<<"${selected}"
mount_zfs ${fs} ${BASE_MOUNT}
mnt="$( mount_zfs "${fs}" )"
ret=$?
if [ $ret != 0 ]; then
emergency_shell "unable to mount ${fs}"
fi
test -e ${BASE_MOUNT}/etc/default/grub && . ${BASE_MOUNT}/etc/default/grub
while IFS= read -r line
do
cli_args="${line}"
done < "${BASE}/${fs}/default_args"
kexec -l ${BASE_MOUNT}${kernel} \
--initrd=${BASE_MOUNT}/${initramfs} \
--command-line="root=zfs:${fs} ${GRUB_CMDLINE_LINUX_DEFAULT}"
kexec -l ${mnt}${kernel} \
--initrd=${mnt}/${initramfs} \
--command-line="root=zfs:${fs} ${cli_args}"
umount_zfs ${fs}
umount ${mnt}
# Export if read-write, to ensure a clean pool
pool="${selected%%/*}"
@ -170,7 +136,7 @@ clone_snapshot() {
pool="${selected%%/*}"
# If the pool is read-only, flip the import arg off and, export then import
# If the pool is read-only, flip the arg off then export and import
if [ "$( zpool get -H -o value readonly ${pool} )" = "on" ]; then
export_pool "${pool}"
import_args="${import_args/readonly=on/readonly=off}"
@ -179,6 +145,16 @@ clone_snapshot() {
target="${selected/@/_}"
if $( zfs list -H -o name | grep -q ${target} ); then
last_env="$( zfs list -H -o name | grep ${target} | tail -1 )"
index="${last_env##${target}_}"
index="$(( ${index} + 1 ))"
else
index="0"
fi
target="$( printf "%s_%0.3d" ${target} ${index} )"
zfs clone -o mountpoint=/ \
-o canmount=noauto \
${selected} ${target}
@ -187,7 +163,7 @@ clone_snapshot() {
if [ $ret -eq 0 ]; then
key_wrapper "${target}"
if [ $? -eq 0 ]; then
if output=$( find_be_kernels "${target}" "${BASE_MOUNT}" ); then
if output=$( find_be_kernels "${target}" ); then
echo "${target}" >> ${BASE}/env
return 0
else
@ -204,33 +180,47 @@ clone_snapshot() {
fi
}
set_default_env() {
local selected
selected="${1}"
pool="${selected%%/*}"
# If the pool is read-only, flip the arg off then export and import
if [ "$( zpool get -H -o value readonly ${pool} )" = "on" ]; then
export_pool "${pool}"
import_args="${import_args/readonly=on/readonly=off}"
import_pool "${pool}"
key_wrapper "${pool}"
fi
if output="$( zpool set bootfs=${selected} ${pool} )"; then
BOOTFS="${selected}"
fi
}
# arg1: ZFS filesystem
# arg2: mountpoint
# prints: nothing
# returns: number of kernels found
# returns: 0 if kernels were found
find_be_kernels() {
local fs mnt
fs="${1}"
mnt="${2}"
local sane kernel version pairs
local sane kernel version kernel_records
pairs=()
# Check if /boot even exists in the environment
mount_zfs "${fs}" "${mnt}"
mnt="$( mount_zfs "${fs}" )"
kernel_records="${mnt/mnt/kernels}"
if [ ! -d "${mnt}/boot" ]; then
umount_zfs "${fs}"
return
umount "${mnt}"
return 1
fi
# Create a filename with out /'s
sane="$( underscore ${fs} )"
# Remove this file if it already exists
test -f ${BASE}/${sane} && rm ${BASE}/${sane}
for kernel in $( ls ${mnt}/boot/vmlinux-* \
${mnt}/boot/vmlinuz-* \
${mnt}/boot/kernel-* \
@ -243,16 +233,26 @@ find_be_kernels() {
"initrd-${version}" "initramfs-${version}.img"; do
if test -e "${mnt}/boot/${i}" ; then
echo "${fs} ${kernel} /boot/${i}" >> ${BASE}/${sane}
echo "${fs} ${kernel} /boot/${i}" >> "${kernel_records}"
break
fi
done
done
umount_zfs "${fs}"
defaults="$( select_kernel "${fs}" )"
IFS=' ' read def_fs def_kernel def_initramfs <<<"${defaults}"
def_kernel="$( basename "${def_kernel}" )"
def_version=$( echo $def_kernel | sed -e "s,^[^0-9]*-,,g" )
# Return the number of kernels found
return "${#pairs[@]}"
def_kernel_file="${mnt/mnt/default_kernel}"
echo "${def_version}" > "${def_kernel_file}"
def_args="$( find_kernel_args "${mnt}" )"
def_args_file="${mnt/mnt/default_args}"
echo "${def_args}" > "${def_args_file}"
umount "${mnt}"
return 0
}
# arg1: ZFS filesystem
@ -264,13 +264,12 @@ select_kernel() {
zfsbe="${1}"
local sane specific_kernel kexec_args
sane="$( underscore ${zfsbe} )"
specific_kernel="$( zfs get -H -o value org.zfsbootmenu:kernel ${zfsbe} )"
# No value set, pick the last kernel entry
if [ "${specific_kernel}" = "-" ]; then
kexec_args="$( tail -1 ${BASE}/${sane} )"
kexec_args="$( tail -1 ${BASE}/${zfsbe}/kernels )"
else
while read -r kexec_args; do
local fs kernel initramfs
@ -278,12 +277,30 @@ select_kernel() {
if [[ "${kernel}" =~ "${specific_kernel}" ]]; then
break
fi
done <<<"$( cat ${BASE}/${sane} )"
done <<<"$( cat ${BASE}/${zfsbe}/kernels )"
fi
echo "${kexec_args}"
}
find_kernel_args() {
local zfsbe
zfsbe="${1}"
local arguments
if [ -f "${zfsbe}/etc/default/grub" ]; then
echo "$(
. "${zfsbe}/etc/default/grub" ;
echo "${GRUB_CMDLINE_LINUX_DEFAULT}"
)"
return
fi
# No arguments found, return something generic
echo "quiet loglevel=3"
}
# no arguments
# prints: nothing
# returns: number of pools that can be imported

View File

@ -40,6 +40,10 @@ else
menu_timeout=10
fi
if getargbool 1 die_on_import_failure ; then
info "ZFSBootMenu: Disabling die on import failure"
fi
wait_for_zfs=0
case "${root}" in
""|zfsbootmenu|zfsbootmenu:)

View File

@ -0,0 +1,32 @@
#!/bin/bash
BASE="${1}"
ENV="${2}"
BOOTFS="${3}"
BLUE='\033[0;34m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
while IFS= read -r line
do
selected_kernel="${line}"
done < "${BASE}/${ENV}/default_kernel"
while IFS= read -r line
do
selected_arguments="${line}"
done < "${BASE}/${ENV}/default_args"
if [[ "${BOOTFS}" =~ "${ENV}" ]]; then
selected_env_str="${ENV} (default) - ${selected_kernel}"
else
selected_env_str="${ENV} - ${selected_kernel}"
fi
# Left pad the strings to center them based on the preview width
selected_env_str="$( printf "%*s\n" $(( (${#selected_env_str} + FZF_PREVIEW_COLUMNS) / 2)) "${selected_env_str}" )"
selected_arguments="$( printf "%*s\n" $(( (${#selected_arguments} + FZF_PREVIEW_COLUMNS) / 2)) "${selected_arguments}" )"
echo -e "${GREEN}${selected_env_str}${NC}"
echo "${selected_arguments}"

View File

@ -1,6 +1,8 @@
#!/bin/bash
. /lib/zfsbootmenu-lib.sh
test -f /lib/zfsbootmenu-lib.sh && source /lib/zfsbootmenu-lib.sh
test -f zfsbootmenu-lib.sh && source zfsbootmenu-lib.sh
echo "Loading boot menu ..."
TERM=linux
@ -11,15 +13,7 @@ OLDIFS="$IFS"
export FZF_DEFAULT_OPTS="--layout=reverse-list --cycle \
--inline-info --tac"
BE_SELECTED=0
KERNEL_SELECTED=0
BASE="$( mktemp -d /tmp/zfs.XXXX )"
BASE_MOUNT="${BASE}/be"
mkdir ${BASE_MOUNT}
ENV_HEADER="[ALT+K] select kernel [ENTER] boot\n[ALT+A] all snapshots [ALT+S] BE snapshots"
echo -e ${ENV_HEADER} > ${BASE}/env_header
# I should probably just modprobe zfs right off the bat
# rootok is always 1 here, otherwise we wouldn't be here ...
@ -46,7 +40,10 @@ if [ $ret -gt 0 ]; then
emergency_shell "unable to successfully import a pool"
fi
else
emergency_shell "no pools available to import"
if [ ${die_on_import_failure} -eq 1 ]; then
emergency_shell "no pools available to import"
exit;
fi
fi
# Prefer a specific pool when checking for a bootfs value
@ -129,7 +126,7 @@ if [[ ! -z "${BOOTFS}" ]]; then
fi
# Generate a list of valid kernels for our bootfs
if output=$( find_be_kernels "${BOOTFS}" "${BASE_MOUNT}" ); then
if output=$( find_be_kernels "${BOOTFS}" ); then
# Automatically select a kernel and boot it
kexec_kernel "$( select_kernel "${BOOTFS}" )"
fi
@ -150,7 +147,7 @@ for FS in $( zfs list -H -o name,mountpoint | grep -E "/$" | cut -f1 ); do
fi
# Check for kernels under the mountpoint, add to our BE list
if output=$( find_be_kernels "${FS}" "${BASE_MOUNT}" ); then
if output="$( find_be_kernels "${FS}" )" ; then
echo ${FS} >> ${BASE}/env
fi
done
@ -160,9 +157,12 @@ if [ ! -f ${BASE}/env ]; then
fi
# This is the actual menuing system
BE_SELECTED=0
tput civis
while true; do
if [ ${BE_SELECTED} -eq 0 ]; then
bootenv="$( draw_be "${BASE}/env" "${BASE}/env_header")"
bootenv="$( draw_be "${BASE}/env" )"
ret=$?
# key press
@ -181,7 +181,7 @@ while true; do
exit
;;
"alt-k")
selected_kernel="$( draw_kernel ${BASE}/$( underscore ${selected_be} ) )"
selected_kernel="$( draw_kernel "${selected_be}" )"
ret=$?
if [ $ret -eq 130 ]; then
@ -191,6 +191,10 @@ while true; do
exit
fi
;;
"alt-d")
set_default_env "${selected_be}"
BE_SELECTED=0
;;
"alt-s")
selected_snap="$( draw_snapshots ${selected_be} )"
ret=$?