bootctl: add Install() varlink API

Fixes: #11221
This commit is contained in:
Lennart Poettering
2026-02-06 23:15:49 +01:00
parent 38433a6d06
commit aea76373b2
8 changed files with 198 additions and 7 deletions
+124
View File
@@ -3,6 +3,8 @@
#include <stdlib.h>
#include <unistd.h>
#include "sd-varlink.h"
#include "alloc-util.h"
#include "boot-entry.h"
#include "bootctl.h"
@@ -18,11 +20,13 @@
#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "find-esp.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "id128-util.h"
#include "install-file.h"
#include "io-util.h"
#include "json-util.h"
#include "kernel-config.h"
#include "log.h"
#include "openssl-util.h"
@@ -89,6 +93,15 @@ typedef struct InstallContext {
.touch_variables = -1, \
}
static const char* install_operation_table[_INSTALL_OPERATION_MAX] = {
[INSTALL_NEW] = "new",
[INSTALL_UPDATE] = "update",
[INSTALL_REMOVE] = "remove",
[INSTALL_TEST] = "test",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_operation, InstallOperation);
static void install_context_done(InstallContext *c) {
assert(c);
@@ -1912,3 +1925,114 @@ int verb_is_installed(int argc, char *argv[], void *userdata) {
return EXIT_FAILURE;
}
}
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_install_operation, InstallOperation, install_operation_from_string);
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryTokenType, boot_entry_token_type_from_string);
typedef struct InstallParameters {
InstallContext context;
unsigned root_fd_index;
} InstallParameters;
static void install_parameters_done(InstallParameters *p) {
assert(p);
install_context_done(&p->context);
}
int vl_method_install(
sd_varlink *link,
sd_json_variant *parameters,
sd_varlink_method_flags_t flags,
void *userdata) {
int r;
assert(link);
_cleanup_(install_parameters_done) InstallParameters p = {
.context = INSTALL_CONTEXT_NULL,
.root_fd_index = UINT_MAX,
};
static const sd_json_dispatch_field dispatch_table[] = {
{ "operation", SD_JSON_VARIANT_STRING, json_dispatch_install_operation, voffsetof(p, context.operation), SD_JSON_MANDATORY },
{ "graceful", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, context.graceful), 0 },
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
{ "touchVariables", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, context.touch_variables), 0 },
{},
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
if (!IN_SET(p.context.operation, INSTALL_NEW, INSTALL_UPDATE))
return sd_varlink_error_invalid_parameter_name(link, "operation");
if (p.root_fd_index != UINT_MAX) {
p.context.root_fd = sd_varlink_peek_dup_fd(link, p.root_fd_index);
if (p.context.root_fd < 0)
return log_debug_errno(p.context.root_fd, "Failed to acquire root fd from Varlink: %m");
r = fd_verify_directory(p.context.root_fd);
if (r < 0)
return log_debug_errno(r, "Specified file descriptor does not refer to a directory: %m");
if (!p.context.root) {
r = fd_get_path(p.context.root_fd, &p.context.root);
if (r < 0)
return log_debug_errno(r, "Failed to get path of file descriptor: %m");
if (empty_or_root(p.context.root))
p.context.root = mfree(p.context.root);
}
}
if (p.context.root_fd < 0 && p.context.root) {
p.context.root_fd = open(p.context.root, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (p.context.root_fd < 0)
return log_debug_errno(errno, "Failed to open '%s': %m", p.context.root);
}
if (p.context.root_fd < 0)
p.context.root_fd = XAT_FDROOT;
if (p.context.entry_token_type < 0)
p.context.entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
r = find_esp_and_warn_at(
p.context.root_fd,
/* path= */ NULL,
/* unprivileged_mode= */ false,
&p.context.esp_path,
&p.context.esp_part,
&p.context.esp_pstart,
&p.context.esp_psize,
&p.context.esp_uuid,
/* ret_devid= */ NULL);
if (r == -ENOKEY)
return sd_varlink_error(link, "io.systemd.BootControl.NoESPFound", NULL);
if (r < 0)
return r;
r = find_xbootldr_and_warn_at(
p.context.root_fd,
/* path= */ NULL,
/* unprivileged_mode= */ false,
&p.context.xbootldr_path,
/* ret_uuid= */ NULL,
/* ret_devid= */ NULL);
if (r == -ENOKEY)
log_debug_errno(r, "Didn't find an XBOOTLDR partition, using ESP as $BOOT.");
else if (r < 0)
return r;
r = run_install(&p.context);
if (r < 0)
return r;
return sd_varlink_reply(link, NULL);
}
+4
View File
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
int verb_install(int argc, char *argv[], void *userdata);
int verb_remove(int argc, char *argv[], void *userdata);
int verb_is_installed(int argc, char *argv[], void *userdata);
int vl_method_install(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
+3 -2
View File
@@ -711,7 +711,7 @@ static int vl_server(void) {
r = varlink_server_new(
&varlink_server,
SD_VARLINK_SERVER_ROOT_ONLY,
SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
/* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
@@ -724,7 +724,8 @@ static int vl_server(void) {
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);
"io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware,
"io.systemd.BootControl.Install", vl_method_install);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");