@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -291,4 +291,4 @@ static const char *const boot_entry_token_type_table[] = {
|
||||
[BOOT_ENTRY_TOKEN_AUTO] = "auto",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType);
|
||||
DEFINE_STRING_TABLE_LOOKUP(boot_entry_token_type, BootEntryTokenType);
|
||||
|
||||
@@ -34,4 +34,4 @@ int boot_entry_token_ensure_at(
|
||||
|
||||
int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType);
|
||||
DECLARE_STRING_TABLE_LOOKUP(boot_entry_token_type, BootEntryTokenType);
|
||||
|
||||
@@ -36,21 +36,37 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(source, BootEntrySource, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The string identifier of the entry"),
|
||||
SD_VARLINK_DEFINE_FIELD(id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Path to the primary definition file for the entry"),
|
||||
SD_VARLINK_DEFINE_FIELD(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Directory path of the file system root the entry was found on"),
|
||||
SD_VARLINK_DEFINE_FIELD(root, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The entry's title string"),
|
||||
SD_VARLINK_DEFINE_FIELD(title, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The possibly mangled/augmented title to show for the entry"),
|
||||
SD_VARLINK_DEFINE_FIELD(showTitle, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("An explicitly configured sorting key for the enry"),
|
||||
SD_VARLINK_DEFINE_FIELD(sortKey, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The version of the entry"),
|
||||
SD_VARLINK_DEFINE_FIELD(version, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Machine ID of the OS installation belonging to the entry, if known"),
|
||||
SD_VARLINK_DEFINE_FIELD(machineId, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("EFI architecture name for this entry"),
|
||||
SD_VARLINK_DEFINE_FIELD(architecture, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Command line options to pass to the invoked kernel or EFI binary"),
|
||||
SD_VARLINK_DEFINE_FIELD(options, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Path to the Linux kernel to invoke, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(linux, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Path to the EFI binary to invoke, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(efi, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Path to an UKI EFI binary to invoke, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(uki, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("An UKI profile index to invoke. If not specified defaults to the first profile."),
|
||||
SD_VARLINK_DEFINE_FIELD(profile, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Path to the initrd image to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(initrd, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("Devicetree file to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(devicetree, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Devicetree overlay file to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
|
||||
SD_VARLINK_DEFINE_FIELD(devicetreeOverlay, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates whether the boot loader reported this entry on the current boot"),
|
||||
SD_VARLINK_DEFINE_FIELD(isReported, SD_VARLINK_BOOL, 0),
|
||||
@@ -83,12 +99,50 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
SD_VARLINK_FIELD_COMMENT("The current state of the reboot-to-firmware-UI flag"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(state, SD_VARLINK_BOOL, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
Operation,
|
||||
SD_VARLINK_FIELD_COMMENT("Install the boot loader afresh, creating everything it needs"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(new),
|
||||
SD_VARLINK_FIELD_COMMENT("Just update existing boot loader binaries"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(update));
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
BootEntryTokenType,
|
||||
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on /etc/machine-id"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(machine_id),
|
||||
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on the IMAGE_ID= field from /etc/os-release"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(os_image_id),
|
||||
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on the ID= field from /etc/os-release"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(os_id),
|
||||
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on a manually chosen string"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(literal),
|
||||
SD_VARLINK_FIELD_COMMENT("Choose automatically how to pick identifiers for type #1 boot entries"),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(auto));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Install,
|
||||
SD_VARLINK_FIELD_COMMENT("Operation, either 'new' or 'update'"),
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(operation, Operation, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("If true, continue on various failures"),
|
||||
SD_VARLINK_DEFINE_INPUT(graceful, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Index into array of file descriptors passed along with this message, pointing to file descriptor to root file system to operate on"),
|
||||
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
|
||||
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("If true the boot loader will be registered in an EFI boot entry via EFI variables, otherwise this is omitted"),
|
||||
SD_VARLINK_DEFINE_INPUT(touchVariables, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
RebootToFirmwareNotSupported);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
NoSuchBootEntry);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
NoESPFound);
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_BootControl,
|
||||
"io.systemd.BootControl",
|
||||
@@ -101,13 +155,21 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
&vl_type_BootEntryAddon,
|
||||
SD_VARLINK_SYMBOL_COMMENT("A structure encapsulating a boot entry"),
|
||||
&vl_type_BootEntry,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The operation to execute"),
|
||||
&vl_type_Operation,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Enumerates boot entries. Method call must be called with 'more' flag set. Each response returns one entry. If no entries are defined returns the NoSuchBootEntry error."),
|
||||
&vl_method_ListBootEntries,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Sets the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."),
|
||||
&vl_method_SetRebootToFirmware,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Gets the current state of the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."),
|
||||
&vl_method_GetRebootToFirmware,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The boot entry token type to use."),
|
||||
&vl_type_BootEntryTokenType,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Install the boot loader on the ESP."),
|
||||
&vl_method_Install,
|
||||
SD_VARLINK_SYMBOL_COMMENT("SetRebootToFirmware() and GetRebootToFirmware() return this if the firmware does not actually support the reboot-to-firmware-UI concept."),
|
||||
&vl_error_RebootToFirmwareNotSupported,
|
||||
SD_VARLINK_SYMBOL_COMMENT("No boot entry defined."),
|
||||
&vl_error_NoSuchBootEntry);
|
||||
&vl_error_NoSuchBootEntry,
|
||||
SD_VARLINK_SYMBOL_COMMENT("No EFI System Partition (ESP) found."),
|
||||
&vl_error_NoESPFound);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Boot Entries Service Socket
|
||||
Description=Boot Loader Control Service Socket
|
||||
Documentation=man:bootctl(1)
|
||||
DefaultDependencies=no
|
||||
After=local-fs.target
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Boot Entries Service
|
||||
Description=Boot Loader Control Service
|
||||
Documentation=man:bootctl(1)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
|
||||
Reference in New Issue
Block a user