#include "bootctl-reboot-to-firmware.h"
#include "efi-api.h"
+#include "errno-util.h"
#include "parse-util.h"
int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) {
puts("supported");
return 1; /* recognizable error #1 */
}
- if (r == -EOPNOTSUPP) {
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) {
puts("not supported");
return 2; /* recognizable error #2 */
}
return 0;
}
}
+
+int vl_method_set_reboot_to_firmware(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch dispatch_table[] = {
+ { "state", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, 0, 0 },
+ {}
+ };
+ bool b;
+ int r;
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &b);
+ if (r != 0)
+ return r;
+
+ r = efi_set_reboot_to_firmware(b);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ return varlink_error(link, "io.systemd.BootControl.RebootToFirmwareNotSupported", NULL);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, NULL);
+}
+
+int vl_method_get_reboot_to_firmware(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ int r;
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ r = efi_get_reboot_to_firmware();
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ return varlink_error(link, "io.systemd.BootControl.RebootToFirmwareNotSupported", NULL);
+ if (r < 0)
+ return r;
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_BOOLEAN("state", r)));
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "varlink.h"
+
int verb_reboot_to_firmware(int argc, char *argv[], void *userdata);
+
+int vl_method_set_reboot_to_firmware(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
+int vl_method_get_reboot_to_firmware(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
dev_t esp_devid = 0, xbootldr_devid = 0;
int r, k;
- r = acquire_esp(/* unprivileged_mode= */ -1, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid);
+ r = acquire_esp(/* unprivileged_mode= */ -1,
+ /* graceful= */ false,
+ /* ret_part= */ NULL,
+ /* ret_pstart= */ NULL,
+ /* ret_psize= */ NULL,
+ &esp_uuid,
+ &esp_devid);
if (arg_print_esp_path) {
if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
* error the find_esp_and_warn() won't log on its own) */
return 0;
}
- r = acquire_xbootldr(/* unprivileged_mode= */ -1, &xbootldr_uuid, &xbootldr_devid);
+ r = acquire_xbootldr(
+ /* unprivileged_mode= */ -1,
+ &xbootldr_uuid,
+ &xbootldr_devid);
if (arg_print_dollar_boot_path) {
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
int verb_unlink(int argc, char *argv[], void *userdata) {
return verb_list(argc, argv, userdata);
}
+
+int vl_method_list_boot_entries(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
+ dev_t esp_devid = 0, xbootldr_devid = 0;
+ int r;
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ r = acquire_esp(/* unprivileged_mode= */ false,
+ /* graceful= */ false,
+ /* ret_part= */ NULL,
+ /* ret_pstart= */ NULL,
+ /* ret_psize= */ NULL,
+ /* ret_uuid=*/ NULL,
+ &esp_devid);
+ if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
+ return log_error_errno(r, "Failed to determine ESP location: %m");
+ if (r < 0)
+ return r;
+
+ r = acquire_xbootldr(
+ /* unprivileged_mode= */ false,
+ /* ret_uuid= */ NULL,
+ &xbootldr_devid);
+ if (r == -EACCES)
+ return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
+ if (r < 0)
+ return r;
+
+ r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *previous = NULL;
+ for (size_t i = 0; i < config.n_entries; i++) {
+ if (previous) {
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_VARIANT("entry", previous)));
+ if (r < 0)
+ return r;
+
+ previous = json_variant_unref(previous);
+ }
+
+ r = boot_entry_to_json(&config, i, &previous);
+ if (r < 0)
+ return r;
+ }
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(previous, "entry", JSON_BUILD_VARIANT(previous))));
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "varlink.h"
+
int verb_status(int argc, char *argv[], void *userdata);
int verb_list(int argc, char *argv[], void *userdata);
int verb_unlink(int argc, char *argv[], void *userdata);
+
+int vl_method_list_boot_entries(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
#include "parse-argument.h"
#include "pretty-print.h"
#include "utf8.h"
+#include "varlink.h"
+#include "varlink-io.systemd.BootControl.h"
#include "verbs.h"
#include "virt.h"
char *arg_efi_boot_option_description = NULL;
bool arg_dry_run = false;
ImagePolicy *arg_image_policy = NULL;
+bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
+ r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ if (r > 0) {
+ arg_varlink = true;
+ arg_pager_flags |= PAGER_DISABLE;
+ }
+
return 1;
}
if (r <= 0)
return r;
+ if (arg_varlink) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+ /* Invocation as Varlink service */
+
+ r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = 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 = 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 = 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_print_root_device > 0) {
_cleanup_free_ char *path = NULL;
dev_t devno;
extern char *arg_efi_boot_option_description;
extern bool arg_dry_run;
extern ImagePolicy *arg_image_policy;
+extern bool arg_varlink;
static inline const char *arg_dollar_boot_path(void) {
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
'varlink.c',
'varlink-idl.c',
'varlink-io.systemd.c',
+ 'varlink-io.systemd.BootControl.c',
'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Journal.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.BootControl.h"
+
+static VARLINK_DEFINE_ENUM_TYPE(
+ BootEntryType,
+ VARLINK_DEFINE_ENUM_VALUE(type1),
+ VARLINK_DEFINE_ENUM_VALUE(type2),
+ VARLINK_DEFINE_ENUM_VALUE(loader),
+ VARLINK_DEFINE_ENUM_VALUE(auto));
+
+static VARLINK_DEFINE_STRUCT_TYPE(
+ BootEntry,
+ VARLINK_DEFINE_FIELD_BY_TYPE(type, BootEntryType, 0),
+ VARLINK_DEFINE_FIELD(id, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(path, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(root, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(title, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(showTitle, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(sortKey, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(version, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(machineId, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(architecture, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(options, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(linux, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(efi, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(initrd, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY),
+ VARLINK_DEFINE_FIELD(devicetree, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(devicetreeOverlay, VARLINK_STRING, VARLINK_NULLABLE|VARLINK_ARRAY),
+ VARLINK_DEFINE_FIELD(isReported, VARLINK_BOOL, 0),
+ VARLINK_DEFINE_FIELD(triesLeft, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(triesDone, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(isDefault, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(isSelected, VARLINK_BOOL, VARLINK_NULLABLE));
+
+static VARLINK_DEFINE_METHOD(
+ ListBootEntries,
+ VARLINK_DEFINE_OUTPUT_BY_TYPE(entry, BootEntry, VARLINK_NULLABLE));
+
+static VARLINK_DEFINE_METHOD(
+ SetRebootToFirmware,
+ VARLINK_DEFINE_INPUT(state, VARLINK_BOOL, 0));
+
+static VARLINK_DEFINE_METHOD(
+ GetRebootToFirmware,
+ VARLINK_DEFINE_OUTPUT(state, VARLINK_BOOL, 0));
+
+static VARLINK_DEFINE_ERROR(
+ RebootToFirmwareNotSupported);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_BootControl,
+ "io.systemd.BootControl",
+ &vl_type_BootEntryType,
+ &vl_type_BootEntry,
+ &vl_method_ListBootEntries,
+ &vl_method_SetRebootToFirmware,
+ &vl_method_GetRebootToFirmware,
+ &vl_error_RebootToFirmwareNotSupported);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_BootControl;
#include "varlink.h"
#include "varlink-idl.h"
#include "varlink-io.systemd.h"
+#include "varlink-io.systemd.BootControl.h"
#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
print_separator();
test_parse_format_one(&vl_interface_io_systemd_Credentials);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_BootControl);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
}
+testcase_bootctl_varlink() {
+ varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}'
+
+ # We have no UEFI in the test environment, hence just check that this fails cleanly
+ ( SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' 2>&1 || true ) | grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+ ( SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' 2>&1 || true ) | grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+ ( SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' 2>&1 || true ) | grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+}
+
run_testcases
'file' : 'systemd-boot-update.service',
'conditions' : ['ENABLE_BOOTLOADER'],
},
+ {
+ 'file' : 'systemd-bootctl@.service.in',
+ 'conditions' : ['ENABLE_BOOTLOADER'],
+ },
+ {
+ 'file' : 'systemd-bootctl.socket',
+ 'conditions' : ['ENABLE_BOOTLOADER'],
+ 'symlinks' : ['sockets.target.wants/'],
+ },
{
'file' : 'systemd-confext.service',
'conditions' : ['ENABLE_SYSEXT'],
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Boot Control (Varlink)
+Documentation=man:bootctl(1)
+DefaultDependencies=no
+After=local-fs.target
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.BootControl
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Boot Control (Varlink)
+Documentation=man:bootctl(1)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=local-fs.target
+Before=shutdown.target
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart={{BINDIR}}/bootctl