--- /dev/null
+/* 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);
+}
/* 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 {
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);
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);
}
--- /dev/null
+/* 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);