Skip to content

Commit

Permalink
machined: add varlink interface for registering machines
Browse files Browse the repository at this point in the history
This commit adds the new varlink interface io.systemd.Machine at
/run/systemd/machine/io.systemd.Machine with a single method Register

It supports all combinations of RegisterMachine[WithSSH,WithNetwork] all
under the same method.
  • Loading branch information
sam-leonard-ct authored and bluca committed May 8, 2024
1 parent 1f815bf commit 5b44c81
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 8 deletions.
6 changes: 4 additions & 2 deletions man/systemd-machined.service.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@

<para>The daemon provides both a C library interface
(which is shared with <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
as well as a D-Bus interface.
as well as a D-Bus interface and a Varlink interface.
The library interface may be used to introspect and watch the state of virtual machines/containers.
The bus interface provides the same but in addition may also be used to register or terminate
machines.
machines. The Varlink interface may be used to register machines with optional extensions, e.g. with an
SSH key / address; it can be queried with
<command>varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.Machine</command>.
For more information please consult
<citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>3</manvolnum></citerefentry>
and
Expand Down
171 changes: 171 additions & 0 deletions src/machine/machine-varlink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <limits.h>

#include "sd-id128.h"

#include "hostname-util.h"
#include "json.h"
#include "machine-varlink.h"
#include "machine.h"
#include "path-util.h"
#include "pidref.h"
#include "process-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "varlink.h"

static JSON_DISPATCH_ENUM_DEFINE(dispatch_machine_class, MachineClass, machine_class_from_string);

static int machine_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
char **m = ASSERT_PTR(userdata);
const char *hostname;
int r;

assert(variant);

if (!json_variant_is_string(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));

hostname = json_variant_string(variant);
if (!hostname_is_valid(hostname, /* flags= */ 0))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Invalid machine name");

r = free_and_strdup(m, hostname);
if (r < 0)
return json_log_oom(variant, flags);

return 0;
}

static int machine_leader(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
PidRef *leader = ASSERT_PTR(userdata);
_cleanup_(pidref_done) PidRef temp = PIDREF_NULL;
uint64_t k;
int r;

if (!json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));

k = json_variant_unsigned(variant);
if (k > PID_T_MAX || !pid_is_valid(k))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid PID.", strna(name));

if (k == 1)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid leader PID.", strna(name));

r = pidref_set_pid(&temp, k);
if (r < 0)
return json_log(variant, flags, r, "Failed to pin process " PID_FMT ": %m", leader->pid);

pidref_done(leader);

*leader = TAKE_PIDREF(temp);

return 0;
}

static int machine_ifindices(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
Machine *m = ASSERT_PTR(userdata);
_cleanup_free_ int *netif = NULL;
size_t n_netif, k = 0;

assert(variant);

if (!json_variant_is_array(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));

n_netif = json_variant_elements(variant);

netif = new(int, n_netif);
if (!netif)
return json_log_oom(variant, flags);

JsonVariant *i;
JSON_VARIANT_ARRAY_FOREACH(i, variant) {
uint64_t b;

if (!json_variant_is_unsigned(i))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an unsigned integer.", k, strna(name));

b = json_variant_unsigned(i);
if (b > INT_MAX || b <= 0)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Invalid network interface index %"PRIu64, b);

netif[k++] = (int) b;
}
assert(k == n_netif);

free_and_replace(m->netif, netif);
m->n_netif = n_netif;

return 0;
}

static int machine_cid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
unsigned cid, *c = ASSERT_PTR(userdata);

assert(variant);

if (!json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));

cid = json_variant_unsigned(variant);
if (!VSOCK_CID_IS_REGULAR(cid))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a regular VSOCK CID.", strna(name));

*c = cid;

return 0;
}

int vl_method_register(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
_cleanup_(machine_freep) Machine *machine = NULL;
int r;

static const JsonDispatch dispatch_table[] = {
{ "name", JSON_VARIANT_STRING, machine_name, offsetof(Machine, name), JSON_MANDATORY },
{ "id", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(Machine, id), 0 },
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Machine, service), 0 },
{ "class", JSON_VARIANT_STRING, dispatch_machine_class, offsetof(Machine, class), JSON_MANDATORY },
{ "leader", JSON_VARIANT_UNSIGNED, machine_leader, offsetof(Machine, leader), 0 },
{ "rootDirectory", JSON_VARIANT_STRING, json_dispatch_absolute_path, offsetof(Machine, root_directory), 0 },
{ "ifIndices", JSON_VARIANT_ARRAY, machine_ifindices, 0, 0 },
{ "vsockCid", JSON_VARIANT_UNSIGNED, machine_cid, offsetof(Machine, vsock_cid), 0 },
{ "sshAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Machine, ssh_address), JSON_SAFE },
{ "sshPrivateKeyPath", JSON_VARIANT_STRING, json_dispatch_absolute_path, offsetof(Machine, ssh_private_key_path), 0 },
{}
};

r = machine_new(_MACHINE_CLASS_INVALID, NULL, &machine);
if (r < 0)
return r;

r = varlink_dispatch(link, parameters, dispatch_table, machine);
if (r != 0)
return r;

if (!pidref_is_set(&machine->leader)) {
r = varlink_get_peer_pidref(link, &machine->leader);
if (r < 0)
return r;
}

r = machine_link(manager, machine);
if (r < 0)
return r;

r = cg_pidref_get_unit(&machine->leader, &machine->unit);
if (r < 0)
return r;

r = machine_start(machine, NULL, NULL);
if (r < 0)
return r;

/* the manager will free this machine */
TAKE_PTR(machine);

return varlink_reply(link, NULL);
}
6 changes: 6 additions & 0 deletions src/machine/machine-varlink.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once

#include "varlink.h"

int vl_method_register(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
62 changes: 58 additions & 4 deletions src/machine/machined-varlink.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "format-util.h"
#include "machine-varlink.h"
#include "machined-varlink.h"
#include "mkdir.h"
#include "user-util.h"
#include "varlink.h"
#include "varlink-io.systemd.Machine.h"
#include "varlink-io.systemd.UserDatabase.h"

typedef struct LookupParameters {
Expand Down Expand Up @@ -378,13 +380,13 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
}

int manager_varlink_init(Manager *m) {
static int manager_varlink_init_userdb(Manager *m) {
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
int r;

assert(m);

if (m->varlink_server)
if (m->varlink_userdb_server)
return 0;

r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
Expand Down Expand Up @@ -415,12 +417,64 @@ int manager_varlink_init(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");

m->varlink_server = TAKE_PTR(s);
m->varlink_userdb_server = TAKE_PTR(s);
return 0;
}

static int manager_varlink_init_machine(Manager *m) {
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
int r;

assert(m);

if (m->varlink_machine_server)
return 0;

r = varlink_server_new(&s, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m");

varlink_server_set_userdata(s, m);

r = varlink_server_add_interface(s, &vl_interface_io_systemd_Machine);
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);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");

(void) mkdir_p("/run/systemd/machine", 0755);

r = varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.Machine", 0666);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket: %m");

r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");

m->varlink_machine_server = TAKE_PTR(s);
return 0;
}

int manager_varlink_init(Manager *m) {
int r;

r = manager_varlink_init_userdb(m);
if (r < 0)
return r;

r = manager_varlink_init_machine(m);
if (r < 0)
return r;

return 0;
}

void manager_varlink_done(Manager *m) {
assert(m);

m->varlink_server = varlink_server_unref(m->varlink_server);
m->varlink_userdb_server = varlink_server_unref(m->varlink_userdb_server);
m->varlink_machine_server = varlink_server_unref(m->varlink_machine_server);
}
5 changes: 4 additions & 1 deletion src/machine/machined.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,10 @@ static bool check_idle(void *userdata) {
if (m->operations)
return false;

if (varlink_server_current_connections(m->varlink_server) > 0)
if (varlink_server_current_connections(m->varlink_userdb_server) > 0)
return false;

if (varlink_server_current_connections(m->varlink_machine_server) > 0)
return false;

manager_gc(m, true);
Expand Down
3 changes: 2 additions & 1 deletion src/machine/machined.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ struct Manager {
sd_event_source *nscd_cache_flush_event;
#endif

VarlinkServer *varlink_server;
VarlinkServer *varlink_userdb_server;
VarlinkServer *varlink_machine_server;
};

int manager_add_machine(Manager *m, const char *name, Machine **_machine);
Expand Down
1 change: 1 addition & 0 deletions src/machine/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
libmachine_core_sources = files(
'image-dbus.c',
'machine-dbus.c',
'machine-varlink.c',
'machine.c',
'machined-core.c',
'machined-dbus.c',
Expand Down
1 change: 1 addition & 0 deletions src/shared/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ shared_sources = files(
'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.Machine.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.MountFileSystem.c',
'varlink-io.systemd.NamespaceResource.c',
Expand Down
22 changes: 22 additions & 0 deletions src/shared/varlink-io.systemd.Machine.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "varlink-idl.h"
#include "varlink-io.systemd.Machine.h"

static VARLINK_DEFINE_METHOD(
Register,
VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
VARLINK_DEFINE_INPUT(id, VARLINK_STRING, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(service, VARLINK_STRING, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(class, VARLINK_STRING, 0),
VARLINK_DEFINE_INPUT(leader, VARLINK_INT, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(rootDirectory, VARLINK_STRING, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(ifIndices, VARLINK_INT, VARLINK_ARRAY|VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(vsockCid, VARLINK_INT, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(sshAddress, VARLINK_STRING, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(sshPrivateKeyPath, VARLINK_STRING, VARLINK_NULLABLE));

VARLINK_DEFINE_INTERFACE(
io_systemd_Machine,
"io.systemd.Machine",
&vl_method_Register);
6 changes: 6 additions & 0 deletions src/shared/varlink-io.systemd.Machine.h
Original file line number Diff line number Diff line change
@@ -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_Machine;

0 comments on commit 5b44c81

Please sign in to comment.