Skip to content

Commit

Permalink
feat: add bash completions
Browse files Browse the repository at this point in the history
This will complete started commands and provide a list of possible commands and options.
  • Loading branch information
TobiPeterG committed Jul 8, 2024
1 parent d24067a commit 4ab8cdc
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 67 deletions.
86 changes: 86 additions & 0 deletions completion/sdbootutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
shopt -s nullglob
# shellcheck disable=SC2148
err() {
:
}

_find_kernels()
{
local fn kv
declare -ga result
result=()

for fn in /usr/lib/modules/*/"$image"; do
kv="${fn%/*}"
kv="${kv##*/}"
result+=("$kv")
done
}

_arch_name() {
declare -ga result
result=("${!arch_image_map[@]}")
}

_image_name() {
declare -ga result
result=()
result=("${!arch_image_map[@]}")
}

_sdbootutil_completion() {
eval "$(sdbootutil _print_bash_completion_data)"
local cur prev words cword image
COMPREPLY=()
_get_comp_words_by_ref -n : cur prev words cword

local command_found=0
eval_bootctl
set_image_name
define_commands

define_options
IFS=, read -r -a opts <<< "$opts_long"
local opts_with_dashes=()
for opt in "${opts[@]}"; do
opts_with_dashes+=("--${opt//:/}")
done

local i=0

for word in "${words[@]:1:$cword-1}"; do
if [ -n "${commands["$word"]+yes}" ]; then
command_found=1
fi
if [ " --arch " == " $word " ]; then
# shellcheck disable=SC2034
firmware_arch="${words[i+2]}"
set_image_name
fi
if [ " --image " == " $word " ]; then
image="${words[i+2]}"
fi
((i++))
done

local opt_arg_fun="${option_requires_arg[${prev#--}]}"
if [ "${commands["$prev"]}" == "kernel" ]; then
_find_kernels
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${result[*]}" -- "$cur") )
elif [ "$opt_arg_fun" == "_path" ]; then
# shellcheck disable=SC2207
COMPREPLY=($(compgen -d -- "$cur"))
elif [ "$opt_arg_fun" ]; then
eval "$opt_arg_fun"
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${result[*]}" -- "$cur") )
elif [[ $cur == -* ]] || [ $command_found -eq 1 ]; then
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${opts_with_dashes[*]}" -- "$cur") )
elif [ $command_found -eq 0 ]; then
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "${!commands[*]}" -- "$cur") )
fi
}
complete -F _sdbootutil_completion sdbootutil
236 changes: 169 additions & 67 deletions sdbootutil
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,27 @@ find_kernels()
done
}
set_image_name() {
[ -z "$image" ] || return
declare -gA arch_image_map=(
[x64]="vmlinuz"
[aa64]="Image"
)
if [ -n "${arch_image_map[$firmware_arch]}" ]; then
image="${arch_image_map[$firmware_arch]}"
else
err "Unsupported architecture $firmware_arch"
fi
}
eval_bootctl()
{
# XXX: bootctl should have json output for that too
eval "$(bootctl 2>/dev/null | sed -ne 's/Firmware Arch: *\(\w\+\)/firmware_arch="\1"/p;s/ *token: *\(\w\+\)/entry_token="\1"/p;s, *\$BOOT: *\([^ ]\+\).*,boot_root="\1",p')"
}
# map that uses expected path on the ESP for each installed kernel as key. The
# value is the entry id if an entry exists.
declare -A installed_kernels
Expand Down Expand Up @@ -2014,10 +2035,76 @@ main_menu()
done
}
define_commands() {
declare -gA commands=(
[install]=""
[needs-update]=""
[update]=""
[force-update]=""
[add-kernel]=kernel
[remove-kernel]=kernel
[set-default-snapshot]=""
[add-all-kernels]=""
[remove-all-kernels]=""
[is-installed]=""
[list-snapshots]=""
[list-entries]=""
[list-kernels]=""
[show-entry]=""
[is-bootable]=""
[update-predictions]=""
[kernels]=interactive
[snapshots]=interactive
[entries]=interactive
[mkinitrd]=""
[bootloader]=""
)
}
define_options() {
declare -gA option_requires_arg=(
[help]=""
[flicker]=""
[verbose]=""
[esp-path]="_path"
[entry-token]="_path"
[arch]="_arch_name"
[image]="_image_name"
[entry-keys]="_find_kernels"
[no-variables]=""
[no-reuse-initrd]=""
[no-random-seed]=""
[ask-pin]=""
[portable]=""
[all]=""
)
opts_long=""
for opt in "${!option_requires_arg[@]}"; do
if [ "${option_requires_arg[$opt]}" ]; then
opts_long+="${opt}:,"
else
opts_long+="${opt},"
fi
done
opts_long="${opts_long%,}"
}
####### main #######
getopttmp=$(getopt -o hc:v --long help,flicker,verbose,esp-path:,entry-token:,arch:,image:,entry-keys:,no-variables,no-reuse-initrd,no-random-seed,ask-pin,portable,all -n "${0##*/}" -- "$@")
eval set -- "$getopttmp"
if [ "$1" = "_print_bash_completion_data" ]; then
declare -f set_image_name
declare -f eval_bootctl
declare -f define_commands
declare -f define_options
exit 0
fi
define_options
getopt_tmp=$(getopt -o hc:v --long "$opts_long" -n "${0##*/}" -- "$@")
eval set -- "$getopt_tmp"
while true ; do
case "$1" in
Expand Down Expand Up @@ -2049,16 +2136,16 @@ if [ -z "$SYSTEMD_LOG_LEVEL" -a -n "$verbose" ]; then
export SYSTEMD_LOG_LEVEL
fi
case "$1" in
install|needs-update|update|force-update|add-kernel|remove-kernel|set-default-snapshot|add-all-kernels|mkinitrd|remove-all-kernels|is-installed|list-snapshots|list-entries|list-kernels|show-entry|is-bootable|update-predictions|bootloader) ;;
kernels|snapshots|entries|"") stty_size; interactive=1 ;;
*) err "unknown command $1" ;;
esac
define_commands
if [ -z "$1" ] || [ "${commands["$1"]}" = "interactive" ]; then
stty_size
interactive=1
elif [ -z ${commands["$1"]+yes} ]; then
err "unknown command $1"
fi
[ -n "$arg_esp_path" ] && export SYSTEMD_ESP_PATH="$arg_esp_path"
# XXX: bootctl should have json output for that too
eval "$(bootctl 2>/dev/null | sed -ne 's/Firmware Arch: *\(\w\+\)/firmware_arch="\1"/p;s/ *token: *\(\w\+\)/entry_token="\1"/p;s, *\$BOOT: *\([^ ]\+\).*,boot_root="\1",p')"
eval_bootctl
read -r root_uuid root_device < <(findmnt / -v -r -n -o UUID,SOURCE)
root_subvol=""
subvol_prefix=""
Expand All @@ -2083,11 +2170,7 @@ fi
[ -n "$root_subvol" ] || [ -z "$have_snapshots" ] || err "Can't determine root subvolume"
[ -n "$root_device" ] || err "Can't determine root device"
[ -n "$firmware_arch" ] || err "Can't determine firmware arch"
case "$firmware_arch" in
x64) image=vmlinuz ;;
aa64) image=Image ;;
*) err "Unsupported architecture $firmware_arch" ;;
esac
set_image_name
# XXX: Unify both in /EFI/opensuse?
if [ -n "$arg_portable" ]; then
Expand All @@ -2110,57 +2193,76 @@ if [ "$SDB_ADD_INITIAL_COMPONENT" = "1" ]; then
backup_initial_components
fi
if [ "$1" = "install" ]; then
install_bootloader "${2:-$root_snapshot}"
elif [ "$1" = "needs-update" ]; then
bootloader_needs_update "${2:-$root_snapshot}"
elif [ "$1" = "update" ]; then
if bootloader_needs_update "${2:-$root_snapshot}"; then install_bootloader "${2:-$root_snapshot}"; else :; fi
elif [ "$1" = "force-update" ]; then
if is_installed; then install_bootloader "${2:-$root_snapshot}"; else :; fi
elif [ "$1" = "bootloader" ]; then
bootloader_name "${2:-$root_snapshot}"
elif [ "$1" = "add-kernel" ]; then
install_kernel "${3:-$root_snapshot}" "$2"
elif [ "$1" = "add-all-kernels" ]; then
install_all_kernels "${2:-$root_snapshot}"
elif [ "$1" = "mkinitrd" ]; then
arg_no_reuse_initrd=1
install_all_kernels "${2:-$root_snapshot}"
elif [ "$1" = "remove-kernel" ]; then
remove_kernel "${3:-$root_snapshot}" "$2"
elif [ "$1" = "remove-all-kernels" ]; then
remove_all_kernels "${2:-$root_snapshot}"
elif [ "$1" = "set-default-snapshot" ]; then
set_default_snapshot "${2:-$root_snapshot}"
elif [ "$1" = "is-installed" ]; then
if is_installed; then
log_info "systemd-boot was installed using sdbootutil"
exit 0
else
log_info "not installed using this tool"
exit 1
fi
elif [ "$1" = "list-kernels" ]; then
list_kernels "${2:-$root_snapshot}"
elif [ "$1" = "list-entries" ]; then
list_entries "${2:-}"
elif [ "$1" = "list-snapshots" ]; then
list_snapshots
elif [ "$1" = "show-entry" ]; then
show_entry_fields "${3:-$root_snapshot}" "$2"
elif [ "$1" = "is-bootable" ]; then
is_bootable "${2:-$root_snapshot}"
elif [ "$1" = "update-predictions" ]; then
update_predictions=1
elif [ "$1" = "kernels" ]; then
show_kernels "${2:-$root_snapshot}"
elif [ "$1" = "snapshots" ]; then
show_snapper
elif [ "$1" = "entries" ]; then
show_entries
else
main_menu
fi
case "$1" in
"install")
install_bootloader "${2:-$root_snapshot}"
;;
"needs-update")
bootloader_needs_update "${2:-$root_snapshot}"
;;
"update")
if bootloader_needs_update "${2:-$root_snapshot}"; then install_bootloader "${2:-$root_snapshot}"; fi
;;
"force-update")
if is_installed; then install_bootloader "${2:-$root_snapshot}"; else :; fi
;;
"bootloader")
bootloader_name "${2:-$root_snapshot}"
;;
"add-kernel")
install_kernel "${3:-$root_snapshot}" "$2"
;;
"add-all-kernels" | "mkinitrd")
[[ "$1" = "mkinitrd" ]] && arg_no_reuse_initrd=1
install_all_kernels "${2:-$root_snapshot}"
;;
"remove-kernel")
remove_kernel "${3:-$root_snapshot}" "$2"
;;
"remove-all-kernels")
remove_all_kernels "${2:-$root_snapshot}"
;;
"set-default-snapshot")
set_default_snapshot "${2:-$root_snapshot}"
;;
"is-installed")
if is_installed; then
log_info "systemd-boot was installed using sdbootutil"
exit 0
else
log_info "not installed using this tool"
exit 1
fi
;;
"list-kernels")
list_kernels "${2:-$root_snapshot}"
;;
"list-entries")
list_entries "${2:-}"
;;
"list-snapshots")
list_snapshots
;;
"show-entry")
show_entry_fields "${3:-$root_snapshot}" "$2"
;;
"is-bootable")
is_bootable "${2:-$root_snapshot}"
;;
"update-predictions")
update_predictions=1 ;;
"kernels")
show_kernels "${2:-$root_snapshot}"
;;
"snapshots")
show_snapper
;;
"entries")
show_entries
;;
*)
main_menu
;;
esac
[ -z "$update_predictions" ] || generate_tpm2_predictions
19 changes: 19 additions & 0 deletions sdbootutil.spec
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ Requires: /usr/bin/kernel-install
Plugin script for kernel-install. Note: installation of this
package may disable other plugin scripts that are incompatible.

%package bash-completion
Summary: Bash completions for sdbootutil
Requires: sdbootutil >= %{version}-%{release}
Requires: bash
Requires: bash-completion

%description bash-completion
Bash completions script for sdbootutil.
Allows the user to press TAB to see available commands,
options and parameters.

%prep
%setup -q

Expand All @@ -86,6 +97,9 @@ package may disable other plugin scripts that are incompatible.
%install
install -D -m 755 sdbootutil %{buildroot}%{_bindir}/sdbootutil

#bash completions
install -D -m 755 completion/sdbootutil %{buildroot}%{_datadir}/bash-completion/completions/sdbootutil

# services
for i in sdbootutil-update-predictions.service; do
install -D -m 644 "$i" %{buildroot}%{_unitdir}/"$i"
Expand Down Expand Up @@ -154,4 +168,9 @@ sdbootutil update
%ghost %config(noreplace,missingok) /etc/kernel/install.d/51-dracut-rescue.install
%ghost %config(noreplace,missingok) /etc/kernel/install.d/90-loaderentry.install

%files bash-completion
%dir %{_datadir}/bash-completion
%dir %{_datadir}/bash-completion/completions
%{_datadir}/bash-completion/completions/sdbootutil

%changelog

0 comments on commit 4ab8cdc

Please sign in to comment.