Various smaller tweaks to bootctl (#38996)

This commit is contained in:
Yu Watanabe
2025-09-20 04:28:00 +09:00
committed by GitHub
6 changed files with 167 additions and 164 deletions
+119 -127
View File
@@ -102,7 +102,7 @@ static int load_kernel_install_layout(void) {
int r;
r = load_kernel_install_conf(arg_root,
getenv("KERNEL_INSTALL_CONF_ROOT"),
secure_getenv("KERNEL_INSTALL_CONF_ROOT"),
/* ret_machine_id= */ NULL,
/* ret_boot_root= */ NULL,
&layout,
@@ -210,13 +210,12 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t
r = get_file_version(fd_to, &b);
if (r == -ESRCH)
return log_notice_errno(r, "Skipping \"%s\", it's owned by another boot loader (no version info found).",
to);
return log_info_errno(r, "Skipping \"%s\", it's owned by another boot loader (no version info found).", to);
if (r < 0)
return r;
if (compare_product(a, b) != 0)
return log_notice_errno(SYNTHETIC_ERRNO(ESRCH),
"Skipping \"%s\", it's owned by another boot loader.", to);
return log_info_errno(SYNTHETIC_ERRNO(ESRCH),
"Skipping \"%s\", it's owned by another boot loader.", to);
r = compare_version(a, b);
log_debug("Comparing versions: \"%s\" %s \"%s\"", a, comparison_operator(r), b);
@@ -335,7 +334,11 @@ static int create_subdirs(const char *root, const char * const *subdirs) {
return 0;
}
static int update_efi_boot_binaries(const char *esp_path, const char *source_path) {
static int update_efi_boot_binaries(
const char *esp_path,
const char *source_path,
const char *ignore_filename) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *p = NULL;
int r, ret = 0;
@@ -355,6 +358,9 @@ static int update_efi_boot_binaries(const char *esp_path, const char *source_pat
if (!endswith_no_case(de->d_name, ".efi"))
continue;
if (strcaseeq_ptr(ignore_filename, de->d_name))
continue;
fd = xopenat_full(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, /* mode= */ 0);
if (fd < 0)
return log_error_errno(fd, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
@@ -381,7 +387,7 @@ static int update_efi_boot_binaries(const char *esp_path, const char *source_pat
}
static int copy_one_file(const char *esp_path, const char *name, bool force) {
char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
char *root = IN_SET(arg_install_source, INSTALL_SOURCE_AUTO, INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
_cleanup_free_ char *source_path = NULL, *dest_path = NULL, *p = NULL, *q = NULL;
const char *e;
char *dest_name, *s;
@@ -398,7 +404,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
r = chase(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, &source_path, NULL);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
if (r == -ENOENT && root && arg_install_source == INSTALL_SOURCE_AUTO)
r = chase(p, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, &source_path, NULL);
if (r < 0)
return log_error_errno(r,
@@ -426,7 +432,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
/* Create the EFI default boot loader name (specified for removable devices) */
v = strjoina("/EFI/BOOT/BOOT", e);
ascii_strupper(strrchr(v, '/') + 1);
const char *boot_dot_efi = ascii_strupper(strrchr(v, '/') + 1);
r = chase(v, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT|CHASE_TRIGGER_AUTOFS, &default_dest_path, NULL);
if (r < 0)
@@ -434,24 +440,24 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
RET_GATHER(ret, copy_file_with_version_check(source_path, default_dest_path, force));
/* If we were installed under any other name in /EFI/BOOT, make sure we update those binaries
/* If we were installed under any other name in /EFI/BOOT/, make sure we update those binaries
* as well. */
if (!force)
RET_GATHER(ret, update_efi_boot_binaries(esp_path, source_path));
RET_GATHER(ret, update_efi_boot_binaries(esp_path, source_path, boot_dot_efi));
}
return ret;
}
static int install_binaries(const char *esp_path, const char *arch, bool force) {
char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
char *root = IN_SET(arg_install_source, INSTALL_SOURCE_AUTO, INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *path = NULL;
int r;
r = chase_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, &path, &d);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
if (r == -ENOENT && root && arg_install_source == INSTALL_SOURCE_AUTO)
r = chase_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, &path, &d);
if (r == -ENOENT && arg_graceful() != ARG_GRACEFUL_NO) {
log_debug("Source directory does not exist, ignoring.");
@@ -578,7 +584,7 @@ static int install_entry_token(void) {
if (!arg_make_entry_directory && arg_entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID)
return 0;
p = path_join(arg_root, getenv("KERNEL_INSTALL_CONF_ROOT") ?: "/etc/kernel/", "entry-token");
p = path_join(arg_root, secure_getenv("KERNEL_INSTALL_CONF_ROOT") ?: "/etc/kernel/", "entry-token");
if (!p)
return log_oom();
@@ -900,10 +906,17 @@ static int install_variables(
return graceful ? 0 : r;
}
if (first || r == 0) {
r = efi_add_boot_option(slot, pick_efi_boot_option_description(),
part, pstart, psize,
uuid, path);
bool existing = r > 0;
if (first || !existing) {
r = efi_add_boot_option(
slot,
pick_efi_boot_option_description(),
part,
pstart,
psize,
uuid,
path);
if (r < 0) {
int level = graceful ? arg_quiet ? LOG_DEBUG : LOG_INFO : LOG_ERR;
const char *skip = graceful ? ", skipping" : "";
@@ -913,7 +926,9 @@ static int install_variables(
return graceful ? 0 : r;
}
log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
log_info("%s EFI boot entry \"%s\".",
existing ? "Updated" : "Created",
pick_efi_boot_option_description());
}
return insert_into_order(slot, first);
@@ -948,10 +963,67 @@ static int are_we_installed(const char *esp_path) {
return r == 0;
}
int verb_install(int argc, char *argv[], void *userdata) {
static int load_secure_boot_auto_enroll(
X509 **ret_certificate,
EVP_PKEY **ret_private_key) {
int r;
assert(ret_certificate);
assert(ret_private_key);
if (!arg_secure_boot_auto_enroll) {
*ret_certificate = NULL;
*ret_private_key = NULL;
return 0;
}
if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
if (r < 0)
return r;
}
_cleanup_(X509_freep) X509 *certificate = NULL;
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
r = openssl_load_x509_certificate(
arg_certificate_source_type,
arg_certificate_source,
arg_certificate,
&certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
if (r < 0)
return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
}
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
r = openssl_load_private_key(
arg_private_key_source_type,
arg_private_key_source,
arg_private_key,
&(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "bootctl-private-key-pin",
.keyring = arg_private_key,
.credential = "bootctl.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
},
&private_key,
/* ret_user_interface= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
*ret_certificate = TAKE_PTR(certificate);
*ret_private_key = TAKE_PTR(private_key);
return 0;
}
int verb_install(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
uint64_t pstart = 0, psize = 0;
uint32_t part = 0;
@@ -965,44 +1037,11 @@ int verb_install(int argc, char *argv[], void *userdata) {
/* Support graceful mode only for updates, unless forcibly enabled in chroot environments */
graceful = arg_graceful() == ARG_GRACEFUL_FORCE || (!install && arg_graceful() != ARG_GRACEFUL_NO);
if (arg_secure_boot_auto_enroll) {
if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
if (r < 0)
return r;
}
r = openssl_load_x509_certificate(
arg_certificate_source_type,
arg_certificate_source,
arg_certificate,
&certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
if (r < 0)
return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
}
r = openssl_load_private_key(
arg_private_key_source_type,
arg_private_key_source,
arg_private_key,
&(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "bootctl-private-key-pin",
.keyring = arg_private_key,
.credential = "bootctl.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
},
&private_key,
&ui);
if (r < 0)
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
}
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
_cleanup_(X509_freep) X509 *certificate = NULL;
r = load_secure_boot_auto_enroll(&certificate, &private_key);
if (r < 0)
return r;
r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL);
if (graceful && r == -ENOKEY)
@@ -1161,7 +1200,7 @@ static int rmdir_one(const char *prefix, const char *suffix) {
}
static int remove_subdirs(const char *root, const char *const *subdirs) {
int r, q;
int r;
/* We use recursion here to destroy the directories in reverse order. Which should be safe given how
* short the array is. */
@@ -1170,9 +1209,7 @@ static int remove_subdirs(const char *root, const char *const *subdirs) {
return 0;
r = remove_subdirs(root, subdirs + 1);
q = rmdir_one(root, subdirs[0]);
return r < 0 ? r : q;
return RET_GATHER(r, rmdir_one(root, subdirs[0]));
}
static int remove_entry_directory(const char *root) {
@@ -1186,19 +1223,14 @@ static int remove_entry_directory(const char *root) {
}
static int remove_binaries(const char *esp_path) {
int r, q;
int r;
_cleanup_free_ char *p = path_join(esp_path, "/EFI/systemd");
if (!p)
return log_oom();
r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
q = remove_boot_efi(esp_path);
if (q < 0 && r == 0)
r = q;
return r;
return RET_GATHER(r, remove_boot_efi(esp_path));
}
static int remove_file(const char *root, const char *file) {
@@ -1258,11 +1290,9 @@ static int remove_loader_variables(void) {
q = efi_set_variable(var, NULL, 0);
if (q == -ENOENT)
continue;
if (q < 0) {
log_warning_errno(q, "Failed to remove EFI variable %s: %m", var);
if (r >= 0)
r = q;
} else
if (q < 0)
RET_GATHER(r, log_warning_errno(q, "Failed to remove EFI variable %s: %m", var));
else
log_info("Removed EFI variable %s.", var);
}
@@ -1271,7 +1301,7 @@ static int remove_loader_variables(void) {
int verb_remove(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
int r, q;
int r;
r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid, NULL);
if (r < 0)
@@ -1286,59 +1316,28 @@ int verb_remove(int argc, char *argv[], void *userdata) {
return r;
r = remove_binaries(arg_esp_path);
q = remove_file(arg_esp_path, "/loader/loader.conf");
if (q < 0 && r >= 0)
r = q;
q = remove_file(arg_esp_path, "/loader/random-seed");
if (q < 0 && r >= 0)
r = q;
q = remove_file(arg_esp_path, "/loader/entries.srel");
if (q < 0 && r >= 0)
r = q;
RET_GATHER(r, remove_file(arg_esp_path, "/loader/loader.conf"));
RET_GATHER(r, remove_file(arg_esp_path, "/loader/random-seed"));
RET_GATHER(r, remove_file(arg_esp_path, "/loader/entries.srel"));
FOREACH_STRING(db, "PK.auth", "KEK.auth", "db.auth") {
_cleanup_free_ char *p = path_join("/loader/keys/auto", db);
if (!p)
return log_oom();
q = remove_file(arg_esp_path, p);
if (q < 0 && r >= 0)
r = q;
RET_GATHER(r, remove_file(arg_esp_path, p));
}
q = rmdir_one(arg_esp_path, "/loader/keys/auto");
if (q < 0 && r >= 0)
r = q;
q = remove_subdirs(arg_esp_path, esp_subdirs);
if (q < 0 && r >= 0)
r = q;
q = remove_subdirs(arg_esp_path, dollar_boot_subdirs);
if (q < 0 && r >= 0)
r = q;
q = remove_entry_directory(arg_esp_path);
if (q < 0 && r >= 0)
r = q;
RET_GATHER(r, rmdir_one(arg_esp_path, "/loader/keys/auto"));
RET_GATHER(r, remove_subdirs(arg_esp_path, esp_subdirs));
RET_GATHER(r, remove_subdirs(arg_esp_path, dollar_boot_subdirs));
RET_GATHER(r, remove_entry_directory(arg_esp_path));
if (arg_xbootldr_path) {
/* Remove a subset of these also from the XBOOTLDR partition if it exists */
q = remove_file(arg_xbootldr_path, "/loader/entries.srel");
if (q < 0 && r >= 0)
r = q;
q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs);
if (q < 0 && r >= 0)
r = q;
q = remove_entry_directory(arg_xbootldr_path);
if (q < 0 && r >= 0)
r = q;
RET_GATHER(r, remove_file(arg_xbootldr_path, "/loader/entries.srel"));
RET_GATHER(r, remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs));
RET_GATHER(r, remove_entry_directory(arg_xbootldr_path));
}
(void) sync_everything();
@@ -1352,15 +1351,8 @@ int verb_remove(int argc, char *argv[], void *userdata) {
}
char *path = strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
q = remove_variables(uuid, path, true);
if (q < 0 && r >= 0)
r = q;
q = remove_loader_variables();
if (q < 0 && r >= 0)
r = q;
return r;
RET_GATHER(r, remove_variables(uuid, path, /* in_order= */ true));
return RET_GATHER(r, remove_loader_variables());
}
int verb_is_installed(int argc, char *argv[], void *userdata) {
+1 -1
View File
@@ -61,7 +61,7 @@ static int set_system_token(void) {
if (!touch_variables())
return 0;
r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
r = secure_getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
if (r < 0) {
if (r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
+1 -1
View File
@@ -124,7 +124,7 @@ int settle_entry_token(void) {
r = boot_entry_token_ensure(
arg_root,
getenv("KERNEL_INSTALL_CONF_ROOT"),
secure_getenv("KERNEL_INSTALL_CONF_ROOT"),
arg_machine_id,
/* machine_id_is_random = */ false,
&arg_entry_token_type,
+39 -32
View File
@@ -67,7 +67,7 @@ sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
bool arg_arch_all = false;
char *arg_root = NULL;
char *arg_image = NULL;
InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO;
InstallSource arg_install_source = INSTALL_SOURCE_AUTO;
char *arg_efi_boot_option_description = NULL;
bool arg_dry_run = false;
ImagePolicy *arg_image_policy = NULL;
@@ -483,11 +483,11 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_INSTALL_SOURCE:
if (streq(optarg, "auto"))
arg_install_source = ARG_INSTALL_SOURCE_AUTO;
arg_install_source = INSTALL_SOURCE_AUTO;
else if (streq(optarg, "image"))
arg_install_source = ARG_INSTALL_SOURCE_IMAGE;
arg_install_source = INSTALL_SOURCE_IMAGE;
else if (streq(optarg, "host"))
arg_install_source = ARG_INSTALL_SOURCE_HOST;
arg_install_source = INSTALL_SOURCE_HOST;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unexpected parameter for --install-source=: %s", optarg);
@@ -648,7 +648,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_root && arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
@@ -697,6 +697,38 @@ static int bootctl_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
int r;
/* Invocation as Varlink service */
r = varlink_server_new(
&varlink_server,
SD_VARLINK_SERVER_ROOT_ONLY,
/* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method_many(
varlink_server,
"io.systemd.BootControl.ListBootEntries", vl_method_list_boot_entries,
"io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
"io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");
r = sd_varlink_server_loop_auto(varlink_server);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return 0;
}
static int run(int argc, char *argv[]) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
@@ -708,33 +740,8 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
if (arg_varlink) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
/* Invocation as Varlink service */
r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method_many(
varlink_server,
"io.systemd.BootControl.ListBootEntries", vl_method_list_boot_entries,
"io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
"io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");
r = sd_varlink_server_loop_auto(varlink_server);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return EXIT_SUCCESS;
}
if (arg_varlink)
return vl_server();
if (arg_print_root_device > 0) {
_cleanup_free_ char *path = NULL;
+5 -3
View File
@@ -4,9 +4,11 @@
#include "forward.h"
typedef enum InstallSource {
ARG_INSTALL_SOURCE_IMAGE,
ARG_INSTALL_SOURCE_HOST,
ARG_INSTALL_SOURCE_AUTO,
INSTALL_SOURCE_IMAGE,
INSTALL_SOURCE_HOST,
INSTALL_SOURCE_AUTO,
_INSTALL_SOURCE_MAX,
_INSTALL_SOURCE_INVALID = -EINVAL,
} InstallSource;
typedef enum GracefulMode {
+2
View File
@@ -11,6 +11,8 @@ typedef enum BootEntryTokenType {
BOOT_ENTRY_TOKEN_OS_ID,
BOOT_ENTRY_TOKEN_LITERAL,
BOOT_ENTRY_TOKEN_AUTO,
_BOOT_ENTRY_TOKEN_TYPE_MAX,
_BOOT_ENTRY_TOKEN_TYPE_INVALID = -EINVAL,
} BootEntryTokenType;
bool boot_entry_token_valid(const char *p);