diff --git a/src/bootloaders/systemd-class.c b/src/bootloaders/systemd-class.c index 63ca8fe..8d6698a 100644 --- a/src/bootloaders/systemd-class.c +++ b/src/bootloaders/systemd-class.c @@ -2,6 +2,7 @@ * This file is part of clr-boot-manager. * * Copyright © 2016-2018 Intel Corporation + * Copyright © 2024 Solus Project * * clr-boot-manager is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as @@ -374,6 +375,9 @@ bool sd_class_set_default_kernel(const BootManager *manager, const Kernel *kerne } autofree(char) *item_name = NULL; + autofree(char) *console_mode = NULL; + autofree(char) *timeout_s = NULL; + autofree(char) *console_mode_s = NULL; int timeout = 0; const char *prefix = NULL; autofree(char) *old_conf = NULL; @@ -392,23 +396,30 @@ bool sd_class_set_default_kernel(const BootManager *manager, const Kernel *kerne } timeout = boot_manager_get_timeout_value((BootManager *)manager); + console_mode = boot_manager_get_console_mode((BootManager *)manager); + /* Set the timeout as configured by the user */ if (timeout > 0) { - /* Set the timeout as configured by the user */ - item_name = string_printf("timeout %d\ndefault %s-%s-%s-%d.conf\n", - timeout, - prefix, - kernel->meta.ktype, - kernel->meta.version, - kernel->meta.release); + timeout_s = string_printf("timeout %d\n", timeout); } else { - item_name = string_printf("default %s-%s-%s-%d.conf\n", - prefix, - kernel->meta.ktype, - kernel->meta.version, - kernel->meta.release); + timeout_s = string_printf(""); } + /* Set the console mode as configured by the user */ + if (console_mode != NULL) { + console_mode_s = string_printf("console-mode %s\n", console_mode); + } else { + console_mode_s = string_printf(""); + } + + item_name = string_printf("default %s-%s-%s-%d.conf\n%s%s", + prefix, + kernel->meta.ktype, + kernel->meta.version, + kernel->meta.release, + timeout_s, + console_mode_s); + write_config: if (file_get_text(sd_class_config.loader_config, &old_conf)) { if (streq(old_conf, item_name)) { diff --git a/src/bootman/bootman.h b/src/bootman/bootman.h index d7345d3..4c48d71 100644 --- a/src/bootman/bootman.h +++ b/src/bootman/bootman.h @@ -2,6 +2,7 @@ * This file is part of clr-boot-manager. * * Copyright © 2016-2018 Intel Corporation + * Copyright © 2024 Solus Project * * clr-boot-manager is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as @@ -344,6 +345,20 @@ bool boot_manager_is_update_efi_vars(BootManager *self); */ int boot_manager_get_timeout_value(BootManager *manager); +/** + * Determine the console mode based on the contents of + * SYSCONFDIR/console_mode + */ +char *boot_manager_get_console_mode(BootManager *manager); + +/** + * Determine the console mode based on the contents of + * SYSCONFDIR/console_mode + * + * @param mode console mode for systemd-boot. See `man loader.conf` for valid values + */ +bool boot_manager_set_console_mode(BootManager *manager, const char *mode); + /** * Determine the default kernel for the given type if it is in the set * This does not create a new instance, simply a pointer to the existing diff --git a/src/bootman/timeout.c b/src/bootman/config.c similarity index 62% rename from src/bootman/timeout.c rename to src/bootman/config.c index 9325c46..a86d303 100644 --- a/src/bootman/timeout.c +++ b/src/bootman/config.c @@ -2,6 +2,7 @@ * This file is part of clr-boot-manager. * * Copyright © 2016-2018 Intel Corporation + * Copyright © 2024 Solus Project * * clr-boot-manager is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as @@ -21,7 +22,7 @@ #include "log.h" #include "nica/files.h" -bool boot_manager_set_timeout_value(BootManager *self, int timeout) +static bool _write_sysconf_file(BootManager *self, const char *filename, const char *contents) { autofree(FILE) *fp = NULL; autofree(char) *path = NULL; @@ -38,9 +39,9 @@ bool boot_manager_set_timeout_value(BootManager *self, int timeout) return false; } - path = string_printf("%s%s/timeout", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY); + path = string_printf("%s%s/%s", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY, filename); - if (timeout <= 0) { + if (contents == NULL) { /* Nothing to be done here. */ if (!nc_file_exists(path)) { return true; @@ -58,42 +59,76 @@ bool boot_manager_set_timeout_value(BootManager *self, int timeout) return false; } - if (fprintf(fp, "%d\n", timeout) < 0) { + if (fprintf(fp, "%s\n", contents) < 0) { LOG_FATAL("Unable to set new timeout: %s", strerror(errno)); return false; } return true; } -int boot_manager_get_timeout_value(BootManager *self) +bool boot_manager_set_timeout_value(BootManager *self, int timeout) +{ + if (timeout <= 0) { + return _write_sysconf_file(self, "timeout", NULL); + } + + autofree(char) *timeout_s = string_printf("%d", timeout); + + return _write_sysconf_file(self, "timeout", timeout_s); +} + +bool boot_manager_set_console_mode(BootManager *self, const char *mode) +{ + return _write_sysconf_file(self, "console_mode", mode); +} + +static char *_read_sysconf_value(BootManager *self, const char *filename) { autofree(FILE) *fp = NULL; autofree(char) *path = NULL; - int t_val; + autofree(char) *line = NULL; + size_t size = 0; if (!self || !self->sysconfig) { - return false; + return NULL; } - path = string_printf("%s%s/timeout", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY); - - /* Default timeout being -1, i.e. don't use one */ + path = string_printf("%s%s/%s", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY, filename); if (!nc_file_exists(path)) { - return -1; + return NULL; } fp = fopen(path, "r"); if (!fp) { LOG_FATAL("Unable to open %s for reading: %s", path, strerror(errno)); - return -1; + return NULL; } - if (fscanf(fp, "%d\n", &t_val) != 1) { + __ssize_t n = getline(&line, &size, fp); + if (n < 0) { + LOG_ERROR("Failed to parse config file %s, using defaults", path); + return NULL; + } + + return strndup(line, (size_t)n - 1); +} + +int boot_manager_get_timeout_value(BootManager *self) +{ + autofree(char) *value = _read_sysconf_value(self, "timeout"); + + int timeout = atoi(value); + if (timeout <= 0) { LOG_ERROR("Failed to parse config file, defaulting to no timeout"); return -1; } - return t_val; + return timeout; +} + +char *boot_manager_get_console_mode(BootManager *self) +{ + return _read_sysconf_value(self, "console_mode"); } /* diff --git a/src/cli/main.c b/src/cli/main.c index 5d377bf..cf5ea5f 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -2,6 +2,7 @@ * This file is part of clr-boot-manager. * * Copyright © 2016-2018 Intel Corporation + * Copyright © 2024 Solus Project * * clr-boot-manager is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as @@ -20,6 +21,7 @@ #include "ops/report_booted.h" #include "ops/timeout.h" +#include "ops/console_mode.h" #include "ops/update.h" #include "ops/kernels.h" #include "ops/mount.h" @@ -29,6 +31,8 @@ static SubCommand cmd_help; static SubCommand cmd_version; static SubCommand cmd_set_timeout; static SubCommand cmd_get_timeout; +static SubCommand cmd_set_console_mode; +static SubCommand cmd_get_console_mode; static SubCommand cmd_report_booted; static SubCommand cmd_list_kernels; static SubCommand cmd_set_kernel; @@ -71,7 +75,7 @@ static bool print_usage(int argc, char **argv) nc_hashmap_iter_init(g_commands, &iter); while (nc_hashmap_iter_next(&iter, (void **)&id, (void **)&command)) { - fprintf(stdout, "%15s - %s\n", id, command->blurb); + fprintf(stdout, "%16s - %s\n", id, command->blurb); } cli_print_default_args_help(); @@ -168,6 +172,46 @@ to forcibly delay the system boot for a specified number of seconds.", return EXIT_FAILURE; } + /* Set the console mode */ + cmd_set_console_mode = (SubCommand){ + .name = "set-console-mode", + .blurb = "Set the console mode to be used by the bootloader", + .help = "Set the default console mode to be used by" PACKAGE_NAME + " when using\n\ +the \"update\" command.\n\ +This value will be used when next configuring the bootloader, and is used\n\ +to configure the console mode.\n\ +See `console-mode` in `man loader.conf` for possible values.", + .callback = cbm_command_set_console_mode, + .usage = " [--path=/path/to/filesystem/root]", + .requires_root = true, + }; + + if (!nc_hashmap_put(commands, cmd_set_console_mode.name, &cmd_set_console_mode)) { + DECLARE_OOM(); + return EXIT_FAILURE; + } + + /* Get the console mode */ + cmd_get_console_mode = (SubCommand){ + .name = "get-console-mode", + .blurb = "Get the console mode to be used by the bootloader", + .help = "Get the default console mode to be used by" PACKAGE_NAME + " when using\n\ +the \"update\" command.\n\ +This value will be used when next configuring the bootloader, and is used\n\ +to configure the console mode.\n\ +See `console-mode` in `man loader.conf` for possible values.", + .callback = cbm_command_get_console_mode, + .usage = " [--path=/path/to/filesystem/root]", + .requires_root = true, + }; + + if (!nc_hashmap_put(commands, cmd_get_console_mode.name, &cmd_get_console_mode)) { + DECLARE_OOM(); + return EXIT_FAILURE; + } + /* Report the system as successfully booted */ cmd_report_booted = (SubCommand){.name = "report-booted", diff --git a/src/cli/ops/console_mode.c b/src/cli/ops/console_mode.c new file mode 100644 index 0000000..effba04 --- /dev/null +++ b/src/cli/ops/console_mode.c @@ -0,0 +1,149 @@ +/* + * This file is part of clr-boot-manager. + * + * Copyright © 2024 Solus Project + * + * clr-boot-manager is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#include +#include + +#include "bootman.h" +#include "cli.h" +#include "update.h" + +// See `man loader.conf` +static inline bool is_console_mode(const char *str) +{ + char list[][5] = { "", "0", "1", "2", "auto", "max", "keep" }; + + for (size_t i = 0; i < sizeof list; i++) { + if (strcmp(str, list[(int)i]) == 0) { + return true; + } + } + + return false; +} + +bool cbm_command_set_console_mode(int argc, char **argv) +{ + autofree(char) *root = NULL; + autofree(BootManager) *manager = NULL; + bool update_efi_vars = false; + + if (!cli_default_args_init(&argc, &argv, &root, NULL, &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); + + /* Use specified root if required */ + if (root) { + if (!boot_manager_set_prefix(manager, root)) { + return false; + } + } else { + /* Default to "/", bail if it doesn't work. */ + if (!boot_manager_set_prefix(manager, "/")) { + return false; + } + } + + if (argc != 1) { + fprintf(stderr, "set-console-mode takes one string parameter\n"); + return false; + } + + char *console_mode = argv[optind]; + + if (!is_console_mode(console_mode)) { + fprintf(stderr, + "Please provide a valid value, see `man loader.conf` or use " + " to disable.\n"); + return false; + } + + if (strcmp(console_mode, "") == 0) { + console_mode = NULL; + } + + if (!boot_manager_set_console_mode(manager, console_mode)) { + fprintf(stderr, "Failed to update console mode\n"); + return false; + } + + if (console_mode == NULL) { + fprintf(stdout, "Console mode has been removed\n"); + } else { + fprintf(stdout, "New console mode is: %s\n", console_mode); + } + + return cbm_command_update_do(manager, root, false); +} + +bool cbm_command_get_console_mode(int argc, char **argv) +{ + autofree(char) *root = NULL; + autofree(BootManager) *manager = NULL; + autofree(char) *console_mode = NULL; + bool update_efi_vars = false; + + cli_default_args_init(&argc, &argv, &root, NULL, &update_efi_vars); + + manager = boot_manager_new(); + if (!manager) { + DECLARE_OOM(); + return false; + } + + boot_manager_set_update_efi_vars(manager, update_efi_vars); + + /* Use specified root if required */ + if (root) { + if (!boot_manager_set_prefix(manager, root)) { + return false; + } + } else { + /* Default to "/", bail if it doesn't work. */ + if (!boot_manager_set_prefix(manager, "/")) { + return false; + } + } + + if (argc != 0) { + fprintf(stderr, "get-console-mode does not take any parameters\n"); + return false; + } + + console_mode = boot_manager_get_console_mode(manager); + if (console_mode == NULL) { + fprintf(stdout, "No console mode is currently configured\n"); + } else { + fprintf(stdout, "Console mode: %s\n", console_mode); + } + return true; +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=8 tabstop=8 expandtab: + * :indentSize=8:tabSize=8:noTabs=true: + */ diff --git a/src/cli/ops/console_mode.h b/src/cli/ops/console_mode.h new file mode 100644 index 0000000..4cd8a38 --- /dev/null +++ b/src/cli/ops/console_mode.h @@ -0,0 +1,59 @@ +/* + * This file is part of clr-boot-manager. + * + * Copyright © 2024 Solus Project + * + * clr-boot-manager is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#pragma once + +#include "cli.h" + +/** + * Set the console mode to be used by the bootloader + * + * Set the default console mode to be used when using the "update" command. + * This value will be used when next configuring the bootloader, and is used + * to configure the console mode. + * + * See `console-mode` in `man loader.conf` for possible values. + * + * @param argc Must be 1. + * @param argv Single argument matching a valid console mode. + * + * @return boolean indicating success or failure. + */ +bool cbm_command_set_console_mode(int argc, char **argv); + +/** + * Get the console mode to be used by the bootloader + * + * Get the default console mode to be used when using the "update" command. + * This value will be used when next configuring the bootloader, and is used + * to configure the console mode. + * + * See `console-mode` in `man loader.conf` for possible values. + * + * @param argc Must be 0. + * @param argv None. + * + * @return boolean indicating success or failure. + */ +bool cbm_command_get_console_mode(int argc, char **argv); + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=8 tabstop=8 expandtab: + * :indentSize=8:tabSize=8:noTabs=true: + */ diff --git a/src/meson.build b/src/meson.build index 426e081..824bf50 100644 --- a/src/meson.build +++ b/src/meson.build @@ -40,7 +40,7 @@ libcbm_sources = [ 'bootman/bootman.c', 'bootman/kernel.c', 'bootman/sysconfig.c', - 'bootman/timeout.c', + 'bootman/config.c', 'bootman/update.c', 'lib/blkid_stub.c', 'lib/cmdline.c', @@ -102,6 +102,7 @@ clr_boot_manager_sources = [ 'cli/ops/mount.c', 'cli/ops/report_booted.c', 'cli/ops/timeout.c', + 'cli/ops/console_mode.c', 'cli/ops/update.c', ]