Skip to content

Commit

Permalink
Merge 0859552 into c2e6ed6
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-leonard-ct committed Apr 24, 2024
2 parents c2e6ed6 + 0859552 commit 2e54501
Show file tree
Hide file tree
Showing 22 changed files with 540 additions and 60 deletions.
24 changes: 21 additions & 3 deletions man/org.freedesktop.machine1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ node /org/freedesktop/machine1 {
in i signal);
GetMachineAddresses(in s name,
out a(iay) addresses);
GetMachineSSHInfo(in s name,
out s ssh_address,
out s ssh_private_key_path);
GetMachineOSRelease(in s name,
out a{ss} fields);
@org.freedesktop.systemd1.Privileged("true")
Expand Down Expand Up @@ -378,6 +381,10 @@ node /org/freedesktop/machine1 {
<constant>AF_INET6</constant>) and a byte array containing the addresses. This is only supported for
containers that make use of network namespacing.</para>

<para><function>GetMachineSSHInfo()</function> retrieves the SSH information of a machine. This method
returns two strings, the SSH address which can be used to tell SSH where to connect, and the path
to the SSH private key required for the connection to succeed.</para>

<para><function>GetMachineOSRelease()</function> retrieves the OS release information of a
container. This method returns an array of key value pairs read from the
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file in
Expand Down Expand Up @@ -459,6 +466,8 @@ node /org/freedesktop/machine1/machine/rawhide {
Kill(in s who,
in i signal);
GetAddresses(out a(iay) addresses);
GetSSHInfo(out s ssh_address,
out s ssh_private_key_path);
GetOSRelease(out a{ss} fields);
GetUIDShift(out u shift);
OpenPTY(out h pty,
Expand Down Expand Up @@ -601,9 +610,9 @@ node /org/freedesktop/machine1/machine/rawhide {
take the same arguments as <function>TerminateMachine()</function> and
<function>KillMachine()</function> on the Manager interface, respectively.</para>

<para><function>GetAddresses()</function> and <function>GetOSRelease()</function> get the IP address and OS
release information from the machine. These methods take the same arguments as
<function>GetMachineAddresses()</function> and <function>GetMachineOSRelease()</function> of the
<para><function>GetAddresses()</function>, <function>GetSSHInfo</function> and <function>GetOSRelease()</function> get the IP address,
SSH connection and OS release information from the machine. These methods take the same arguments as
<function>GetMachineAddresses()</function>, <function>GetMachineSSHInfo()</function> and <function>GetMachineOSRelease()</function> of the
Manager interface, respectively.</para>
</refsect2>

Expand Down Expand Up @@ -636,6 +645,15 @@ node /org/freedesktop/machine1/machine/rawhide {
towards the container, the VM or the host. For details about this information see the description of
<function>CreateMachineWithNetwork()</function> above.</para>

<para><varname>VsockCid</varname> is the VSOCK CID of the VM if it is known, or
<constant>VMADDR_CID_ANY</constant> otherwise.</para>

<para><varname>SshAddress</varname> is the address of the VM in a format <command>ssh</command> can understand
if it is known or the empty string.</para>

<para><varname>SshPrivateKeyPath</varname> is the path to the SSH private key of the VM if it is known
or the empty string.</para>

<para><varname>State</varname> is the state of the machine and is one of <literal>opening</literal>,
<literal>running</literal>, or <literal>closing</literal>. Note that the state machine is not considered
part of the API and states might be removed or added without this being considered API breakage.
Expand Down
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
to allow <command>machinectl shell</command> to work; 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
29 changes: 29 additions & 0 deletions src/machine/machine-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,27 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
return sd_bus_send(NULL, reply, NULL);
}

int bus_machine_method_get_ssh_info(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Machine *m = ASSERT_PTR(userdata);
int r;

assert(message);

r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;

if (!m->ssh_address || !m->ssh_private_key_path)
return -ENOENT;

r = sd_bus_message_append(reply, "ss", m->ssh_address, m->ssh_private_key_path);
if (r < 0)
return r;

return sd_bus_send(NULL, reply, NULL);
}

#define EXIT_NOT_FOUND 2

int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Expand Down Expand Up @@ -1261,6 +1282,9 @@ static const sd_bus_vtable machine_vtable[] = {
SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("VsockCid", "u", NULL, offsetof(Machine, vsock_cid), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SshAddress", "s", NULL, offsetof(Machine, ssh_address), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SshPrivateKeyPath", "s", NULL, offsetof(Machine, ssh_private_key_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),

SD_BUS_METHOD("Terminate",
Expand All @@ -1278,6 +1302,11 @@ static const sd_bus_vtable machine_vtable[] = {
SD_BUS_RESULT("a(iay)", addresses),
bus_machine_method_get_addresses,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("GetSSHInfo",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("s", ssh_address, "s", ssh_private_key_path),
bus_machine_method_get_ssh_info,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("a{ss}", fields),
Expand Down
1 change: 1 addition & 0 deletions src/machine/machine-dbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_get_ssh_info(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error);
Expand Down
163 changes: 163 additions & 0 deletions src/machine/machine-varlink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* 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;

/* once manager_link has succeeded 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);

0 comments on commit 2e54501

Please sign in to comment.