Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bash completions #62

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
TobiPeterG marked this conversation as resolved.
Show resolved Hide resolved
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)"
aplanas marked this conversation as resolved.
Show resolved Hide resolved
local cur prev words cword image
COMPREPLY=()
_get_comp_words_by_ref -n : cur prev words cword
TobiPeterG marked this conversation as resolved.
Show resolved Hide resolved

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this +yes required?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes.
It should match all commands with a key, not only those with a value

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]=""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has a parameter

[add-all-kernels]=""
[remove-all-kernels]=""
[is-installed]=""
[list-snapshots]=""
[list-entries]=""
[list-kernels]=""
[show-entry]=""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this also has parameters

[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
aplanas marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+yes should be dropped?

Copy link
Collaborator Author

@TobiPeterG TobiPeterG Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? This checks if the command is known. If not, it will fail here.

The +yes makes sure that commands that don't need a value are also registered

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