diff options
-rw-r--r-- | src/boot/bootctl-install.c | 4 | ||||
-rw-r--r-- | src/boot/bootctl-util.c | 98 | ||||
-rw-r--r-- | src/boot/bootctl.c | 29 | ||||
-rw-r--r-- | src/boot/bootctl.h | 11 | ||||
-rw-r--r-- | src/shared/boot-entry.c | 224 | ||||
-rw-r--r-- | src/shared/boot-entry.h | 25 | ||||
-rw-r--r-- | src/shared/meson.build | 1 |
7 files changed, 267 insertions, 125 deletions
diff --git a/src/boot/bootctl-install.c b/src/boot/bootctl-install.c index dc7ea8c5ca..a50f984a23 100644 --- a/src/boot/bootctl-install.c +++ b/src/boot/bootctl-install.c @@ -133,7 +133,7 @@ static int settle_make_entry_directory(void) { bool layout_type1 = use_boot_loader_spec_type1(); if (arg_make_entry_directory < 0) { /* Automatic mode */ if (layout_type1) { - if (arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID) { + if (arg_entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID) { r = path_is_temporary_fs("/etc/machine-id"); if (r < 0) return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m"); @@ -510,7 +510,7 @@ static int install_entry_token(void) { /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry * directory, or if anything else but the machine ID */ - if (!arg_make_entry_directory && arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID) + if (!arg_make_entry_directory && arg_entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID) return 0; p = path_join(arg_root, etc_kernel(), "entry-token"); diff --git a/src/boot/bootctl-util.c b/src/boot/bootctl-util.c index 62c0b64406..6cae9b4deb 100644 --- a/src/boot/bootctl-util.c +++ b/src/boot/bootctl-util.c @@ -5,11 +5,8 @@ #include "bootctl.h" #include "bootctl-util.h" #include "fileio.h" -#include "os-util.h" -#include "path-util.h" #include "stat-util.h" #include "sync-util.h" -#include "utf8.h" int sync_everything(void) { int ret = 0, k; @@ -118,93 +115,14 @@ finish: int settle_entry_token(void) { int r; - switch (arg_entry_token_type) { - - case ARG_ENTRY_TOKEN_AUTO: { - _cleanup_free_ char *buf = NULL, *p = NULL; - p = path_join(arg_root, etc_kernel(), "entry-token"); - if (!p) - return log_oom(); - r = read_one_line_file(p, &buf); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to read %s: %m", p); - - if (!isempty(buf)) { - free_and_replace(arg_entry_token, buf); - arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL; - } else if (sd_id128_is_null(arg_machine_id)) { - _cleanup_free_ char *id = NULL, *image_id = NULL; - - r = parse_os_release(arg_root, - "IMAGE_ID", &image_id, - "ID", &id); - if (r < 0) - return log_error_errno(r, "Failed to load /etc/os-release: %m"); - - if (!isempty(image_id)) { - free_and_replace(arg_entry_token, image_id); - arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID; - } else if (!isempty(id)) { - free_and_replace(arg_entry_token, id); - arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID; - } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields."); - } else { - r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); - if (r < 0) - return r; - - arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID; - } - - break; - } - - case ARG_ENTRY_TOKEN_MACHINE_ID: - if (sd_id128_is_null(arg_machine_id)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); - - r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); - if (r < 0) - return r; - - break; - - case ARG_ENTRY_TOKEN_OS_IMAGE_ID: { - _cleanup_free_ char *buf = NULL; - - r = parse_os_release(arg_root, "IMAGE_ID", &buf); - if (r < 0) - return log_error_errno(r, "Failed to load /etc/os-release: %m"); - - if (isempty(buf)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IMAGE_ID= field not set in /etc/os-release."); - - free_and_replace(arg_entry_token, buf); - break; - } - - case ARG_ENTRY_TOKEN_OS_ID: { - _cleanup_free_ char *buf = NULL; - - r = parse_os_release(arg_root, "ID", &buf); - if (r < 0) - return log_error_errno(r, "Failed to load /etc/os-release: %m"); - - if (isempty(buf)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ID= field not set in /etc/os-release."); - - free_and_replace(arg_entry_token, buf); - break; - } - - case ARG_ENTRY_TOKEN_LITERAL: - assert(!isempty(arg_entry_token)); /* already filled in by command line parser */ - break; - } - - if (isempty(arg_entry_token) || !(utf8_is_valid(arg_entry_token) && string_is_safe(arg_entry_token))) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token); + r = boot_entry_token_ensure( + arg_root, + etc_kernel(), + arg_machine_id, + &arg_entry_token_type, + &arg_entry_token); + if (r < 0) + return r; log_debug("Using entry token: %s", arg_entry_token); return 0; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 022e680255..710cf8da65 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -43,7 +43,7 @@ bool arg_quiet = false; int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */ sd_id128_t arg_machine_id = SD_ID128_NULL; char *arg_install_layout = NULL; -EntryTokenType arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO; +BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO; char *arg_entry_token = NULL; JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; bool arg_arch_all = false; @@ -328,30 +328,11 @@ static int parse_argv(int argc, char *argv[]) { arg_quiet = true; break; - case ARG_ENTRY_TOKEN: { - const char *e; - - if (streq(optarg, "machine-id")) { - arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID; - arg_entry_token = mfree(arg_entry_token); - } else if (streq(optarg, "os-image-id")) { - arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID; - arg_entry_token = mfree(arg_entry_token); - } else if (streq(optarg, "os-id")) { - arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID; - arg_entry_token = mfree(arg_entry_token); - } else if ((e = startswith(optarg, "literal:"))) { - arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL; - - r = free_and_strdup_warn(&arg_entry_token, e); - if (r < 0) - return r; - } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Unexpected parameter for --entry-token=: %s", optarg); - + case ARG_ENTRY_TOKEN: + r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token); + if (r < 0) + return r; break; - } case ARG_MAKE_ENTRY_DIRECTORY: if (streq(optarg, "auto")) /* retained for backwards compatibility */ diff --git a/src/boot/bootctl.h b/src/boot/bootctl.h index 9012bf932b..c87d43694f 100644 --- a/src/boot/bootctl.h +++ b/src/boot/bootctl.h @@ -3,17 +3,10 @@ #include "sd-id128.h" +#include "boot-entry.h" #include "json.h" #include "pager.h" -typedef enum EntryTokenType { - ARG_ENTRY_TOKEN_MACHINE_ID, - ARG_ENTRY_TOKEN_OS_IMAGE_ID, - ARG_ENTRY_TOKEN_OS_ID, - ARG_ENTRY_TOKEN_LITERAL, - ARG_ENTRY_TOKEN_AUTO, -} EntryTokenType; - typedef enum InstallSource { ARG_INSTALL_SOURCE_IMAGE, ARG_INSTALL_SOURCE_HOST, @@ -32,7 +25,7 @@ extern bool arg_quiet; extern int arg_make_entry_directory; /* tri-state: < 0 for automatic logic */ extern sd_id128_t arg_machine_id; extern char *arg_install_layout; -extern EntryTokenType arg_entry_token_type; +extern BootEntryTokenType arg_entry_token_type; extern char *arg_entry_token; extern JsonFormatFlags arg_json_format_flags; extern bool arg_arch_all; diff --git a/src/shared/boot-entry.c b/src/shared/boot-entry.c new file mode 100644 index 0000000000..90e72bd754 --- /dev/null +++ b/src/shared/boot-entry.c @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "boot-entry.h" +#include "fileio.h" +#include "id128-util.h" +#include "os-util.h" +#include "path-util.h" +#include "string-util.h" +#include "utf8.h" + +bool boot_entry_token_valid(const char *p) { + return utf8_is_valid(p) && string_is_safe(p) && filename_is_valid(p); +} + +static int entry_token_load(const char *root, const char *etc_kernel, BootEntryTokenType *type, char **token) { + _cleanup_free_ char *buf = NULL, *p = NULL; + int r; + + assert(type); + assert(*type == BOOT_ENTRY_TOKEN_AUTO); + assert(token); + + if (!etc_kernel) + return 0; + + p = path_join(root, etc_kernel, "entry-token"); + if (!p) + return log_oom(); + + r = read_one_line_file(p, &buf); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", p); + + if (isempty(buf)) + return 0; + + if (!boot_entry_token_valid(buf)) { + log_debug("Invalid entry token specified in %s, ignoring.", p); + return 0; + } + + *token = TAKE_PTR(buf); + *type = BOOT_ENTRY_TOKEN_LITERAL; + return 1; +} + +static int entry_token_from_machine_id(sd_id128_t machine_id, BootEntryTokenType *type, char **token) { + char *p; + + assert(type); + assert(IN_SET(*type, BOOT_ENTRY_TOKEN_AUTO, BOOT_ENTRY_TOKEN_MACHINE_ID)); + assert(token); + + if (sd_id128_is_null(machine_id)) + return 0; + + p = strdup(SD_ID128_TO_STRING(machine_id)); + if (!p) + return log_oom(); + + *token = p; + *type = BOOT_ENTRY_TOKEN_MACHINE_ID; + return 1; +} + +static int entry_token_from_os_release(const char *root, BootEntryTokenType *type, char **token) { + _cleanup_free_ char *id = NULL, *image_id = NULL; + int r; + + assert(type); + assert(IN_SET(*type, BOOT_ENTRY_TOKEN_AUTO, BOOT_ENTRY_TOKEN_OS_IMAGE_ID, BOOT_ENTRY_TOKEN_OS_ID)); + assert(token); + + switch (*type) { + case BOOT_ENTRY_TOKEN_AUTO: + r = parse_os_release(root, + "IMAGE_ID", &image_id, + "ID", &id); + break; + + case BOOT_ENTRY_TOKEN_OS_IMAGE_ID: + r = parse_os_release(root, "IMAGE_ID", &image_id); + break; + + case BOOT_ENTRY_TOKEN_OS_ID: + r = parse_os_release(root, "ID", &id); + break; + + default: + assert_not_reached(); + } + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to load %s/etc/os-release: %m", strempty(root)); + + if (!isempty(image_id) && boot_entry_token_valid(image_id)) { + *token = TAKE_PTR(image_id); + *type = BOOT_ENTRY_TOKEN_OS_IMAGE_ID; + return 1; + } + + if (!isempty(id) && boot_entry_token_valid(id)) { + *token = TAKE_PTR(id); + *type = BOOT_ENTRY_TOKEN_OS_ID; + return 1; + } + + return 0; +} + +int boot_entry_token_ensure( + const char *root, + const char *etc_kernel, + sd_id128_t machine_id, + BootEntryTokenType *type, + char **token) { + + int r; + + assert(type); + assert(token); + + if (*token) + return 0; /* Already set. */ + + switch (*type) { + + case BOOT_ENTRY_TOKEN_AUTO: + r = entry_token_load(root, etc_kernel, type, token); + if (r != 0) + return r; + + r = entry_token_from_machine_id(machine_id, type, token); + if (r != 0) + return r; + + r = entry_token_from_os_release(root, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No machine ID set, and %s/etc/os-release carries no ID=/IMAGE_ID= fields.", + strempty(root)); + + case BOOT_ENTRY_TOKEN_MACHINE_ID: + r = entry_token_from_machine_id(machine_id, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); + + case BOOT_ENTRY_TOKEN_OS_IMAGE_ID: + r = entry_token_from_os_release(root, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "IMAGE_ID= field not set in %s/etc/os-release.", + strempty(root)); + + case BOOT_ENTRY_TOKEN_OS_ID: + r = entry_token_from_os_release(root, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "ID= field not set in %s/etc/os-release.", + strempty(root)); + + case BOOT_ENTRY_TOKEN_LITERAL: + /* In this case, the token should be already set by the user input. */ + return -EINVAL; + + default: + assert_not_reached(); + } +} + +int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token) { + assert(s); + assert(type); + assert(token); + + /* + * This function is intended to be used in command line parsers, to handle token that are passed in. + * + * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON SUCCESS! + * Hence, do not pass in uninitialized pointers. + */ + + if (streq(s, "machine-id")) { + *type = BOOT_ENTRY_TOKEN_MACHINE_ID; + *token = mfree(*token); + return 0; + } + + if (streq(s, "os-image-id")) { + *type = BOOT_ENTRY_TOKEN_OS_IMAGE_ID; + *token = mfree(*token); + return 0; + } + + if (streq(s, "os-id")) { + *type = BOOT_ENTRY_TOKEN_OS_ID; + *token = mfree(*token); + return 0; + } + + const char *e = startswith(s, "literal:"); + if (e) { + if (!boot_entry_token_valid(e)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid entry token literal is specified for --entry-token=."); + + *type = BOOT_ENTRY_TOKEN_LITERAL; + return free_and_strdup_warn(token, e); + } + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unexpected parameter for --entry-token=: %s", s); +} diff --git a/src/shared/boot-entry.h b/src/shared/boot-entry.h new file mode 100644 index 0000000000..97b30702bc --- /dev/null +++ b/src/shared/boot-entry.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdbool.h> + +#include "sd-id128.h" + +typedef enum BootEntryTokenType { + BOOT_ENTRY_TOKEN_MACHINE_ID, + BOOT_ENTRY_TOKEN_OS_IMAGE_ID, + BOOT_ENTRY_TOKEN_OS_ID, + BOOT_ENTRY_TOKEN_LITERAL, + BOOT_ENTRY_TOKEN_AUTO, +} BootEntryTokenType; + +bool boot_entry_token_valid(const char *p); + +int boot_entry_token_ensure( + const char *root, + const char *etc_kernel, /* will be prefixed with root, typically /etc/kernel. */ + sd_id128_t machine_id, + BootEntryTokenType *type, /* input and output */ + char **token); /* output, but do not pass uninitialized value. */ + +int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token); diff --git a/src/shared/meson.build b/src/shared/meson.build index 9342d25273..cb0697d1b9 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -10,6 +10,7 @@ shared_sources = files( 'bitmap.c', 'blockdev-util.c', 'bond-util.c', + 'boot-entry.c', 'boot-timestamps.c', 'bootspec.c', 'bpf-dlopen.c', |