#include <stdlib.h>
#include <unistd.h>
+#include "sd-varlink.h"
+
#include "alloc-util.h"
#include "boot-entry.h"
#include "bootctl.h"
#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"
.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);
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);
+}
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),
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",
&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);