From 8276f49e43e52bc20e653b1d38689234758b6c40 Mon Sep 17 00:00:00 2001 From: William Douglas Date: Wed, 4 Jan 2023 14:20:20 -0800 Subject: [PATCH] Add remove-kernel command In some cases users will want to remove kernels from their systems outside an update workflow. This is fairly painful to do manually so add a command to handle the operation which removes both the /boot data and configuration files as well as the /usr data. The default symlink for a kernel type will remain as this is something that the update mechanism should be in control of and figuring out what would be the fallback is out of cbm's scope. Signed-off-by: William Douglas --- data/completions/bash/clr-boot-manager.in | 4 +- data/completions/zsh/_clr-boot-manager.in | 3 +- man/clr-boot-manager.1.in | 9 +++ src/bootman/bootman.c | 41 ++++++++++++ src/bootman/bootman.h | 10 +++ src/cli/main.c | 16 +++++ src/cli/ops/kernels.c | 77 +++++++++++++++++++++++ src/cli/ops/kernels.h | 1 + 8 files changed, 158 insertions(+), 3 deletions(-) diff --git a/data/completions/bash/clr-boot-manager.in b/data/completions/bash/clr-boot-manager.in index f38911c1..1c8daaa1 100644 --- a/data/completions/bash/clr-boot-manager.in +++ b/data/completions/bash/clr-boot-manager.in @@ -23,14 +23,14 @@ _clr_boot_manager() { case "$3" in "$1"|help) - opts="version report-booted help update set-timeout get-timeout set-kernel list-kernels help" + opts="version report-booted help update set-timeout get-timeout set-kernel remove-kernel list-kernels help" COMPREPLY=($(compgen -W "${opts}" -- "${2}")) ;; get-timeout|list-kernels|update|set-timeout) opts="--path --image --no-efi-update" COMPREPLY=($(compgen -W "${opts}" -- "${2}")) ;; - set-kernel) + set-kernel|remove-kernel) opts="--path --image --no-efi-update" COMPREPLY=($(compgen -W "${opts}" -- "${2}")) COMPREPLY+=($(compgen -G "@KERNEL_DIRECTORY@/@KERNEL_NAMESPACE@*" )) diff --git a/data/completions/zsh/_clr-boot-manager.in b/data/completions/zsh/_clr-boot-manager.in index 6a25ff2f..b9400a35 100644 --- a/data/completions/zsh/_clr-boot-manager.in +++ b/data/completions/zsh/_clr-boot-manager.in @@ -26,6 +26,7 @@ local -a subcmds; subcmds=( "set-timeout:Set the timeout to be used by the bootloader" "get-timeout:Get the timeout to be used by the bootloader" "set-kernel:Configure kernel to be used at next boot" + "remove-kernel:Remove kernel from system" "list-kernels:Display currently selectable kernels to boot" "help:Display help information on available commands" ) @@ -50,7 +51,7 @@ if [[ -n "$state" ]]; then get-timeout|list-kernels|update) _arguments $args && ret=0 ;; - set-kernel) + set-kernel|remove-kernel) local -a kernelpath kernelpath=(${opt_args[--path=]:-${opt_args[-p]}}) diff --git a/man/clr-boot-manager.1.in b/man/clr-boot-manager.1.in index 00b78d61..a9ce1f53 100644 --- a/man/clr-boot-manager.1.in +++ b/man/clr-boot-manager.1.in @@ -112,6 +112,15 @@ Configure the default booting kernel. This command will not prevent the update command from changing the default kernel\&. .RE +.PP +\fBremove-kernel\fR +.RS 4 +Remove a kernel from the system (both /boot and /usr locations). + +Warning: This can remove the only kernel from the system, ensure you have a default kernel +set after running this command\&. +.RE + .SH "EXIT STATUS" .PP On success, 0 is returned, a non\-zero failure code otherwise\& diff --git a/src/bootman/bootman.c b/src/bootman/bootman.c index 6cf1d17a..a35381ab 100644 --- a/src/bootman/bootman.c +++ b/src/bootman/bootman.c @@ -312,6 +312,47 @@ bool boot_manager_install_kernel(BootManager *self, const Kernel *kernel) return self->bootloader->install_kernel(self, kernel); } +bool boot_manager_remove_kernel_wrapper(BootManager *self, const Kernel *kernel) +{ + assert(self != NULL); + autofree(KernelArray) *kernels = NULL; + autofree(char) *boot_dir = NULL; + int did_mount = -1; + bool matched = false; + bool kernel_removed = false; + + CHECK_DBG_RET_VAL(!self->bootloader, false, "Invalid boot loader: null"); + + CHECK_DBG_RET_VAL(!cbm_is_sysconfig_sane(self->sysconfig), false, + "Sysconfig is not sane"); + + /* Grab the available kernels */ + kernels = boot_manager_get_kernels(self); + CHECK_ERR_RET_VAL(!kernels || kernels->len == 0, false, + "No kernels discovered in %s, bailing", self->kernel_dir); + + did_mount = detect_and_mount_boot(self, &boot_dir); + CHECK_DBG_RET_VAL(did_mount < 0, false, "Boot was not mounted"); + + for (uint16_t i = 0; i < kernels->len; i++) { + const Kernel *k = nc_array_get(kernels, i); + if (streq(kernel->meta.ktype, k->meta.ktype) && + streq(kernel->meta.version, k->meta.version) && + kernel->meta.release == k->meta.release) { + matched = true; + kernel_removed = boot_manager_remove_kernel(self, k); + break; + } + } + if (did_mount > 0) { + umount_boot(boot_dir); + } + + CHECK_ERR(!matched, "No matching kernel in %s, bailing", self->kernel_dir); + + return kernel_removed; +} + bool boot_manager_remove_kernel(BootManager *self, const Kernel *kernel) { assert(self != NULL); diff --git a/src/bootman/bootman.h b/src/bootman/bootman.h index db45eab7..e949a8f8 100644 --- a/src/bootman/bootman.h +++ b/src/bootman/bootman.h @@ -267,6 +267,16 @@ void *boot_manager_get_data(BootManager *manager); */ void boot_manager_set_data(BootManager *manager, void *data); +/** + * Wrapping function to remove a kernel that handles sanity checks and /boot mount state + * + * @param kernel A valid kernel instance + * + * @return a boolean value, indicating success or failure + */ + +bool boot_manager_remove_kernel_wrapper(BootManager *manager, const Kernel *kernel); + /** * Attempt to uninstall a previously installed kernel * diff --git a/src/cli/main.c b/src/cli/main.c index 54d77c54..79cf021f 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -31,6 +31,7 @@ static SubCommand cmd_get_timeout; static SubCommand cmd_report_booted; static SubCommand cmd_list_kernels; static SubCommand cmd_set_kernel; +static SubCommand cmd_remove_kernel; static char *binary_name = NULL; static NcHashmap *g_commands = NULL; static bool explicit_help = false; @@ -209,6 +210,21 @@ kernel for the next time the system boots.", return EXIT_FAILURE; } + /* Remove kernel */ + cmd_remove_kernel = (SubCommand){ + .name = "remove-kernel", + .blurb = "Remove the kernel from the system", + .help = "This command will remove a kernel from the system.", + .callback = cbm_command_remove_kernel, + .usage = " [--path=/path/to/filesystem/root]", + .requires_root = true + }; + + if (!nc_hashmap_put(commands, cmd_remove_kernel.name, &cmd_remove_kernel)) { + DECLARE_OOM(); + return EXIT_FAILURE; + } + /* Version */ cmd_version = (SubCommand){ .name = "version", diff --git a/src/cli/ops/kernels.c b/src/cli/ops/kernels.c index 4dbd17de..9c788e33 100644 --- a/src/cli/ops/kernels.c +++ b/src/cli/ops/kernels.c @@ -158,6 +158,83 @@ bool cbm_command_set_kernel(int argc, char **argv) return true; } +bool cbm_command_remove_kernel(int argc, char **argv) +{ + autofree(char) *root = NULL; + autofree(BootManager) *manager = NULL; + autofree(KernelArray) *kernels = NULL; + bool forced_image = false; + char type[32] = { 0 }; + char version[16] = { 0 }; + int release = 0; + Kernel kern = { 0 }; + bool update_efi_vars = true; + + if (!cli_default_args_init(&argc, &argv, &root, &forced_image, &update_efi_vars)) { + return false; + } + + manager = boot_manager_new(); + if (!manager) { + DECLARE_OOM(); + return false; + } + + boot_manager_set_update_efi_vars(manager, update_efi_vars); + + if (root) { + autofree(char) *realp = NULL; + + realp = realpath(root, NULL); + if (!realp) { + LOG_FATAL("Path specified does not exist: %s", root); + return false; + } + /* Anything not / is image mode */ + if (!streq(realp, "/")) { + boot_manager_set_image_mode(manager, true); + } else { + boot_manager_set_image_mode(manager, forced_image); + } + + /* CBM will check this again, we just needed to check for + * image mode.. */ + if (!boot_manager_set_prefix(manager, root)) { + return false; + } + } else { + boot_manager_set_image_mode(manager, forced_image); + /* Default to "/", bail if it doesn't work. */ + if (!boot_manager_set_prefix(manager, "/")) { + return false; + } + } + + if (argc != 1) { + fprintf(stderr, + "remove-kernel takes a kernel ID of the form %s.TYPE.VERSION-RELEASE\n", + KERNEL_NAMESPACE); + return false; + } + + if (sscanf(argv[optind], KERNEL_NAMESPACE ".%31[^.].%15[^-]-%d", type, version, &release) != 3) { + fprintf(stderr, + "remove-kernel takes a kernel ID of the form %s.TYPE.VERSION-RELEASE\n", + KERNEL_NAMESPACE); + return false; + } + + kern.meta.ktype = type; + kern.meta.version = version; + kern.meta.release = release; + + /* Let CBM take care of the rest */ + if (!boot_manager_remove_kernel_wrapper(manager, &kern)) { + return false; + } + return true; +} + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/src/cli/ops/kernels.h b/src/cli/ops/kernels.h index cd59a7ba..9d078170 100644 --- a/src/cli/ops/kernels.h +++ b/src/cli/ops/kernels.h @@ -15,6 +15,7 @@ bool cbm_command_list_kernels(int argc, char **argv); bool cbm_command_set_kernel(int argc, char **argv); +bool cbm_command_remove_kernel(int argc, char **argv); /* * Editor modelines - https://www.wireshark.org/tools/modelines.html