Merge pull request #31286 from poettering/bootctl-varlink

bootctl: add simple varlink IPC interface
This commit is contained in:
Luca Boccassi
2024-02-14 18:59:33 +00:00
committed by GitHub
17 changed files with 351 additions and 43 deletions
+59 -38
View File
@@ -1668,6 +1668,63 @@ int show_boot_entry(
return -status;
}
int boot_entry_to_json(const BootConfig *c, size_t i, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
const BootEntry *e;
int r;
assert(c);
assert(ret);
if (i >= c->n_entries) {
*ret = NULL;
return 0;
}
e = c->entries + i;
r = json_variant_merge_objectb(
&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("type", JSON_BUILD_STRING(boot_entry_type_json_to_string(e->type))),
JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
JSON_BUILD_PAIR_CONDITION(e->root, "root", JSON_BUILD_STRING(e->root)),
JSON_BUILD_PAIR_CONDITION(e->title, "title", JSON_BUILD_STRING(e->title)),
JSON_BUILD_PAIR_CONDITION(boot_entry_title(e), "showTitle", JSON_BUILD_STRING(boot_entry_title(e))),
JSON_BUILD_PAIR_CONDITION(e->sort_key, "sortKey", JSON_BUILD_STRING(e->sort_key)),
JSON_BUILD_PAIR_CONDITION(e->version, "version", JSON_BUILD_STRING(e->version)),
JSON_BUILD_PAIR_CONDITION(e->machine_id, "machineId", JSON_BUILD_STRING(e->machine_id)),
JSON_BUILD_PAIR_CONDITION(e->architecture, "architecture", JSON_BUILD_STRING(e->architecture)),
JSON_BUILD_PAIR_CONDITION(e->kernel, "linux", JSON_BUILD_STRING(e->kernel)),
JSON_BUILD_PAIR_CONDITION(e->efi, "efi", JSON_BUILD_STRING(e->efi)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", JSON_BUILD_STRV(e->initrd)),
JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay))));
if (r < 0)
return log_oom();
/* Sanitizers (only memory sanitizer?) do not like function call with too many
* arguments and trigger false positive warnings. Let's not add too many json objects
* at once. */
r = json_variant_merge_objectb(
&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
JSON_BUILD_PAIR_CONDITION(c->default_entry >= 0, "isDefault", JSON_BUILD_BOOLEAN(i == (size_t) c->default_entry)),
JSON_BUILD_PAIR_CONDITION(c->selected_entry >= 0, "isSelected", JSON_BUILD_BOOLEAN(i == (size_t) c->selected_entry))));
if (r < 0)
return log_oom();
r = json_cmdline(e, &c->global_addons, &v);
if (r < 0)
return log_oom();
*ret = TAKE_PTR(v);
return 1;
}
int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
int r;
@@ -1677,44 +1734,9 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
for (size_t i = 0; i < config->n_entries; i++) {
const BootEntry *e = config->entries + i;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = json_variant_merge_objectb(
&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("type", JSON_BUILD_STRING(boot_entry_type_json_to_string(e->type))),
JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
JSON_BUILD_PAIR_CONDITION(e->root, "root", JSON_BUILD_STRING(e->root)),
JSON_BUILD_PAIR_CONDITION(e->title, "title", JSON_BUILD_STRING(e->title)),
JSON_BUILD_PAIR_CONDITION(boot_entry_title(e), "showTitle", JSON_BUILD_STRING(boot_entry_title(e))),
JSON_BUILD_PAIR_CONDITION(e->sort_key, "sortKey", JSON_BUILD_STRING(e->sort_key)),
JSON_BUILD_PAIR_CONDITION(e->version, "version", JSON_BUILD_STRING(e->version)),
JSON_BUILD_PAIR_CONDITION(e->machine_id, "machineId", JSON_BUILD_STRING(e->machine_id)),
JSON_BUILD_PAIR_CONDITION(e->architecture, "architecture", JSON_BUILD_STRING(e->architecture)),
JSON_BUILD_PAIR_CONDITION(e->kernel, "linux", JSON_BUILD_STRING(e->kernel)),
JSON_BUILD_PAIR_CONDITION(e->efi, "efi", JSON_BUILD_STRING(e->efi)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", JSON_BUILD_STRV(e->initrd)),
JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay))));
if (r < 0)
return log_oom();
r = json_cmdline(e, &config->global_addons, &v);
if (r < 0)
return log_oom();
/* Sanitizers (only memory sanitizer?) do not like function call with too many
* arguments and trigger false positive warnings. Let's not add too many json objects
* at once. */
r = json_variant_merge_objectb(
&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
JSON_BUILD_PAIR_CONDITION(config->default_entry >= 0, "isDefault", JSON_BUILD_BOOLEAN(i == (size_t) config->default_entry)),
JSON_BUILD_PAIR_CONDITION(config->selected_entry >= 0, "isSelected", JSON_BUILD_BOOLEAN(i == (size_t) config->selected_entry))));
r = boot_entry_to_json(config, i, &v);
if (r < 0)
return log_oom();
@@ -1723,8 +1745,7 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
return log_oom();
}
json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
return json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
} else {
for (size_t n = 0; n < config->n_entries; n++) {
r = show_boot_entry(
+2
View File
@@ -143,3 +143,5 @@ int show_boot_entries(
JsonFormatFlags json_format);
int boot_filename_extract_tries(const char *fname, char **ret_stripped, unsigned *ret_tries_left, unsigned *ret_tries_done);
int boot_entry_to_json(const BootConfig *c, size_t i, JsonVariant **ret);
+9 -2
View File
@@ -102,7 +102,8 @@ int efi_loader_get_entries(char ***ret) {
if (r < 0)
return r;
/* The variable contains a series of individually NUL terminated UTF-16 strings. */
/* The variable contains a series of individually NUL terminated UTF-16 strings. We gracefully
* consider the final NUL byte optional (i.e. the last string may or may not end in a NUL byte).*/
for (size_t i = 0, start = 0;; i++) {
_cleanup_free_ char *decoded = NULL;
@@ -116,6 +117,11 @@ int efi_loader_get_entries(char ***ret) {
if (!end && entries[i] != 0)
continue;
/* Empty string at the end of variable? That's the trailer, we are done (i.e. we have a final
* NUL terminator). */
if (end && start == i)
break;
/* We reached the end of a string, let's decode it into UTF-8 */
decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
if (!decoded)
@@ -128,7 +134,8 @@ int efi_loader_get_entries(char ***ret) {
} else
log_debug("Ignoring invalid loader entry '%s'.", decoded);
/* We reached the end of the variable */
/* Exit the loop if we reached the end of the variable (i.e. we do not have a final NUL
* terminator) */
if (end)
break;
+1
View File
@@ -174,6 +174,7 @@ shared_sources = files(
'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',
@@ -0,0 +1,59 @@
/* 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);
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "varlink-idl.h"
extern const VarlinkInterface vl_interface_io_systemd_BootControl;