Skip to content

Commit

Permalink
Merge 4a38cd1 into 82e00a3
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-leonard-ct committed Apr 24, 2024
2 parents 82e00a3 + 4a38cd1 commit 79eb0fe
Show file tree
Hide file tree
Showing 19 changed files with 427 additions and 55 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
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
55 changes: 53 additions & 2 deletions src/machine/machine-dbus.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <errno.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/wait.h>

Expand Down Expand Up @@ -482,6 +483,7 @@ static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {

switch (m->class) {

case MACHINE_VM:
case MACHINE_HOST:
*ret = NULL;
break;
Expand Down Expand Up @@ -585,7 +587,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
sd_bus *container_bus = NULL;
_cleanup_close_ int master = -EBADF, slave = -EBADF;
_cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
_cleanup_free_ char *command_line = NULL;
_cleanup_free_ char *command_line = NULL, *ssh_path = NULL;
Machine *m = ASSERT_PTR(userdata);
const char *p, *unit, *user, *path, *description, *utmp_id;
int r;
Expand Down Expand Up @@ -638,6 +640,55 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (!strv_env_is_valid(env))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");

/* if we are a VM we need to transform args/path/env into an SSH command line */
if (m->class == MACHINE_VM) {
_cleanup_strv_free_ char **ssh_cmdline = NULL;

if (isempty(m->ssh_address) || isempty(m->ssh_private_key_path))
return -EOPNOTSUPP;

r = find_executable("ssh", &ssh_path);
if (r < 0)
return r;

ssh_cmdline = strv_new("ssh", "-t", "-o", "IdentitiesOnly yes");
if (!ssh_cmdline)
return -ENOMEM;

r = strv_extend(&ssh_cmdline, "-o");
if (r < 0)
return r;

r = strv_extendf(&ssh_cmdline, "IdentityFile=%s", m->ssh_private_key_path);
if (r < 0)
return r;

STRV_FOREACH(envvar, env) {
r = strv_extend(&ssh_cmdline, "-o");
if (r < 0)
return r;

r = strv_extendf(&ssh_cmdline, "SetEnv %s", *envvar);
if (r < 0)
return r;
}

r = strv_extend(&ssh_cmdline, m->ssh_address);
if (r < 0)
return r;

r = strv_extend(&ssh_cmdline, path);
if (r < 0)
return r;

r = strv_extend_strv(&ssh_cmdline, strv_skip(args, 1), /* filter_duplicates= */ false);
if (r < 0)
return r;

path = ssh_path;
strv_free_and_replace(args, ssh_cmdline);
}

command_line = strv_join(args, " ");
if (!command_line)
return -ENOMEM;
Expand Down Expand Up @@ -718,7 +769,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;

if (!strv_isempty(env)) {
if (!strv_isempty(env) && m->class != MACHINE_VM) {
r = sd_bus_message_open_container(tm, 'r', "sv");
if (r < 0)
return r;
Expand Down
150 changes: 150 additions & 0 deletions src/machine/machine-varlink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#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 "sd-id128.h"
#include "socket-util.h"
#include "string-util.h"
#include "varlink.h"
#include <limits.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);
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));

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

r = free_and_strdup(m, json_variant_string(variant));
if (r < 0)
return json_log(variant, flags, r, "Failed to allocate string: %m");

return 0;
}

static int machine_leader(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
PidRef *leader = ASSERT_PTR(userdata);
_cleanup_(pidref_free) 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);

m->netif = TAKE_PTR(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), JSON_MANDATORY },
{ "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), JSON_SAFE },
{}
};

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

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

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

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);
43 changes: 26 additions & 17 deletions src/machine/machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "path-util.h"
#include "process-util.h"
#include "serialize.h"
#include "socket-util.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
Expand All @@ -36,13 +37,9 @@
#include "unit-name.h"
#include "user-util.h"

DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);

int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
int machine_new(MachineClass class, const char *name, Machine **ret) {
_cleanup_(machine_freep) Machine *m = NULL;
int r;

assert(manager);
assert(class < _MACHINE_CLASS_MAX);
assert(name);
assert(ret);
Expand All @@ -57,27 +54,36 @@ int machine_new(Manager *manager, MachineClass class, const char *name, Machine

*m = (Machine) {
.leader = PIDREF_NULL,
.vsock_cid = VMADDR_CID_ANY,
};

m->name = strdup(name);
if (!m->name)
return -ENOMEM;

if (class != MACHINE_HOST) {
m->state_file = path_join("/run/systemd/machines", m->name);
if (!m->state_file)
m->class = class;

*ret = TAKE_PTR(m);
return 0;
}

int machine_link(Manager *manager, Machine *machine) {
int r;
assert(manager);
assert(machine);

if (machine->class != MACHINE_HOST) {
machine->state_file = path_join("/run/systemd/machines", machine->name);
if (!machine->state_file)
return -ENOMEM;
}

m->class = class;

r = hashmap_put(manager->machines, m->name, m);
r = hashmap_put(manager->machines, machine->name, machine);
if (r < 0)
return r;

m->manager = manager;
machine->manager = manager;

*ret = TAKE_PTR(m);
return 0;
}

Expand All @@ -93,11 +99,9 @@ Machine* machine_free(Machine *m) {
LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
}

machine_release_unit(m);

free(m->scope_job);

if (m->manager) {
machine_release_unit(m);

(void) hashmap_remove(m->manager->machines, m->name);

if (m->manager->host_machine == m)
Expand All @@ -113,10 +117,13 @@ Machine* machine_free(Machine *m) {
sd_bus_message_unref(m->create_message);

free(m->name);
free(m->scope_job);
free(m->state_file);
free(m->service);
free(m->root_directory);
free(m->netif);
free(m->ssh_address);
free(m->ssh_private_key_path);
return mfree(m);
}

Expand Down Expand Up @@ -604,6 +611,7 @@ int machine_openpt(Machine *m, int flags, char **ret_slave) {

switch (m->class) {

case MACHINE_VM:
case MACHINE_HOST:
return openpt_allocate(flags, ret_slave);

Expand All @@ -623,6 +631,7 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {

switch (m->class) {

case MACHINE_VM:
case MACHINE_HOST:
return open_terminal(path, mode);

Expand Down

0 comments on commit 79eb0fe

Please sign in to comment.