From 327969237080aaf0c2303943cb9df6dcf96f1566 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 16 Nov 2018 18:55:51 +0100 Subject: [PATCH 01/19] efivars: extend character set that may be used in boot loader entry ids a bit See https://github.com/systemd/systemd/pull/10495#discussion_r233992570 --- src/shared/efivars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 26f905bfaa..caae7b9138 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -796,7 +796,7 @@ bool efi_loader_entry_name_valid(const char *s) { if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */ return false; - return in_charset(s, ALPHANUMERICAL "-"); + return in_charset(s, ALPHANUMERICAL "-_."); } int efi_loader_get_entries(char ***ret) { From e667266a74d7bb3edab43ad63b5de76c6915e6b7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Oct 2018 20:02:25 +0200 Subject: [PATCH 02/19] logind: optionally support non-EFI reboot-to-firmware This extends the reboot-to-firmware logic in logind, so that other than EFI firmwares could be theoretically support. The scheme is like this: if you want to support this, set the $SYSTEMD_REBOOT_TO_FIRMWARE=1 env var for logind. If so, this will override the EFI logic, and cause a file /run/systemd/reboot-to-firmware file to be created when reboot-to-firmware is requested. This file has no contents, it's mere existance indicates a reboot with reboot-to-firmware set. The idea is that for alternative firmwares a drop-in for logind is added that sets the env var, in combination with some code run during shutdown that checks for the file and does the right thing. --- src/login/logind-dbus.c | 97 ++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index b9ea370ec0..262b0290a8 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -18,6 +18,7 @@ #include "device-util.h" #include "dirent-util.h" #include "efivars.h" +#include "env-util.h" #include "escape.h" #include "fd-util.h" #include "fileio-label.h" @@ -2392,9 +2393,24 @@ static int property_get_reboot_to_firmware_setup( assert(reply); assert(userdata); - r = efi_get_reboot_to_firmware(); - if (r < 0 && r != -EOPNOTSUPP) - log_warning_errno(r, "Failed to determine reboot-to-firmware state: %m"); + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what is currently configured in the EFI variables */ + r = efi_get_reboot_to_firmware(); + if (r < 0 && r != -EOPNOTSUPP) + log_warning_errno(r, "Failed to determine reboot-to-firmware-setup state: %m"); + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); + else if (r > 0) { + /* Non-EFI case: let's see whether /run/systemd/reboot-to-firmware-setup exists. */ + if (access("/run/systemd/reboot-to-firmware-setup", F_OK) < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to check whether /run/systemd/reboot-to-firmware-setup exists: %m"); + + r = false; + } else + r = true; + } return sd_bus_message_append(reply, "b", r > 0); } @@ -2404,8 +2420,9 @@ static int method_set_reboot_to_firmware_setup( void *userdata, sd_bus_error *error) { - int b, r; Manager *m = userdata; + bool use_efi; + int b, r; assert(message); assert(m); @@ -2414,6 +2431,29 @@ static int method_set_reboot_to_firmware_setup( if (r < 0) return r; + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what the firmware supports */ + + r = efi_reboot_to_firmware_supported(); + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware."); + if (r < 0) + return r; + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */ + use_efi = false; + r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", @@ -2427,9 +2467,20 @@ static int method_set_reboot_to_firmware_setup( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = efi_set_reboot_to_firmware(b); - if (r < 0) - return r; + if (use_efi) { + r = efi_set_reboot_to_firmware(b); + if (r < 0) + return r; + } else { + if (b) { + r = touch("/run/systemd/reboot-to-firmware-setup"); + if (r < 0) + return r; + } else { + if (unlink("/run/systemd/reboot-to-firmware-setup") < 0 && errno != ENOENT) + return -errno; + } + } return sd_bus_reply_method_return(message, NULL); } @@ -2439,22 +2490,38 @@ static int method_can_reboot_to_firmware_setup( void *userdata, sd_bus_error *error) { - int r; - bool challenge; - const char *result; + const char *result = NULL; Manager *m = userdata; + bool challenge; + int r; assert(message); assert(m); - r = efi_reboot_to_firmware_supported(); - if (r < 0) { - if (r != -EOPNOTSUPP) - log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m"); + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what the firmware supports */ - return sd_bus_reply_method_return(message, "s", "na"); + r = efi_reboot_to_firmware_supported(); + if (r < 0) { + if (r != -EOPNOTSUPP) + log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m"); + + result = "na"; + } + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); + + result = "na"; } + if (result) + return sd_bus_reply_method_return(message, "s", result); + r = bus_test_polkit(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", From 4629499e7fed6b67e03aa202da1e757160d63582 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 16:05:14 +0100 Subject: [PATCH 03/19] bootctl: tweak 'list' output a bit Let's suppress the final newline in the list if it's the last entry we are outputting. --- src/boot/bootctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 1e0d115fe3..6c0885198a 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1214,7 +1214,8 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (r < 0) return r; - puts(""); + if (n+1 < config.n_entries) + putchar('\n'); strv_remove(found_by_loader, config.entries[n].id); } @@ -1223,7 +1224,7 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (!strv_isempty(found_by_loader)) { char **i; - printf("Automatic/Other Entries Found by Boot Loader:\n\n"); + printf("\nAutomatic/Other Entries Found by Boot Loader:\n\n"); STRV_FOREACH(i, found_by_loader) puts(*i); From 7800322ca553f62058a9dd57a747f3a637ea8827 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 16:53:11 +0100 Subject: [PATCH 04/19] bootspec: move log msg from systemctl.c to bootspec.c find_default_boot_entry() is only used by systemctl.c, and currently handles one log message in the caller instead of the callee. Let's simplify that and move it over, too --- src/shared/bootspec.c | 4 +++- src/systemctl/systemctl.c | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 53ab042404..ae20b34a40 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1307,7 +1307,9 @@ int find_default_boot_entry( assert(e); r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL); - if (r < 0) + if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */ + return log_error_errno(r, "Cannot find the ESP partition mount point."); + if (r < 0) /* But it logs about all these cases, hence don't log here again */ return r; r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 444d426eb0..0141e2995d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3536,10 +3536,7 @@ static int load_kexec_kernel(void) { return log_error_errno(errno, KEXEC" is not available: %m"); r = find_default_boot_entry(NULL, NULL, &config, &e); - if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ - return log_error_errno(r, "Cannot find the ESP partition mount point."); if (r < 0) - /* But it logs about all these cases, hence don't log here again */ return r; if (strv_length(e->initrd) > 1) From 78c38fe79c64ea71ffe9bbef9db5f8b17e45b1fd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 17:46:58 +0100 Subject: [PATCH 05/19] systemctl: be careful with boot loader entries lacking a 'linux' line A boot loader entry might not have a 'linux' line, but an 'efi' line or something else. Let's handle that case nicely. --- src/systemctl/systemctl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 0141e2995d..9be6621809 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3539,6 +3539,9 @@ static int load_kexec_kernel(void) { if (r < 0) return r; + if (!e->kernel) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Boot entry does not refer to Linux kernel, which is not supported currently."); if (strv_length(e->initrd) > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Boot entry specifies multiple initrds, which is not supported currently."); From 0f88d829790c6f04c25acdea6ae4718525a1eacf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 17:47:50 +0100 Subject: [PATCH 06/19] systemctl: when something is not supported use EOPNOTSUPP --- src/systemctl/systemctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9be6621809..6a4e73dee4 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3543,7 +3543,7 @@ static int load_kexec_kernel(void) { return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Boot entry does not refer to Linux kernel, which is not supported currently."); if (strv_length(e->initrd) > 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Boot entry specifies multiple initrds, which is not supported currently."); kernel = path_join(e->root, e->kernel); @@ -3551,7 +3551,7 @@ static int load_kexec_kernel(void) { return log_oom(); if (!strv_isempty(e->initrd)) { - initrd = path_join(e->root, *e->initrd); + initrd = path_join(e->root, e->initrd[0]); if (!initrd) return log_oom(); } From c943e95f892c62f9bded88de219bcf3c80cdc3d4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 17:48:31 +0100 Subject: [PATCH 07/19] systemctl: let's FORK_WAIT where we can --- src/systemctl/systemctl.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6a4e73dee4..0b31a4c9da 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3569,7 +3569,7 @@ static int load_kexec_kernel(void) { if (arg_dry_run) return 0; - r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { @@ -3578,19 +3578,14 @@ static int load_kexec_kernel(void) { "--load", kernel, "--append", options, initrd ? "--initrd" : NULL, initrd, - NULL }; + NULL + }; /* Child */ execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_check("kexec", pid, WAIT_LOG); - if (r < 0) - return r; - if (r > 0) - /* Command failed */ - return -EPROTO; return 0; } From 5b1da1ba6da432dc6b120f4ad438aa4a360b2df7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 17:51:15 +0100 Subject: [PATCH 08/19] bootspec: simplify find_default_boot_entry() If the only caller passes NULL for the two paths, let's remove the parameter altogether. --- src/shared/bootspec.c | 6 ++---- src/shared/bootspec.h | 2 +- src/systemctl/systemctl.c | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index ae20b34a40..d5bd0ff75e 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1295,8 +1295,6 @@ found: } int find_default_boot_entry( - const char *esp_path, - const char *xbootldr_path, BootConfig *config, const BootEntry **e) { @@ -1306,13 +1304,13 @@ int find_default_boot_entry( assert(config); assert(e); - r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL); + r = find_esp_and_warn(NULL, false, &esp_where, NULL, NULL, NULL, NULL); if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */ return log_error_errno(r, "Cannot find the ESP partition mount point."); if (r < 0) /* But it logs about all these cases, hence don't log here again */ return r; - r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL); + r = find_xbootldr_and_warn(NULL, false, &xbootldr_where, NULL); if (r < 0 && r != -ENOKEY) return r; diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index f184902558..695e86d8f7 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -50,4 +50,4 @@ static inline const char* boot_entry_title(const BootEntry *entry) { int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); -int find_default_boot_entry(const char *esp_path, const char *xbootldr_path, BootConfig *config, const BootEntry **e); +int find_default_boot_entry(BootConfig *config, const BootEntry **e); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 0b31a4c9da..d88435f8e9 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3535,7 +3535,7 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(NULL, NULL, &config, &e); + r = find_default_boot_entry(&config, &e); if (r < 0) return r; From 93f14ce28d8477ad70ba6a60299b84af794e18b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 18:35:26 +0100 Subject: [PATCH 09/19] bootspec: move augmentation of loader-discovered entries into bootspec.c Previously, bootctl would show boot loader entries discovered by the boot loader which couldn't found locally separately in the output. Let's move this code into bootspec.c, and beef it up a bit. This way we can use it later on for logind, and correctly show automatically discovered windows/macos entries too. --- src/boot/bootctl.c | 22 +++--------- src/shared/bootspec.c | 82 +++++++++++++++++++++++++++++++++++++++++-- src/shared/bootspec.h | 25 +++++++++++++ 3 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 6c0885198a..b5f4b8e5a3 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -311,13 +311,15 @@ static int status_variables(void) { static int boot_entry_show(const BootEntry *e, bool show_as_default) { assert(e); - printf(" title: %s%s%s%s%s%s\n", + printf(" title: %s%s%s%s%s%s\n" + " type: %s\n", ansi_highlight(), boot_entry_title(e), ansi_normal(), ansi_highlight_green(), show_as_default ? " (default)" : "", - ansi_normal()); + ansi_normal(), + boot_entry_type_to_string(e->type)); if (e->id) printf(" id: %s\n", e->id); @@ -1173,7 +1175,6 @@ static int verb_status(int argc, char *argv[], void *userdata) { static int verb_list(int argc, char *argv[], void *userdata) { _cleanup_(boot_config_free) BootConfig config = {}; - _cleanup_free_ char **found_by_loader = NULL; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn @@ -1196,9 +1197,7 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = efi_loader_get_entries(&found_by_loader); - if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP)) - log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m"); + (void) boot_entries_augment_from_loader(&config, false); if (config.n_entries == 0) log_info("No boot loader entries found."); @@ -1216,20 +1215,9 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (n+1 < config.n_entries) putchar('\n'); - - strv_remove(found_by_loader, config.entries[n].id); } } - if (!strv_isempty(found_by_loader)) { - char **i; - - printf("\nAutomatic/Other Entries Found by Boot Loader:\n\n"); - - STRV_FOREACH(i, found_by_loader) - puts(*i); - } - return 0; } diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index d5bd0ff75e..d0841e8ee5 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -22,6 +22,7 @@ #include "path-util.h" #include "pe-header.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "unaligned.h" @@ -50,7 +51,10 @@ static int boot_entry_load( const char *path, BootEntry *entry) { - _cleanup_(boot_entry_free) BootEntry tmp = {}; + _cleanup_(boot_entry_free) BootEntry tmp = { + .type = BOOT_ENTRY_CONF, + }; + _cleanup_fclose_ FILE *f = NULL; unsigned line = 1; char *b, *c; @@ -272,7 +276,9 @@ static int boot_entry_load_unified( BootEntry *ret) { _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL; - _cleanup_(boot_entry_free) BootEntry tmp = {}; + _cleanup_(boot_entry_free) BootEntry tmp = { + .type = BOOT_ENTRY_UNIFIED, + }; _cleanup_fclose_ FILE *f = NULL; const char *k; int r; @@ -664,6 +670,70 @@ int boot_entries_load_config( return 0; } +int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { + + static const char * const title_table[] = { + /* Pretty names for a few well-known automatically discovered entries. */ + "auto-osx", "macOS", + "auto-windows", "Windows Boot Manager", + "auto-efi-shell", "EFI Shell", + "auto-efi-default", "EFI Default Loader", + "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface", + }; + + _cleanup_free_ char **found_by_loader = NULL; + size_t n_allocated; + char **i; + int r; + + assert(config); + + /* Let's add the entries discovered by the boot loader to the end of our list, unless they are + * already included there. */ + + r = efi_loader_get_entries(&found_by_loader); + if (IN_SET(r, -ENOENT, -EOPNOTSUPP)) + return log_debug_errno(r, "Boot loader reported no entries."); + if (r < 0) + return log_error_errno(r, "Failed to determine entries reported by boot loader: %m"); + + n_allocated = config->n_entries; + + STRV_FOREACH(i, found_by_loader) { + _cleanup_free_ char *c = NULL, *t = NULL; + char **a, **b; + + if (boot_config_has_entry(config, *i)) + continue; + + if (only_auto && !startswith(*i, "auto-")) + continue; + + c = strdup(*i); + if (!c) + return log_oom(); + + STRV_FOREACH_PAIR(a, b, (char**) title_table) + if (streq(*a, *i)) { + t = strdup(*b); + if (!t) + return log_oom(); + break; + } + + if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1)) + return log_oom(); + + config->entries[config->n_entries++] = (BootEntry) { + .type = BOOT_ENTRY_LOADER, + .id = TAKE_PTR(c), + .title = TAKE_PTR(t), + }; + } + + return 0; +} + /********************************************************************************/ static int verify_esp_blkid( @@ -1327,3 +1397,11 @@ int find_default_boot_entry( return 0; } + +static const char* const boot_entry_type_table[_BOOT_ENTRY_MAX] = { + [BOOT_ENTRY_CONF] = "conf", + [BOOT_ENTRY_UNIFIED] = "unified", + [BOOT_ENTRY_LOADER] = "loader", +}; + +DEFINE_STRING_TABLE_LOOKUP(boot_entry_type, BootEntryType); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 695e86d8f7..658905a4cb 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -8,7 +8,18 @@ #include "sd-id128.h" +#include "string-util.h" + +typedef enum BootEntryType { + BOOT_ENTRY_CONF, /* Type #1 entries: *.conf files */ + BOOT_ENTRY_UNIFIED, /* Type #2 entries: *.efi files */ + BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI var */ + _BOOT_ENTRY_MAX, + _BOOT_ENTRY_INVALID = -1, +} BootEntryType; + typedef struct BootEntry { + BootEntryType type; char *id; /* This is the file basename without extension */ char *path; /* This is the full path to the drop-in file */ char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ @@ -40,8 +51,19 @@ typedef struct BootConfig { ssize_t default_entry; } BootConfig; +static inline bool boot_config_has_entry(BootConfig *config, const char *id) { + size_t j; + + for (j = 0; j < config->n_entries; j++) + if (streq(config->entries[j].id, id)) + return true; + + return false; +} + void boot_config_free(BootConfig *config); int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); +int boot_entries_augment_from_loader(BootConfig *config, bool only_auto); static inline const char* boot_entry_title(const BootEntry *entry) { return entry->show_title ?: entry->title ?: entry->id; @@ -51,3 +73,6 @@ int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); int find_default_boot_entry(BootConfig *config, const BootEntry **e); + +const char* boot_entry_type_to_string(BootEntryType t) _const_; +BootEntryType boot_entry_type_from_string(const char *s) _pure_; From dba33c4a2c53f1dca6edc1661b608f2c52ba6465 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 1 Mar 2019 18:38:32 +0100 Subject: [PATCH 10/19] bootspec: correct error code shown in log msg --- src/shared/bootspec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index d0841e8ee5..4ebb221a42 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -482,7 +482,7 @@ static int boot_entries_find_unified( r = fd_verify_regular(fd); if (r < 0) { - log_warning_errno(errno, "File %s/%s is not regular, ignoring: %m", dir, de->d_name); + log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name); continue; } From eea4ce1ec716948c1f67d9c541de0c77082401a7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2019 17:52:17 +0100 Subject: [PATCH 11/19] bootspec: introduce new helper boot_entries_load_config_auto() It's a simple wrapper around boot_entries_load_config(), but determines the ESP/XBOOTLDR paths automatically at first. Also, it looks for a path /run/boot-loader-entries/ and loads the entries from there if it exists. This is supposed to be a hook for other boot loaders to make our tools aware of their own entries. --- src/shared/bootspec.c | 48 +++++++++++++++++++++++++++++++++++++++++++ src/shared/bootspec.h | 1 + 2 files changed, 49 insertions(+) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 4ebb221a42..c6aa2c0f8b 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -670,6 +670,54 @@ int boot_entries_load_config( return 0; } +int boot_entries_load_config_auto( + const char *override_esp_path, + const char *override_xbootldr_path, + BootConfig *config) { + + _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL; + int r; + + assert(config); + + /* This function is similar to boot_entries_load_config(), however we automatically search for the + * ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass + * an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's + * read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from + * it). This allows other boot loaders to pass boot loader entry information to our tools if they + * want to. */ + + if (!override_esp_path && !override_xbootldr_path) { + + if (access("/run/boot-loader-entries/", F_OK) < 0) { + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine whether /run/boot-loader-entries/ exists: %m"); + } else { + r = boot_entries_load_config("/run/boot-loader-entries/", NULL, config); + if (r < 0) + return r; + + return 0; + } + } + + r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL); + if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */ + return log_error_errno(r, "Cannot find the ESP partition mount point."); + if (r < 0) /* But it logs about all these cases, hence don't log here again */ + return r; + + r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL); + if (r < 0 && r != -ENOKEY) + return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */ + + r = boot_entries_load_config(esp_where, xbootldr_where, config); + if (r < 0) + return r; + + return 0; +} + int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { static const char * const title_table[] = { diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 658905a4cb..b77497085e 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -63,6 +63,7 @@ static inline bool boot_config_has_entry(BootConfig *config, const char *id) { void boot_config_free(BootConfig *config); int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); +int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config); int boot_entries_augment_from_loader(BootConfig *config, bool only_auto); static inline const char* boot_entry_title(const BootEntry *entry) { From b1a877199635cdd340ba3ce3a7725471691d189c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2019 17:57:54 +0100 Subject: [PATCH 12/19] bootspec: rework find_default_boot_entry() to use boot_entries_load_config_auto() it's pretty much the same code now, hence use it. --- src/shared/bootspec.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index c6aa2c0f8b..4e73e6d4ab 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1416,25 +1416,14 @@ int find_default_boot_entry( BootConfig *config, const BootEntry **e) { - _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL; int r; assert(config); assert(e); - r = find_esp_and_warn(NULL, false, &esp_where, NULL, NULL, NULL, NULL); - if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */ - return log_error_errno(r, "Cannot find the ESP partition mount point."); - if (r < 0) /* But it logs about all these cases, hence don't log here again */ - return r; - - r = find_xbootldr_and_warn(NULL, false, &xbootldr_where, NULL); - if (r < 0 && r != -ENOKEY) - return r; - - r = boot_entries_load_config(esp_where, xbootldr_where, config); + r = boot_entries_load_config_auto(NULL, NULL, config); if (r < 0) - return log_error_errno(r, "Failed to load boot loader entries: %m"); + return r; if (config->default_entry < 0) return log_error_errno(SYNTHETIC_ERRNO(ENOENT), From 38bd74d67e5d2f2c12b1135ff6872544fe944a3f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2019 18:02:30 +0100 Subject: [PATCH 13/19] bootspec: get rid of find_default_boot_entry() entirely Now only two operations are left. Let's just move this into the caller, since it should make things simpler, clearer and shorter, in particular as there's only a single user for this. --- src/shared/bootspec.c | 23 ----------------------- src/shared/bootspec.h | 9 +++++++-- src/systemctl/systemctl.c | 9 ++++++++- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 4e73e6d4ab..03f8629735 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1412,29 +1412,6 @@ found: return 0; } -int find_default_boot_entry( - BootConfig *config, - const BootEntry **e) { - - int r; - - assert(config); - assert(e); - - r = boot_entries_load_config_auto(NULL, NULL, config); - if (r < 0) - return r; - - if (config->default_entry < 0) - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "No boot loader entry suitable as default, refusing to guess."); - - *e = &config->entries[config->default_entry]; - log_debug("Found default boot loader entry in file \"%s\"", (*e)->path); - - return 0; -} - static const char* const boot_entry_type_table[_BOOT_ENTRY_MAX] = { [BOOT_ENTRY_CONF] = "conf", [BOOT_ENTRY_UNIFIED] = "unified", diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index b77497085e..45e1fc37c6 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -61,6 +61,13 @@ static inline bool boot_config_has_entry(BootConfig *config, const char *id) { return false; } +static inline BootEntry* boot_config_default_entry(BootConfig *config) { + if (config->default_entry < 0) + return NULL; + + return config->entries + config->default_entry; +} + void boot_config_free(BootConfig *config); int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config); @@ -73,7 +80,5 @@ static inline const char* boot_entry_title(const BootEntry *entry) { int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); -int find_default_boot_entry(BootConfig *config, const BootEntry **e); - const char* boot_entry_type_to_string(BootEntryType t) _const_; BootEntryType boot_entry_type_from_string(const char *s) _pure_; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index d88435f8e9..9c67a69c23 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3535,10 +3535,17 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(&config, &e); + r = boot_entries_load_config_auto(NULL, NULL, &config); if (r < 0) return r; + e = boot_config_default_entry(&config); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "No boot loader entry suitable as default, refusing to guess."); + + log_debug("Found default boot loader entry in file \"%s\"", e->path); + if (!e->kernel) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Boot entry does not refer to Linux kernel, which is not supported currently."); From eed7210a5c13299364cddf3600c2e76d4889ab36 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2019 19:19:30 +0100 Subject: [PATCH 14/19] bootspec: validate bootpsec entry ids before we use them --- src/shared/bootspec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 03f8629735..39a7a97b12 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -66,13 +66,16 @@ static int boot_entry_load( c = endswith_no_case(path, ".conf"); if (!c) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path); b = basename(path); tmp.id = strndup(b, c - b); if (!tmp.id) return log_oom(); + if (!efi_loader_entry_name_valid(tmp.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path); + tmp.path = strdup(path); if (!tmp.path) return log_oom(); @@ -310,6 +313,9 @@ static int boot_entry_load_unified( if (!tmp.id) return log_oom(); + if (!efi_loader_entry_name_valid(tmp.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id); + tmp.path = strdup(path); if (!tmp.path) return log_oom(); From 31b221cf5e9755656441e5c4ca4524438c5560ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Oct 2018 20:06:52 +0200 Subject: [PATCH 15/19] logind: add support for booting into the boot menu or a specific boot menu entry This behaves similar to the "boot into firmware" logic, and also allows either direct EFI operation (which sd-boot supports and others might support eventually too) or override through env var. --- src/login/logind-dbus.c | 461 ++++++++++++++++++++++++ src/login/org.freedesktop.login1.conf | 16 + src/login/org.freedesktop.login1.policy | 24 +- units/systemd-logind.service.in | 2 +- 4 files changed, 501 insertions(+), 2 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 262b0290a8..7c1e531d2e 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "audit-util.h" +#include "bootspec.h" #include "bus-common-errors.h" #include "bus-error.h" #include "bus-unit-util.h" @@ -28,11 +29,13 @@ #include "logind.h" #include "missing_capability.h" #include "mkdir.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "selinux-util.h" #include "sleep-config.h" #include "special.h" +#include "stdio-util.h" #include "strv.h" #include "terminal-util.h" #include "tmpfile-util.h" @@ -2542,6 +2545,457 @@ static int method_can_reboot_to_firmware_setup( return sd_bus_reply_method_return(message, "s", result); } +static int property_get_reboot_to_boot_loader_menu( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t x = UINT64_MAX; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + _cleanup_free_ char *v = NULL; + + /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished: + * + * 1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user) + * 2. Variable set to "0", boot into boot loader menu is enabled with no timeout (we return 0 to the user) + * 3. Variable set to numeric value formatted in ASCII, boot into boot loader menu with the specified timeout in seconds + */ + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable: %m"); + } else { + uint64_t sec; + + r = safe_atou64(v, &sec); + if (r < 0) + log_warning_errno(r, "Failed to parse LoaderConfigTimeoutOneShot value '%s': %m", v); + else if (sec > (USEC_INFINITY / USEC_PER_SEC)) + log_warning("LoaderConfigTimeoutOneShot too large, ignoring: %m"); + else + x = sec * USEC_PER_SEC; /* return in µs */ + } + + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + else if (r > 0) { + _cleanup_free_ char *v = NULL; + + /* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-menu. */ + + r = read_one_line_file("/run/systemd/reboot-to-boot-loader-menu", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-menu: %m"); + } else { + r = safe_atou64(v, &x); + if (r < 0) + log_warning_errno(r, "Failed to parse /run/systemd/reboot-to-boot-loader-menu: %m"); + } + } + + return sd_bus_message_append(reply, "t", x); +} + +static int method_set_reboot_to_boot_loader_menu( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool use_efi; + uint64_t x; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "t", &x); + if (r < 0) + return r; + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + uint64_t features; + + /* EFI case: let's see if booting into boot loader menu is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu."); + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */ + use_efi = false; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (use_efi) { + if (x == UINT64_MAX) + r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", NULL, 0); + else { + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + xsprintf(buf, "%" PRIu64, DIV_ROUND_UP(x, USEC_PER_SEC)); /* second granularity */ + + r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", buf); + } + if (r < 0) + return r; + } else { + if (x == UINT64_MAX) { + if (unlink("/run/systemd/reboot-to-loader-menu") < 0 && errno != ENOENT) + return -errno; + } else { + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + + xsprintf(buf, "%" PRIu64, x); /* µs granularity */ + + r = write_string_file_atomic_label("/run/systemd/reboot-to-loader-menu", buf); + if (r < 0) + return r; + } + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_boot_loader_menu( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + const char *result = NULL; + Manager *m = userdata; + bool challenge; + int r; + + + assert(message); + assert(m); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + uint64_t features = 0; + + /* EFI case, let's see if booting into boot loader menu is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT)) + result = "na"; + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + + result = "na"; + } + + if (result) + return sd_bus_reply_method_return(message, "s", result); + + r = bus_test_polkit(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + NULL, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + +static int property_get_reboot_to_boot_loader_entry( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *v = NULL; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + /* EFI case: let's read the LoaderEntryOneShot variable */ + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read LoaderEntryOneShot variable: %m"); + } else if (!efi_loader_entry_name_valid(v)) { + log_warning("LoaderEntryOneShot contains invalid entry name '%s', ignoring.", v); + v = mfree(v); + } + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + else if (r > 0) { + + /* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-entry. */ + + r = read_one_line_file("/run/systemd/reboot-to-boot-loader-entry", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry: %m"); + } else if (!efi_loader_entry_name_valid(v)) { + log_warning("/run/systemd/reboot-to-boot-loader-entry is not valid, ignoring."); + v = mfree(v); + } + } + + return sd_bus_message_append(reply, "s", v); +} + +static int boot_loader_entry_exists(const char *id) { + _cleanup_(boot_config_free) BootConfig config = {}; + int r; + + assert(id); + + r = boot_entries_load_config_auto(NULL, NULL, &config); + if (r < 0) + return r; + + (void) boot_entries_augment_from_loader(&config, true); + + return boot_config_has_entry(&config, id); +} + +static int method_set_reboot_to_boot_loader_entry( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool use_efi; + const char *v; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &v); + if (r < 0) + return r; + + if (isempty(v)) + v = NULL; + else if (efi_loader_entry_name_valid(v)) { + r = boot_loader_entry_exists(v); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader entry '%s' is not known.", v); + } else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Boot loader entry name '%s' is not valid, refusing.", v); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + uint64_t features; + + /* EFI case: let's see if booting into boot loader entry is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot into boot loader entry is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry."); + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */ + use_efi = false; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (use_efi) { + if (isempty(v)) + /* Delete item */ + r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderEntryOneShot", NULL, 0); + else + r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", v); + if (r < 0) + return r; + } else { + if (isempty(v)) { + if (unlink("/run/systemd/reboot-to-boot-loader-entry") < 0 && errno != ENOENT) + return -errno; + } else { + r = write_string_file_atomic_label("/run/systemd/reboot-boot-to-loader-entry", v); + if (r < 0) + return r; + } + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_boot_loader_entry( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + const char *result = NULL; + Manager *m = userdata; + bool challenge; + int r; + + assert(message); + assert(m); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + uint64_t features = 0; + + /* EFI case, let's see if booting into boot loader entry is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader entry is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT)) + result = "na"; + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + + result = "na"; + } + + if (result) + return sd_bus_reply_method_return(message, "s", result); + + r = bus_test_polkit(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + NULL, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + +static int property_get_boot_loader_entries( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_(boot_config_free) BootConfig config = {}; + size_t i; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = boot_entries_load_config_auto(NULL, NULL, &config); + if (r < 0) + return r; + + (void) boot_entries_augment_from_loader(&config, true); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + for (i = 0; i < config.n_entries; i++) { + BootEntry *e = config.entries + i; + + r = sd_bus_message_append(reply, "s", e->id); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + static int method_set_wall_message( sd_bus_message *message, void *userdata, @@ -2707,6 +3161,9 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0), + SD_BUS_PROPERTY("RebootToBootLoaderMenu", "t", property_get_reboot_to_boot_loader_menu, 0, 0), + SD_BUS_PROPERTY("RebootToBootLoaderEntry", "s", property_get_reboot_to_boot_loader_entry, 0, 0), + SD_BUS_PROPERTY("BootLoaderEntries", "as", property_get_boot_loader_entries, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -2781,6 +3238,10 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToBootLoaderMenu", "t", NULL, method_set_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToBootLoaderEntry", NULL, "s", method_can_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToBootLoaderEntry", "s", NULL, method_set_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("SessionNew", "so", 0), diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index f880f3e2da..5677c15b8a 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -198,6 +198,22 @@ send_interface="org.freedesktop.login1.Manager" send_member="SetRebootToFirmwareSetup"/> + + + + + + + + diff --git a/src/login/org.freedesktop.login1.policy b/src/login/org.freedesktop.login1.policy index 5ee62ab5b4..671384d34e 100644 --- a/src/login/org.freedesktop.login1.policy +++ b/src/login/org.freedesktop.login1.policy @@ -338,7 +338,7 @@ - Allow indication to the firmware to boot to setup interface + Indicate to the firmware to boot to setup interface Authentication is required to indicate to the firmware to boot to setup interface. auth_admin_keep @@ -348,6 +348,28 @@ org.freedesktop.login1.reboot + + Indicate to the boot loader to boot to the boot loader menu + Authentication is required to indicate to the boot loader to boot to the boot loader menu. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + + + Indicate to the boot loader to boot a specific entry + Authentication is required to indicate to the boot loader to boot into a specific boot loader entry. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + Set a wall message Authentication is required to set a wall message diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index fb6fda4907..a864f66c68 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -21,7 +21,7 @@ After=dbus.socket [Service] BusName=org.freedesktop.login1 -CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE ExecStart=@rootlibexecdir@/systemd-logind FileDescriptorStoreMax=512 IPAddressDeny=any From 97af80c5a7029c3f92e982dcf9338b9e67ad9cde Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Oct 2018 20:43:45 +0200 Subject: [PATCH 16/19] systemctl: add support for booting into boot menu/entry (This also removes support for booting into the EFI firmware setup without logind. That's because otherwise the non-EFI fallback logind implements can't work.) Fixes: #9896 --- man/systemctl.xml | 29 ++++++-- src/systemctl/systemctl.c | 150 ++++++++++++++++++++++++++++++++++---- 2 files changed, 161 insertions(+), 18 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 08aacd8f41..512c255b39 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -617,11 +617,30 @@ - When used with the reboot command, - indicate to the system's firmware to boot into setup - mode. Note that this is currently only supported on some EFI - systems and only if the system was booted in EFI - mode. + When used with the reboot command, indicate to the system's firmware to reboot into + the firmware setup interface. Note that this functionality is not available on all systems. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to show the + boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass + zero in order to disable the menu time-out. Note that not all boot loaders support this + functionality. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to boot into + a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or + help in order to list available entries. Note that not all boot loaders support this + functionality. diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9c67a69c23..38897ecefb 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -170,6 +170,8 @@ static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static bool arg_firmware_setup = false; +static usec_t arg_boot_loader_menu = USEC_INFINITY; +static const char *arg_boot_loader_entry = NULL; static bool arg_now = false; static bool arg_jobs_before = false; static bool arg_jobs_after = false; @@ -3473,7 +3475,11 @@ static int logind_check_inhibitors(enum action a) { #endif } -static int logind_prepare_firmware_setup(void) { +static int prepare_firmware_setup(void) { + + if (!arg_firmware_setup) + return 0; + #if ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; @@ -3497,27 +3503,75 @@ static int logind_prepare_firmware_setup(void) { return 0; #else - log_error("Cannot remotely indicate to EFI to boot into setup mode."); + log_error("Booting into firmware setup not supported."); return -ENOSYS; #endif } -static int prepare_firmware_setup(void) { - int r; +static int prepare_boot_loader_menu(void) { - if (!arg_firmware_setup) + if (arg_boot_loader_menu == USEC_INFINITY) return 0; - if (arg_transport == BUS_TRANSPORT_LOCAL) { +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; - r = efi_set_reboot_to_firmware(true); - if (r < 0) - log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m"); - else - return r; - } + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; - return logind_prepare_firmware_setup(); + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderMenu", + &error, + NULL, + "t", arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r)); + + return 0; +#else + log_error("Booting into boot loader menu not supported."); + return -ENOSYS; +#endif +} + +static int prepare_boot_loader_entry(void) { + + if (!arg_boot_loader_entry) + return 0; + +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderEntry", + &error, + NULL, + "s", arg_boot_loader_entry); + if (r < 0) + return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r)); + + return 0; +#else + log_error("Booting into boot loader entry not supported."); + return -ENOSYS; +#endif } static int load_kexec_kernel(void) { @@ -3644,6 +3698,14 @@ static int start_special(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = prepare_boot_loader_menu(); + if (r < 0) + return r; + + r = prepare_boot_loader_entry(); + if (r < 0) + return r; + if (a == ACTION_REBOOT && argc > 1) { r = update_reboot_parameter_and_warn(argv[1]); if (r < 0) @@ -7522,6 +7584,10 @@ static int systemctl_help(void) { " short-monotonic, short-unix,\n" " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" + " --boot-loader-menu=TIME\n" + " Boot into boot loader menu on next boot\n" + " --boot-loader-entry=NAME\n" + " Boot into a specific boot loader entry on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" "Unit Commands:\n" " list-units [PATTERN...] List units currently in memory\n" @@ -7776,6 +7842,39 @@ static void help_states(void) { DUMP_STRING_TABLE(timer_state, TimerState, _TIMER_STATE_MAX); } +static int help_boot_loader_entry(void) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char **l = NULL; + sd_bus *bus; + char **i; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "BootLoaderEntries", + &error, + &l); + if (r < 0) + return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r)); + + if (strv_isempty(l)) { + log_error("No boot loader entries discovered."); + return -ENODATA; + } + + STRV_FOREACH(i, l) + puts(*i); + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, @@ -7806,6 +7905,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_JOB_MODE, ARG_PRESET_MODE, ARG_FIRMWARE_SETUP, + ARG_BOOT_LOADER_MENU, + ARG_BOOT_LOADER_ENTRY, ARG_NOW, ARG_MESSAGE, ARG_WAIT, @@ -7855,6 +7956,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "recursive", no_argument, NULL, 'r' }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU }, + { "boot-loader-entry", required_argument, NULL, ARG_BOOT_LOADER_ENTRY }, { "now", no_argument, NULL, ARG_NOW }, { "message", required_argument, NULL, ARG_MESSAGE }, {} @@ -8128,6 +8231,27 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_firmware_setup = true; break; + case ARG_BOOT_LOADER_MENU: + + r = parse_sec(optarg, &arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Failed to parse --boot-loader-menu= argument '%s': %m", optarg); + + break; + + case ARG_BOOT_LOADER_ENTRY: + + if (streq(optarg, "help")) { /* Yes, this means, "help" is not a valid boot loader entry name we can deal with */ + r = help_boot_loader_entry(); + if (r < 0) + return r; + + return 0; + } + + arg_boot_loader_entry = empty_to_null(optarg); + break; + case ARG_STATE: { if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), From 350f9518ac5fcd9c4a643ae61524003687939c83 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Oct 2018 13:58:41 +0200 Subject: [PATCH 17/19] logind: refactor logic of returning polkit challenge result into its own function Let's share some code. --- src/login/logind-dbus.c | 128 +++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 75 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 7c1e531d2e..13e7d3bcda 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2488,14 +2488,38 @@ static int method_set_reboot_to_firmware_setup( return sd_bus_reply_method_return(message, NULL); } +static int return_test_polkit( + sd_bus_message *message, + int capability, + const char *action, + const char **details, + uid_t good_user, + sd_bus_error *e) { + + const char *result; + bool challenge; + int r; + + r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + static int method_can_reboot_to_firmware_setup( sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *result = NULL; Manager *m = userdata; - bool challenge; int r; assert(message); @@ -2510,7 +2534,7 @@ static int method_can_reboot_to_firmware_setup( if (r != -EOPNOTSUPP) log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m"); - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } } else if (r <= 0) { @@ -2519,30 +2543,16 @@ static int method_can_reboot_to_firmware_setup( if (r < 0) log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } - if (result) - return sd_bus_reply_method_return(message, "s", result); - - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - UID_INVALID, - &challenge, - error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - - return sd_bus_reply_method_return(message, "s", result); + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + UID_INVALID, + error); } static int property_get_reboot_to_boot_loader_menu( @@ -2698,9 +2708,7 @@ static int method_can_reboot_to_boot_loader_menu( void *userdata, sd_bus_error *error) { - const char *result = NULL; Manager *m = userdata; - bool challenge; int r; @@ -2717,7 +2725,7 @@ static int method_can_reboot_to_boot_loader_menu( if (r < 0) log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m"); if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT)) - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } else if (r <= 0) { /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU */ @@ -2725,30 +2733,16 @@ static int method_can_reboot_to_boot_loader_menu( if (r < 0) log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } - if (result) - return sd_bus_reply_method_return(message, "s", result); - - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-boot-loader-menu", - NULL, - UID_INVALID, - &challenge, - error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - - return sd_bus_reply_method_return(message, "s", result); + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + NULL, + UID_INVALID, + error); } static int property_get_reboot_to_boot_loader_entry( @@ -2906,9 +2900,7 @@ static int method_can_reboot_to_boot_loader_entry( void *userdata, sd_bus_error *error) { - const char *result = NULL; Manager *m = userdata; - bool challenge; int r; assert(message); @@ -2924,7 +2916,7 @@ static int method_can_reboot_to_boot_loader_entry( if (r < 0) log_warning_errno(r, "Failed to determine whether reboot to boot loader entry is supported: %m"); if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT)) - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } else if (r <= 0) { /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY */ @@ -2932,30 +2924,16 @@ static int method_can_reboot_to_boot_loader_entry( if (r < 0) log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); - result = "na"; + return sd_bus_reply_method_return(message, "s", "na"); } - if (result) - return sd_bus_reply_method_return(message, "s", result); - - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-boot-loader-entry", - NULL, - UID_INVALID, - &challenge, - error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - - return sd_bus_reply_method_return(message, "s", result); + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + NULL, + UID_INVALID, + error); } static int property_get_boot_loader_entries( From e86c7a3abc744eca5f4e634a9040a15865bba01d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Oct 2018 15:53:18 +0200 Subject: [PATCH 18/19] docs: document the new environment variables logind groks --- docs/ENVIRONMENT.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 99b5b03b68..f72ed20dd0 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -107,6 +107,51 @@ systemd-udevd: with `:` in which case the kernel command line option takes precedence, if it is specified as well. +* `$SYSTEMD_REBOOT_TO_FIRMWARE_SETUP` — if set overrides systemd-logind's + built-in EFI logic of requesting a reboot into the firmware. Takes a + boolean. If set to false the functionality is turned off entirely. If set to + true instead of requesting a reboot into the firmware setup UI through EFI a + file `/run/systemd/reboot-to-firmware-setup` is created whenever this is + requested. This file may be checked for by services run during system + shutdown in order to request the appropriate operation from the firmware in + an alternative fashion. + +* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU` — similar to the above, allows + overriding of systemd-logind's built-in EFI logic of requesting a reboot into + the boot loader menu. Takes a boolean. If set to false the functionality is + turned off entirely. If set to true instead of requesting a reboot into the + boot loader menu through EFI a file `/run/systemd/reboot-to-boot-loader-menu` + is created whenever this is requested. The file contains the requested boot + loader menu timeout in µs, formatted in ASCII decimals, or zero in case no + time-out is requested. This file may be checked for by services run during + system shutdown in order to request the appropriate operation from the boot + loader in an alternative fashion. + +* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY` — similar to the above, allows + overriding of systemd-logind's built-in EFI logic of requesting a reboot into + a specific boot loader entry. Takes a boolean. If set to false the + functionality is turned off entirely. If set to true instead of requesting a + reboot into a specific boot loader entry through EFI a file + `/run/systemd/reboot-to-boot-loader-entry` is created whenever this is + requested. The file contains the requested boot loader entry identifier. This + file may be checked for by services run during system shutdown in order to + request the appropriate operation from the boot loader in an alternative + fashion. Note that by default only boot loader entries which follow the [Boot + Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION) and are + placed in the ESP or the Extended Boot Loader partition may be selected this + way. However, if a directory `/run/boot-loader-entries/` exists, the entries + are loaded from there instead. The directory should contain the usual + directory hierarchy mandated by the Boot Loader Specification, i.e. the entry + drop-ins should be placed in + `/run/boot-loader-entries/loader/entries/*.conf`, and the files referenced by + the drop-ins (including the kernels and initrds) somewhere else below + `/run/boot-loader-entries/`. Note that all these files may be (and are + supposed to be) symlinks. systemd-logind will load these files on-demand, + these files can hence be updated (ideally atomically) whenever the boot + loader configuration changes. A foreign boot loader installer script should + hence synthesize drop-in snippets and symlinks for all boot entries at boot + or whenever they change if it wants to integrate with systemd-logind's APIs. + installed systemd tests: * `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if From 12be0693eb66dcbb8b20c5e15e229567efc69f56 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 23 Oct 2018 14:17:22 +0200 Subject: [PATCH 19/19] update TODO --- TODO | 5 ----- 1 file changed, 5 deletions(-) diff --git a/TODO b/TODO index 18fdf4e257..bcf1d02b94 100644 --- a/TODO +++ b/TODO @@ -91,17 +91,12 @@ Features: show state of these flags, and optionally trigger such a factory reset on next boot by setting the flag. -* sd-boot: add "oneshot boot timeout" variable support - * sd-boot: automatically load EFI modules from some drop-in dir, so that people can add in file system drivers and such * sd-boot: optionally, show boot menu when previous default boot item has non-zero "tries done" count -* logind: add "boot into bootmenu" API, and possibly even "boot into windows" - and "boot into macos". - * maybe set a special xattr on cgroups that have delegate=yes set, to make it easy to mark cut points