Skip to content

Commit

Permalink
recovery shell quality of life improvements
Browse files Browse the repository at this point in the history
The recovery shell is quite powerful, but discoverability can be
limited. In an attempt to start fixing that, the following has been
done:

* Add a `help` alias and documentation page listing a few key things
  that can be done in the recovery shell.

* Enable tab completions for the following: set_ro_pool, set_rw_pool,
  mount_zfs, zfs-chroot/zfs_chroot

* Only source aliases/tab completions in interactive shells so that we
  don't impact the normal ZBM shell environment.

Closes #197
  • Loading branch information
zdykstra committed Aug 21, 2021
1 parent 5876ca9 commit c1feb56
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 5 deletions.
31 changes: 31 additions & 0 deletions 90zfsbootmenu/help-files/134/recovery-shell.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
NAME

zfsbootmenu - Recovery Shell

Common Commands

zfsbootmenu
Launch the interactive boot environment menu.

zfs-chroot zfs filesystem
Enter a chroot of the specified boot environment. The boot environment is mounted read/write if the zpool is imported read/write.

set_rw_pool pool
Export, then re-import the pool read/write.

set_ro_pool pool
Export, then re-import the pool read-only.

mount_zfs zfs filesystem
Mount the filesystem at a unique location and print the mount point.

help
View the online help system.

logs
View warning/error/debug logs.

AUTHOR

ZFSBootMenu Team <https://github.com/zbm-dev/zfsbootmenu>

35 changes: 35 additions & 0 deletions 90zfsbootmenu/help-files/54/recovery-shell.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
NAME

zfsbootmenu - Recovery Shell

Common Commands

zfsbootmenu
Launch the interactive boot environment menu.

zfs-chroot zfs filesystem
Enter a chroot of the specified boot environment.
The boot environment is mounted read/write if the
zpool is imported read/write.

set_rw_pool pool
Export, then re-import the pool read/write.

set_ro_pool pool
Export, then re-import the pool read-only.

mount_zfs zfs filesystem
Mount the filesystem at a unique location and print
the mount point.

help
View the online help system.

logs
View warning/error/debug logs.

AUTHOR

ZFSBootMenu Team
<https://github.com/zbm-dev/zfsbootmenu>

32 changes: 32 additions & 0 deletions 90zfsbootmenu/help-files/94/recovery-shell.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
NAME

zfsbootmenu - Recovery Shell

Common Commands

zfsbootmenu
Launch the interactive boot environment menu.

zfs-chroot zfs filesystem
Enter a chroot of the specified boot environment. The boot environment is mounted read/write
if the zpool is imported read/write.

set_rw_pool pool
Export, then re-import the pool read/write.

set_ro_pool pool
Export, then re-import the pool read-only.

mount_zfs zfs filesystem
Mount the filesystem at a unique location and print the mount point.

help
View the online help system.

logs
View warning/error/debug logs.

AUTHOR

ZFSBootMenu Team <https://github.com/zbm-dev/zfsbootmenu>

20 changes: 19 additions & 1 deletion 90zfsbootmenu/module-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ install() {
_ret=0
# shellcheck disable=SC2154
inst_simple "${moddir}/zfsbootmenu-lib.sh" "/lib/zfsbootmenu-lib.sh" || _ret=$?
inst_simple "${moddir}/zfsbootmenu-completions.sh" "/lib/zfsbootmenu-completions.sh" || _ret=$?
inst_simple "${moddir}/zfsbootmenu-init.sh" "/libexec/zfsbootmenu-init" || _ret=$?
inst_simple "${moddir}/zfsbootmenu-preview.sh" "/libexec/zfsbootmenu-preview" || _ret=$?
inst_simple "${moddir}/zfsbootmenu-input.sh" "/libexec/zfsbootmenu-input" || _ret=$?
Expand Down Expand Up @@ -317,12 +318,29 @@ EOF
cat << EOF >> "${initdir}/etc/profile"
[ -f /lib/zfsbootmenu-lib.sh ] && source /lib/zfsbootmenu-lib.sh
[ -f /etc/zfsbootmenu.conf ] && source /etc/zfsbootmenu.conf
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
export TERM=vt220
zdebug "sourced /etc/profile"
EOF

# Setup a default environment for all login shells
cat << EOF >> "${initdir}/.bashrc"
[ -f /etc/profile ] && source /etc/profile
[ -f /lib/zfsbootmenu-completions.sh ] && source /lib/zfsbootmenu-completions.sh
export PS1="\033[0;33mzfsbootmenu\033[0m \w > "
alias clear="tput clear"
alias reset="tput reset"
alias zbm="zfsbootmenu"
alias logs="zlogtail -f"
alias logs="ztrace"
alias trace="ztrace"
alias debug="ztrace"
alias help="/libexec/zfsbootmenu-help -L recovery-shell"
zdebug "sourced /.bashrc"
EOF
}
72 changes: 72 additions & 0 deletions 90zfsbootmenu/zfsbootmenu-completions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash
# shellcheck disable=SC2207

# disabling this allows completions with the @ character
shopt -u hostcomplete

_zfs-chroot() {
local ZFS
COMPREPLY=()

[ "${#COMP_WORDS[@]}" != "2" ] && return

for SNAP in $( zfs list -H -o name -t snapshot ) ; do
ZFS+=("${SNAP}")
done

for FS in $( zfs list -H -o name ) ; do
ZFS+=("${FS}")
done

COMPREPLY=( $( compgen -W "${ZFS[*]}" -- "${COMP_WORDS[1]}" ) )
}
complete -F _zfs-chroot zfs-chroot
complete -F _zfs-chroot zfs_chroot

_set_rw_pool() {
local ZPOOL
COMPREPLY=()

[ "${#COMP_WORDS[@]}" != "2" ] && return

for POOL in $( zpool list -H -o name ) ; do
if ! is_writable "${POOL}" ; then
ZPOOL+=("${POOL}")
fi
done
COMPREPLY=( $( compgen -W "${ZPOOL[*]}" -- "${COMP_WORDS[1]}" ) )
}
complete -F _set_rw_pool set_rw_pool

_set_ro_pool() {
local ZPOOL
COMPREPLY=()

[ "${#COMP_WORDS[@]}" != "2" ] && return

for POOL in $( zpool list -H -o name ) ; do
if is_writable "${POOL}" ; then
ZPOOL+=("${POOL}")
fi
done
COMPREPLY=( $( compgen -W "${ZPOOL[*]}" -- "${COMP_WORDS[1]}" ) )
}
complete -F _set_ro_pool set_ro_pool

_mount_zfs() {
local ZFS
COMPREPLY=()

[ "${#COMP_WORDS[@]}" != "2" ] && return

for SNAP in $( zfs list -H -o name -t snapshot ) ; do
ZFS+=("${SNAP}")
done

for FS in $( zfs list -H -o name ) ; do
ZFS+=("${FS}")
done

COMPREPLY=( $( compgen -W "${ZFS[*]}" -- "${COMP_WORDS[1]}" ) )
}
complete -F _mount_zfs mount_zfs
3 changes: 2 additions & 1 deletion 90zfsbootmenu/zfsbootmenu-help.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ help_pager() {
SORTED+=("${FINAL}")

printf '%s\n' "${SORTED[@]}" | ${FUZZYSEL} \
--prompt 'Topic >' \
--prompt 'Topic > ' \
--with-nth=2.. \
--bind pgup:preview-up,pgdn:preview-down \
--preview="$0 -s {1}" \
--preview-window="right:${PREVIEW_SIZE}:wrap" \
--header="$( colorize green "[ESC]" ) $( colorize lightblue "back" )" \
--tac \
--inline-info \
--ansi
}

Expand Down
41 changes: 39 additions & 2 deletions 90zfsbootmenu/zfsbootmenu-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,33 @@ set_rw_pool() {
return 1
}

# arg1: pool name
# prints: nothing
# returns: 0 on success, 1 on failure

set_ro_pool() {
local pool ret

pool="${1}"
if [ -z "${pool}" ]; then
zerror "pool is undefined"
return 1
fi
zdebug "pool set to ${pool}"

if export_pool "${pool}" ; then
read_write='' import_pool "${pool}"
ret=$?

zdebug "import_pool: ${ret}"

return ${ret}
fi

return 1
}


# arg1: ZFS filesystem
# prints: name of encryption root, if present
# returns: 0 if system has an encryption root, 1 otherwise
Expand Down Expand Up @@ -2082,7 +2109,7 @@ zfs_chroot() {
# returns: nothing

emergency_shell() {
local message
local message skip mp fs
message=${1:-unknown reason}

tput clear
Expand All @@ -2091,7 +2118,17 @@ emergency_shell() {

echo -n "Launching emergency shell: "
echo -e "${message}\n"
/bin/bash -l

# -i (interactive) mode will source /.bashrc
/bin/bash -i

# shellcheck disable=SC2034
while read -r skip mp fs skip ; do
if [ "${fs}" == "zfs" ]; then
umount "${mp}"
zdebug "unmounting: ${mp}"
fi
done < /proc/self/mounts
}

# prints: nothing
Expand Down
2 changes: 1 addition & 1 deletion 90zfsbootmenu/zfsbootmenu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ while true; do
# This will make all keys in the pool unavailable, but populate_be_list
# should reload the missing keys in the next iteration, so why unlock here?
if is_writable "${pool}"; then
export_pool "${pool}" && read_write='' import_pool "${pool}"
set_ro_pool "${pool}"
else
set_rw_pool "${pool}"
fi
Expand Down
45 changes: 45 additions & 0 deletions pod/online/recovery-shell.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
=pod

=head1 NAME

B<zfsbootmenu> - Recovery Shell

=head1 Common Commands

=over 2

=item I<zfsbootmenu>

Launch the interactive boot environment menu.

=item I<zfs-chroot> B<zfs filesystem>

Enter a chroot of the specified boot environment. The boot environment is mounted I<read/write> if the zpool is imported I<read/write>.

=item I<set_rw_pool> B<pool>

Export, then re-import the pool I<read/write>.

=item I<set_ro_pool> B<pool>

Export, then re-import the pool I<read-only>.

=item I<mount_zfs> B<zfs filesystem>

Mount the filesystem at a unique location and print the mount point.

=item I<help>

View the online help system.

=item I<logs>

View warning/error/debug logs.

=back

=head2 AUTHOR

ZFSBootMenu Team L<https://github.com/zbm-dev/zfsbootmenu>

=cut

0 comments on commit c1feb56

Please sign in to comment.