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 Mar 21, 2024
1 parent 04bfbac commit da4439b
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 73 deletions.
70 changes: 70 additions & 0 deletions completion/sdbootutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
err() {
:
}

find_kernels()
{
local fn kv
found_kernels=()

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

eval "$(sdbootutil _print_bash_completion_data)"

_sdbootutil_completion() {
local cur prev words cword subvol_prefix image
COMPREPLY=()
_get_comp_words_by_ref -n : cur prev words cword

local command_found=0
eval_bootctl
set_image_name
define_commands
local i=0

for word in "${words[@]:1:$cword-1}"; do
if [[ ${command_types["$word"]} =~ ^(command|command_needing_kernel)$ ]]; then
command_found=1
fi
if [[ " --arch " == " $word " ]]; then
firmware_arch="${words[i+2]}"
set_image_name
fi
if [[ " --image " == " $word " ]]; then
image="${words[i+2]}"
fi
((i++))
done

if [[ ${command_types["$prev"]} == "command_needing_kernel" ]]; then
find_kernels
local kernel_versions="${found_kernels[@]}"
COMPREPLY=( $(compgen -W "${kernel_versions}" -- ${cur}) )
elif [[ "$prev" == "--arch" ]]; then
COMPREPLY=( $(compgen -W "x64 aa64" -- ${cur}) )
elif [[ "$prev" == "--image" ]]; then
COMPREPLY=( $(compgen -W "vmlinuz Image" -- ${cur}) )
elif [[ $cur == -* ]]; then
local opts_and_args
for key in "${!command_types[@]}"; do
if [[ ${command_types["$key"]} =~ ^(option|argument)$ ]]; then
opts_and_args+="$key "
fi
done
COMPREPLY=( $(compgen -W "${opts_and_args}" -- ${cur}) )
elif [[ $command_found -eq 0 ]]; then
local commands
for key in "${!command_types[@]}"; do
if [[ ${command_types["$key"]} =~ ^(option|argument|command|command_needing_kernel)$ ]]; then
commands+="$key "
fi
done
COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
fi
}
complete -F _sdbootutil_completion sdbootutil
171 changes: 98 additions & 73 deletions sdbootutil
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,6 @@ calc_chksum() {
set -- $(sha1sum "$1")
chksum="$1"
}
# map with kernel version as key and checksum as value
declare -A found_kernels
find_kernels()
Expand All @@ -832,6 +831,21 @@ find_kernels()
done
}
set_image_name()
{
case "$firmware_arch" in
x64) image=vmlinuz ;;
aa64) image=Image ;;
*) err "Unsupported architecture $firmware_arch" ;;
esac
}
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 @@ -1498,8 +1512,33 @@ main_menu()
done
}
define_commands() {
declare -gA command_types=(
[install]="command" [needs-update]="command" [update]="command" [force-update]="command"
[add-kernel]="command_needing_kernel" [remove-kernel]="command_needing_kernel"
[set-default-snapshot]="command" [add-all-kernels]="command"
[remove-all-kernels]="command" [is-installed]="command"
[list-snapshots]="command" [list-entries]="command"
[list-kernels]="command" [is-bootable]="command"
[update-predictions]="command" [kernels]="command_interactive"
[snapshots]="command_interactive" [entries]="command_interactive" [mkinitrd]="command"
[bootloader]="command"
[--help]="option" [--verbose]="option" [--flicker]="option"
[--esp-path]="argument" [--arch]="argument" [--entry-token]="argument"
[--image]="argument" [--no-variables]="option" [--no-reuse-initrd]="option"
[--no-random-seed]="option" [--all]="option"
)
}
####### main #######
if [[ "$1" == "_print_bash_completion_data" ]]; then
declare -f set_image_name
declare -f eval_bootctl
declare -f define_commands
exit 0
fi
getopttmp=$(getopt -o hc:v --long help,flicker,verbose,esp-path:,entry-token:,arch:,image:,no-variables,no-reuse-initrd,no-random-seed,all -n "${0##*/}" -- "$@")
eval set -- "$getopttmp"
Expand All @@ -1521,11 +1560,13 @@ while true ; do
esac
done
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|is-bootable|update-predictions|bootloader) ;;
kernels|snapshots|entries|"") stty_size; interactive=1 ;;
*) err "unknown command $1" ;;
esac
define_commands
if [[ -z "$1" || ${command_types["$1"]} == "command_interactive" ]]; then
stty_size
interactive=1
elif [[ -z ${command_types["$1"]} ]]; then
err "unknown command $1"
fi
for i in /etc/os-release /usr/lib/os-release; do
[ -f "$i" ] || continue
Expand All @@ -1534,9 +1575,7 @@ for i in /etc/os-release /usr/lib/os-release; do
done
[ -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=$(btrfs subvol show / 2>/dev/null|head -1)
subvol_prefix="${root_subvol%/.snapshots/*}"
Expand All @@ -1554,73 +1593,59 @@ settle_entry_token
[ -n "$root_device" ] || err "Can't determine root device"
[ -n "$entry_token" ] || err "No entry token. sd-boot not installed?"
[ -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
root_snapshot="${root_subvol#"${subvol_prefix}"/.snapshots/}"
root_snapshot="${root_snapshot%/snapshot}"
# XXX: Unify both in /EFI/opensuse?
if is_sdboot; then
boot_dst="/EFI/systemd"
elif is_grub2; then
boot_dst="/EFI/opensuse"
else
err "Bootloader not detected"
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" = "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 ;;
"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
7 changes: 7 additions & 0 deletions sdbootutil.spec
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ package may disable other plugin scripts that are incompatible.
%install
install -D -m 755 sdbootutil %{buildroot}%{_bindir}/sdbootutil

#bash completions
mkdir -p %{buildroot}%{_datadir}/bash-completion/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 @@ -137,6 +141,9 @@ sdbootutil update
%files
%license LICENSE
%{_bindir}/sdbootutil
%dir %{_datadir}/bash-completion
%dir %{_datadir}/bash-completion/completions
%{_datadir}/bash-completion/completions/sdbootutil
%{_unitdir}/sdbootutil-update-predictions.service

%files rpm-scriptlets
Expand Down

0 comments on commit da4439b

Please sign in to comment.