From 1c7642a3b7296845588bf82485bdf063136726fc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 11 May 2024 19:55:15 +0200 Subject: [PATCH] machined: add simple varlink API for listing machines --- src/machine/machined-varlink.c | 84 ++++++++++++++++++++++++- src/shared/varlink-io.systemd.Machine.c | 39 ++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index dc35877c49..38acfe97cd 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "format-util.h" +#include "hostname-util.h" #include "json-util.h" #include "machine-varlink.h" #include "machined-varlink.h" #include "mkdir.h" +#include "socket-util.h" #include "user-util.h" #include "varlink.h" #include "varlink-io.systemd.Machine.h" @@ -383,6 +385,83 @@ static int vl_method_get_memberships(Varlink *link, sd_json_variant *parameters, return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); } +static int list_machine_one(Varlink *link, Machine *m, bool more) { + int r; + + assert(link); + assert(m); + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + + r = sd_json_buildo( + &v, + SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(m->name)), + SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->id), "id", SD_JSON_BUILD_ID128(m->id)), + SD_JSON_BUILD_PAIR("class", SD_JSON_BUILD_STRING(machine_class_to_string(m->class))), + SD_JSON_BUILD_PAIR_CONDITION(!!m->service, "service", SD_JSON_BUILD_STRING(m->service)), + SD_JSON_BUILD_PAIR_CONDITION(!!m->root_directory, "rootDirectory", SD_JSON_BUILD_STRING(m->root_directory)), + SD_JSON_BUILD_PAIR_CONDITION(!!m->unit, "unit", SD_JSON_BUILD_STRING(m->unit)), + SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", SD_JSON_BUILD_UNSIGNED(m->leader.pid)), + SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)), + SD_JSON_BUILD_PAIR_CONDITION(m->vsock_cid != VMADDR_CID_ANY, "vSockCid", SD_JSON_BUILD_UNSIGNED(m->vsock_cid)), + SD_JSON_BUILD_PAIR_CONDITION(!!m->ssh_address, "sshAddress", SD_JSON_BUILD_STRING(m->ssh_address))); + if (r < 0) + return r; + + if (more) + return varlink_notify(link, v); + + return varlink_reply(link, v); +} + +static int vl_method_list(Varlink *link, sd_json_variant *parameters, VarlinkMethodFlags flags, void *userdata) { + Manager *m = ASSERT_PTR(userdata); + const char *mn = NULL; + + const sd_json_dispatch_field dispatch_table[] = { + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&mn), 0 }, + {} + }; + + int r; + + assert(parameters); + + r = varlink_dispatch(link, parameters, dispatch_table, 0); + if (r != 0) + return r; + + if (mn) { + if (!hostname_is_valid(mn, /* flags= */ VALID_HOSTNAME_DOT_HOST)) + return varlink_error_invalid_parameter_name(link, "name"); + + Machine *machine = hashmap_get(m->machines, mn); + if (!machine) + return varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); + + return list_machine_one(link, machine, /* more= */ false); + } + + if (!FLAGS_SET(flags, VARLINK_METHOD_MORE)) + return varlink_error(link, VARLINK_ERROR_EXPECTED_MORE, NULL); + + Machine *previous = NULL, *i; + HASHMAP_FOREACH(i, m->machines) { + if (previous) { + r = list_machine_one(link, previous, /* more= */ true); + if (r < 0) + return r; + } + + previous = i; + } + + if (previous) + return list_machine_one(link, previous, /* more= */ false); + + return varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); +} + static int manager_varlink_init_userdb(Manager *m) { _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; int r; @@ -443,7 +522,10 @@ static int manager_varlink_init_machine(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m"); - r = varlink_server_bind_method(s, "io.systemd.Machine.Register", vl_method_register); + r = varlink_server_bind_method_many( + s, + "io.systemd.Machine.Register", vl_method_register, + "io.systemd.Machine.List", vl_method_list); if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 2d25a345d7..b5f8f5c075 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -16,10 +16,49 @@ static VARLINK_DEFINE_METHOD( VARLINK_DEFINE_INPUT(sshAddress, VARLINK_STRING, VARLINK_NULLABLE), VARLINK_DEFINE_INPUT(sshPrivateKeyPath, VARLINK_STRING, VARLINK_NULLABLE)); +static VARLINK_DEFINE_STRUCT_TYPE( + Timestamp, + VARLINK_FIELD_COMMENT("Timestamp in µs in the CLOCK_REALTIME clock (wallclock)"), + VARLINK_DEFINE_FIELD(realtime, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("Timestamp in µs in the CLOCK_MONOTONIC clock"), + VARLINK_DEFINE_FIELD(monotonic, VARLINK_INT, VARLINK_NULLABLE)); + +static VARLINK_DEFINE_METHOD( + List, + VARLINK_FIELD_COMMENT("If non-null the name of a running machine to report details on. If null/unspecified enumerates all running machines."), + VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("Name of the machine"), + VARLINK_DEFINE_OUTPUT(name, VARLINK_STRING, 0), + VARLINK_FIELD_COMMENT("128bit ID identifying this machine, formatted in hexadecimal"), + VARLINK_DEFINE_OUTPUT(id, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("Name of the software that registered this machine"), + VARLINK_DEFINE_OUTPUT(service, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("The class of this machine"), + VARLINK_DEFINE_OUTPUT(class, VARLINK_STRING, 0), + VARLINK_FIELD_COMMENT("Leader process PID of this machine"), + VARLINK_DEFINE_OUTPUT(leader, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("Root directory of this machine, if known, relative to host file system"), + VARLINK_DEFINE_OUTPUT(rootDirectory, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("The service manager unit this machine resides in"), + VARLINK_DEFINE_OUTPUT(unit, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("Timestamp when the machine was activated"), + VARLINK_DEFINE_OUTPUT_BY_TYPE(timestamp, Timestamp, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("AF_VSOCK CID of the machine if known and applicable"), + VARLINK_DEFINE_OUTPUT(vSockCid, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_FIELD_COMMENT("SSH address to connect to"), + VARLINK_DEFINE_OUTPUT(sshAddress, VARLINK_STRING, VARLINK_NULLABLE)); + +static VARLINK_DEFINE_ERROR(NoSuchMachine); static VARLINK_DEFINE_ERROR(MachineExists); VARLINK_DEFINE_INTERFACE( io_systemd_Machine, "io.systemd.Machine", + VARLINK_SYMBOL_COMMENT("A timestamp object consisting of both CLOCK_REALTIME and CLOCK_MONOTONIC timestamps"), + &vl_type_Timestamp, &vl_method_Register, + VARLINK_SYMBOL_COMMENT("List running machines"), + &vl_method_List, + VARLINK_SYMBOL_COMMENT("No matching machine currently running"), + &vl_error_NoSuchMachine, &vl_error_MachineExists);