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 59% rename from src/bootman/timeout.c rename to src/bootman/config.c index 9325c46..bc2f6f9 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 @@ -11,6 +12,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -21,16 +23,15 @@ #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) { + assert(self != NULL); + assert(self->sysconfig != NULL); + autofree(FILE) *fp = NULL; autofree(char) *path = NULL; autofree(char) *dir = NULL; - if (!self || !self->sysconfig) { - return false; - } - dir = string_printf("%s%s", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY); if (!nc_mkdir_p(dir, 00755)) { @@ -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,80 @@ 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) { - autofree(FILE) *fp = NULL; - autofree(char) *path = NULL; - int t_val; - - if (!self || !self->sysconfig) { - return false; + if (timeout <= 0) { + return write_sysconf_file(self, "timeout", NULL); } - path = string_printf("%s%s/timeout", self->sysconfig->prefix, KERNEL_CONF_DIRECTORY); + autofree(char) *timeout_s = string_printf("%d", timeout); + + return write_sysconf_file(self, "timeout", timeout_s); +} - /* Default timeout being -1, i.e. don't use one */ +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) +{ + assert(self != NULL); + assert(self->sysconfig != NULL); + + autofree(FILE) *fp = NULL; + autofree(char) *path = NULL; + autofree(char) *line = NULL; + size_t size = 0; + + 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 NULL; + } + + __ssize_t n = getline(&line, &size, fp); + if (n < 0) { + LOG_ERROR("Failed to parse config file %s, using defaults", path); + return NULL; + } + + line[strcspn(line, "\n")] = '\0'; + + return strndup(line, (size_t)n); +} + +int boot_manager_get_timeout_value(BootManager *self) +{ + autofree(char) *value = read_sysconf_value(self, "timeout"); + if (value == NULL) { return -1; } - if (fscanf(fp, "%d\n", &t_val) != 1) { + 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', ] diff --git a/tests/check-core.c b/tests/check-core.c index 0d98b33..3e30df7 100644 --- a/tests/check-core.c +++ b/tests/check-core.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 @@ -326,6 +327,29 @@ START_TEST(bootman_timeout_test) } END_TEST +START_TEST(bootman_console_mode_test) +{ + autofree(BootManager) *m = NULL; + m = prepare_playground(&core_config); + + if (create_console_mode_conf()) { + fail_if(strcmp(boot_manager_get_console_mode(m), "max") != 0, + "Failed to get console mode value."); + } else { + fprintf(stderr, "Couldn't create console mode conf\n"); + } + + fail_if(!boot_manager_set_console_mode(m, "auto"), "Failed to set console mode."); + fail_if(strcmp(boot_manager_get_console_mode(m), "auto") != 0, + "Failed to get correct console mode."); + fail_if(!boot_manager_set_console_mode(m, NULL), "Failed to disable console_mode."); + fail_if(nc_file_exists(TOP_BUILD_DIR "/tests/update_playground/" KERNEL_CONF_DIRECTORY + "/console_mode"), + "kernel/console_mode present."); + fail_if(boot_manager_get_console_mode(m) != NULL, "Failed to get default console mode."); +} +END_TEST + START_TEST(bootman_writer_simple_test) { autofree(CbmWriter) *writer = CBM_WRITER_INIT; @@ -406,6 +430,7 @@ static Suite *core_suite(void) tcase_add_test(tc, bootman_list_kernels_no_modules_test); tcase_add_test(tc, bootman_map_kernels_test); tcase_add_test(tc, bootman_timeout_test); + tcase_add_test(tc, bootman_console_mode_test); suite_add_tcase(s, tc); tc = tcase_create("bootman_writer_functions"); diff --git a/tests/harness.c b/tests/harness.c index e531080..92933a9 100644 --- a/tests/harness.c +++ b/tests/harness.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 @@ -566,6 +567,19 @@ bool create_timeout_conf(void) return true; } +bool create_console_mode_conf(void) +{ + autofree(char) *console_mode_conf = NULL; + + console_mode_conf = + string_printf("%s/%s/console_mode", PLAYGROUND_ROOT, KERNEL_CONF_DIRECTORY); + if (!file_set_text((const char *)console_mode_conf, (char *)"max")) { + fprintf(stderr, "Failed to touch: %s %s\n", console_mode_conf, strerror(errno)); + return false; + } + return true; +} + void set_test_system_uefi(void) { autofree(char) *root = NULL; diff --git a/tests/harness.h b/tests/harness.h index 4dff5f4..7fb1cef 100644 --- a/tests/harness.h +++ b/tests/harness.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 @@ -104,6 +105,11 @@ bool confirm_kernel_uninstalled(BootManager *manager, PlaygroundKernel *kernel); */ bool create_timeout_conf(void); +/** + * Create console_mode configuration in /etc + */ +bool create_console_mode_conf(void); + /** * Set up the test harness to emulate UEFI */