mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 00:47:10 +09:00
292 lines
8.9 KiB
C
292 lines
8.9 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "boot-entry.h"
|
|
#include "chase.h"
|
|
#include "fd-util.h"
|
|
#include "fileio.h"
|
|
#include "id128-util.h"
|
|
#include "os-util.h"
|
|
#include "path-util.h"
|
|
#include "string-table.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_one(int rfd, const char *dir, BootEntryTokenType *type, char **token) {
|
|
_cleanup_free_ char *buf = NULL, *p = NULL;
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
int r;
|
|
|
|
assert(rfd >= 0 || rfd == AT_FDCWD);
|
|
assert(dir);
|
|
assert(type);
|
|
assert(*type == BOOT_ENTRY_TOKEN_AUTO);
|
|
assert(token);
|
|
|
|
p = path_join(dir, "entry-token");
|
|
if (!p)
|
|
return log_oom();
|
|
|
|
r = chase_and_fopenat_unlocked(rfd, p, CHASE_AT_RESOLVE_IN_ROOT, "re", NULL, &f);
|
|
if (r == -ENOENT)
|
|
return 0;
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to chase and open '%s': %m", p);
|
|
|
|
r = read_line(f, NAME_MAX, &buf);
|
|
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_load(int rfd, const char *conf_root, BootEntryTokenType *type, char **token) {
|
|
int r;
|
|
|
|
assert(rfd >= 0 || rfd == AT_FDCWD);
|
|
assert(type);
|
|
assert(*type == BOOT_ENTRY_TOKEN_AUTO);
|
|
assert(token);
|
|
|
|
if (conf_root)
|
|
return entry_token_load_one(rfd, conf_root, type, token);
|
|
|
|
FOREACH_STRING(path, "/etc/kernel", "/usr/lib/kernel") {
|
|
r = entry_token_load_one(rfd, path, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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(int rfd, BootEntryTokenType *type, char **token) {
|
|
_cleanup_free_ char *id = NULL, *image_id = NULL;
|
|
int r;
|
|
|
|
assert(rfd >= 0 || rfd == AT_FDCWD);
|
|
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_at(rfd,
|
|
"IMAGE_ID", &image_id,
|
|
"ID", &id);
|
|
break;
|
|
|
|
case BOOT_ENTRY_TOKEN_OS_IMAGE_ID:
|
|
r = parse_os_release_at(rfd, "IMAGE_ID", &image_id);
|
|
break;
|
|
|
|
case BOOT_ENTRY_TOKEN_OS_ID:
|
|
r = parse_os_release_at(rfd, "ID", &id);
|
|
break;
|
|
|
|
default:
|
|
assert_not_reached();
|
|
}
|
|
if (r == -ENOENT)
|
|
return 0;
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to load /etc/os-release: %m");
|
|
|
|
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_at(
|
|
int rfd,
|
|
const char *conf_root,
|
|
sd_id128_t machine_id,
|
|
bool machine_id_is_random,
|
|
BootEntryTokenType *type,
|
|
char **token) {
|
|
|
|
int r;
|
|
|
|
assert(rfd >= 0 || rfd == AT_FDCWD);
|
|
assert(type);
|
|
assert(token);
|
|
|
|
if (*token)
|
|
return 0; /* Already set. */
|
|
|
|
switch (*type) {
|
|
|
|
case BOOT_ENTRY_TOKEN_AUTO:
|
|
r = entry_token_load(rfd, conf_root, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
if (!machine_id_is_random) {
|
|
r = entry_token_from_machine_id(machine_id, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
}
|
|
|
|
r = entry_token_from_os_release(rfd, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
if (machine_id_is_random) {
|
|
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, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
|
|
|
|
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(rfd, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"IMAGE_ID= field not set in /etc/os-release.");
|
|
|
|
case BOOT_ENTRY_TOKEN_OS_ID:
|
|
r = entry_token_from_os_release(rfd, type, token);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"ID= field not set in /etc/os-release.");
|
|
|
|
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 boot_entry_token_ensure(
|
|
const char *root,
|
|
const char *conf_root,
|
|
sd_id128_t machine_id,
|
|
bool machine_id_is_random,
|
|
BootEntryTokenType *type,
|
|
char **token) {
|
|
|
|
assert(token);
|
|
|
|
if (*token)
|
|
return 0; /* Already set. */
|
|
|
|
_cleanup_close_ int rfd = -EBADF;
|
|
|
|
rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
|
|
if (rfd < 0)
|
|
return -errno;
|
|
|
|
return boot_entry_token_ensure_at(rfd, conf_root, machine_id, machine_id_is_random, type, token);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static const char *const boot_entry_token_type_table[] = {
|
|
[BOOT_ENTRY_TOKEN_MACHINE_ID] = "machine-id",
|
|
[BOOT_ENTRY_TOKEN_OS_IMAGE_ID] = "os-image-id",
|
|
[BOOT_ENTRY_TOKEN_OS_ID] = "os-id",
|
|
[BOOT_ENTRY_TOKEN_LITERAL] = "literal",
|
|
[BOOT_ENTRY_TOKEN_AUTO] = "auto",
|
|
};
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType);
|