diff --git a/man/org.freedesktop.machine1.xml b/man/org.freedesktop.machine1.xml index cc4e61d2d4..24bef6a755 100644 --- a/man/org.freedesktop.machine1.xml +++ b/man/org.freedesktop.machine1.xml @@ -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") @@ -230,6 +233,8 @@ node /org/freedesktop/machine1 { + + @@ -378,6 +383,10 @@ node /org/freedesktop/machine1 { AF_INET6) and a byte array containing the addresses. This is only supported for containers that make use of network namespacing. + GetMachineSSHInfo() 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. + GetMachineOSRelease() retrieves the OS release information of a container. This method returns an array of key value pairs read from the os-release5 file in @@ -459,6 +468,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, @@ -507,6 +518,12 @@ node /org/freedesktop/machine1/machine/rawhide { readonly s RootDirectory = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly ai NetworkInterfaces = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u VSockCID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SSHAddress = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SSHPrivateKeyPath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s State = '...'; }; @@ -548,6 +565,8 @@ node /org/freedesktop/machine1/machine/rawhide { + + @@ -590,6 +609,12 @@ node /org/freedesktop/machine1/machine/rawhide { + + + + + + @@ -601,9 +626,9 @@ node /org/freedesktop/machine1/machine/rawhide { take the same arguments as TerminateMachine() and KillMachine() on the Manager interface, respectively. - GetAddresses() and GetOSRelease() get the IP address and OS - release information from the machine. These methods take the same arguments as - GetMachineAddresses() and GetMachineOSRelease() of the + GetAddresses(), GetSSHInfo() and GetOSRelease() get the IP address, + SSH connection and OS release information from the machine. These methods take the same arguments as + GetMachineAddresses(), GetMachineSSHInfo() and GetMachineOSRelease() of the Manager interface, respectively. @@ -636,6 +661,15 @@ node /org/freedesktop/machine1/machine/rawhide { towards the container, the VM or the host. For details about this information see the description of CreateMachineWithNetwork() above. + VSockCID is the VSOCK CID of the VM if it is known, or + VMADDR_CID_ANY otherwise. + + SSHAddress is the address of the VM in a format ssh can understand + if it is known or the empty string. + + SSHPrivateKeyPath is the path to the SSH private key of the VM if it is known + or the empty string. + State is the state of the machine and is one of opening, running, or closing. 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. @@ -675,11 +709,14 @@ $ gdbus introspect --system \ The Manager Object CopyFromMachineWithFlags() and CopyToMachineWithFlags() were added in version 252. + GetMachineSSHInfo() was added in version 256. Machine Objects CopyFromWithFlags() and CopyToWithFlags() were added in version 252. + GetSSHInfo(), VSockCID, SSHAddress + and SSHPrivateKeyPath were added in version 256. diff --git a/man/systemd-machined.service.xml b/man/systemd-machined.service.xml index f3d7755973..b2899ff0fd 100644 --- a/man/systemd-machined.service.xml +++ b/man/systemd-machined.service.xml @@ -100,10 +100,12 @@ The daemon provides both a C library interface (which is shared with systemd-logind.service8) - 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 + varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.Machine. For more information please consult sd-login3 and diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index a7a53eacaf..a4f04c0b86 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -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) { @@ -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", @@ -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), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index a0133451ab..fccad71d12 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -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); diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c new file mode 100644 index 0000000000..377b3d3f0e --- /dev/null +++ b/src/machine/machine-varlink.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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); +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h new file mode 100644 index 0000000000..ce4ec54dc1 --- /dev/null +++ b/src/machine/machine-varlink.h @@ -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); diff --git a/src/machine/machine.c b/src/machine/machine.c index 6e1da3744a..5c21a039e5 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -27,24 +27,21 @@ #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" +#include "string-util.h" #include "terminal-util.h" #include "tmpfile-util.h" #include "uid-range.h" #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); /* Passing class == _MACHINE_CLASS_INVALID here is fine. It @@ -57,27 +54,46 @@ 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) + if (name) { + m->name = strdup(name); + if (!m->name) return -ENOMEM; } m->class = class; - r = hashmap_put(manager->machines, m->name, m); + *ret = TAKE_PTR(m); + return 0; +} + +int machine_link(Manager *manager, Machine *machine) { + int r; + + assert(manager); + assert(machine); + + if (machine->manager) + return -EEXIST; + if (!machine->name) + return -EINVAL; + + if (machine->class != MACHINE_HOST) { + char *temp = path_join("/run/systemd/machines", machine->name); + if (!temp) + return -ENOMEM; + + free_and_replace(machine->state_file, temp); + } + + 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; } @@ -93,11 +109,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) @@ -113,10 +127,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); } diff --git a/src/machine/machine.h b/src/machine/machine.h index 30ef93b36e..f8146fb55b 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -62,12 +62,17 @@ struct Machine { int *netif; size_t n_netif; + unsigned vsock_cid; + char *ssh_address; + char *ssh_private_key_path; + LIST_HEAD(Operation, operations); LIST_FIELDS(Machine, gc_queue); }; -int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret); +int machine_new(MachineClass class, const char *name, Machine **ret); +int machine_link(Manager *manager, Machine *machine); Machine* machine_free(Machine *m); bool machine_may_gc(Machine *m, bool drop_not_started); void machine_add_to_gc_queue(Machine *m); @@ -78,6 +83,8 @@ int machine_save(Machine *m); int machine_load(Machine *m); int machine_kill(Machine *m, KillWho who, int signo); +DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free); + void machine_release_unit(Machine *m); MachineState machine_get_state(Machine *u); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 6b108dc009..dbb03f3d67 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -462,6 +462,10 @@ static int method_get_machine_addresses(sd_bus_message *message, void *userdata, return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_addresses); } +static int method_get_machine_ssh_info(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_ssh_info); +} + static int method_get_machine_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { return redirect_method_to_machine(message, userdata, error, bus_machine_method_get_os_release); } @@ -1067,6 +1071,11 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_RESULT("a(iay)", addresses), method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("GetMachineSSHInfo", + SD_BUS_ARGS("s", name), + SD_BUS_RESULT("s", ssh_address, "s", ssh_private_key_path), + method_get_machine_ssh_info, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("GetMachineOSRelease", SD_BUS_ARGS("s", name), SD_BUS_RESULT("a{ss}", fields), @@ -1498,9 +1507,13 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { machine = hashmap_get(m->machines, name); if (!machine) { - r = machine_new(m, _MACHINE_CLASS_INVALID, name, &machine); + r = machine_new(_MACHINE_CLASS_INVALID, name, &machine); if (r < 0) return r; + + r = machine_link(m, machine); + if (r < 0) + return 0; } if (_machine) diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 6ca98e27cf..0d3ae627c1 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 "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 { @@ -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); @@ -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); } diff --git a/src/machine/machined.c b/src/machine/machined.c index 99301408d3..d7087e4672 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -132,10 +132,14 @@ static int manager_add_host_machine(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to open reference to PID 1: %m"); - r = machine_new(m, MACHINE_HOST, ".host", &t); + r = machine_new(MACHINE_HOST, ".host", &t); if (r < 0) return log_error_errno(r, "Failed to create machine: %m"); + r = machine_link(m, t); + if (r < 0) + return log_error_errno(r, "Failed to link machine to manager: %m"); + t->leader = TAKE_PIDREF(pidref); t->id = mid; @@ -312,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); diff --git a/src/machine/machined.h b/src/machine/machined.h index 280c32bab6..67abed0fd6 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -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); diff --git a/src/machine/meson.build b/src/machine/meson.build index c82a32589d..3150b33de5 100644 --- a/src/machine/meson.build +++ b/src/machine/meson.build @@ -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', diff --git a/src/shared/json.c b/src/shared/json.c index edfae5b47b..e1ee98ef2d 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -23,6 +23,7 @@ #include "math-util.h" #include "memory-util.h" #include "memstream-util.h" +#include "path-util.h" #include "set.h" #include "string-table.h" #include "string-util.h" @@ -5006,6 +5007,27 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi return 0; } +int json_dispatch_absolute_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + const char *path; + char **p = ASSERT_PTR(userdata); + + 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)); + + path = json_variant_string(variant); + if (!path_is_valid(path)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid path.", strna(name)); + if (!path_is_absolute(path)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' must be an absolute path.", strna(name)); + + if (free_and_strdup(p, path) < 0) + return json_log_oom(variant, flags); + + return 0; +} + int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { sd_id128_t *uuid = userdata; int r; diff --git a/src/shared/json.h b/src/shared/json.h index ec6274ad8d..6a93530a3a 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -425,6 +425,7 @@ int json_dispatch_int8(const char *name, JsonVariant *variant, JsonDispatchFlags int json_dispatch_uint8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); +int json_dispatch_absolute_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); diff --git a/src/shared/meson.build b/src/shared/meson.build index 17313aefed..d01367a159 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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', diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c new file mode 100644 index 0000000000..936f01f366 --- /dev/null +++ b/src/shared/varlink-io.systemd.Machine.c @@ -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); diff --git a/src/shared/varlink-io.systemd.Machine.h b/src/shared/varlink-io.systemd.Machine.h new file mode 100644 index 0000000000..c9fc85f150 --- /dev/null +++ b/src/shared/varlink-io.systemd.Machine.h @@ -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;