summaryrefslogtreecommitdiffstats
path: root/src/machine
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
commitfc53809803cd2bc2434e312b19a18fa36776da12 (patch)
treeb4b43bd6538f51965ce32856e9c053d0f90919c8 /src/machine
parentAdding upstream version 255.5. (diff)
downloadsystemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz
systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/machine')
-rw-r--r--src/machine/image-dbus.c94
-rw-r--r--src/machine/image-dbus.h2
-rw-r--r--src/machine/machine-dbus.c77
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine-varlink.c173
-rw-r--r--src/machine/machine-varlink.h6
-rw-r--r--src/machine/machine.c150
-rw-r--r--src/machine/machine.h9
-rw-r--r--src/machine/machinectl.c861
-rw-r--r--src/machine/machined-dbus.c26
-rw-r--r--src/machine/machined-varlink.c62
-rw-r--r--src/machine/machined.c40
-rw-r--r--src/machine/machined.h3
-rw-r--r--src/machine/meson.build7
14 files changed, 628 insertions, 883 deletions
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index aa4525d..d8068cd 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -50,11 +50,8 @@ int bus_image_method_remove(
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -121,11 +118,8 @@ int bus_image_method_rename(
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -133,9 +127,17 @@ int bus_image_method_rename(
if (r == 0)
return 1; /* Will call us back */
+ /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
+ assert_se(hashmap_remove_value(m->image_cache, image->name, image));
+
r = image_rename(image, new_name);
- if (r < 0)
+ if (r < 0) {
+ image_unref(image);
return r;
+ }
+
+ /* Then save the object again in the cache. */
+ assert_se(hashmap_put(m->image_cache, image->name, image) > 0);
return sd_bus_reply_method_return(message, NULL);
}
@@ -173,11 +175,8 @@ int bus_image_method_clone(
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -240,11 +239,8 @@ int bus_image_method_mark_read_only(
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -285,11 +281,8 @@ int bus_image_method_set_limit(
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -393,30 +386,17 @@ static int image_flush_cache(sd_event_source *s, void *userdata) {
return 0;
}
-static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
- _cleanup_free_ char *e = NULL;
- Manager *m = userdata;
- Image *image = NULL;
- const char *p;
+int manager_acquire_image(Manager *m, const char *name, Image **ret) {
int r;
- assert(bus);
- assert(path);
- assert(interface);
- assert(found);
+ assert(m);
+ assert(name);
- p = startswith(path, "/org/freedesktop/machine1/image/");
- if (!p)
+ Image *existing = hashmap_get(m->image_cache, name);
+ if (existing) {
+ if (ret)
+ *ret = existing;
return 0;
-
- e = bus_label_unescape(p);
- if (!e)
- return -ENOMEM;
-
- image = hashmap_get(m->image_cache, e);
- if (image) {
- *found = image;
- return 1;
}
if (!m->image_cache_defer_event) {
@@ -433,19 +413,49 @@ static int image_object_find(sd_bus *bus, const char *path, const char *interfac
if (r < 0)
return r;
- r = image_find(IMAGE_MACHINE, e, NULL, &image);
- if (r == -ENOENT)
- return 0;
+ _cleanup_(image_unrefp) Image *image = NULL;
+ r = image_find(IMAGE_MACHINE, name, NULL, &image);
if (r < 0)
return r;
image->userdata = m;
r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
- if (r < 0) {
- image_unref(image);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = image;
+
+ TAKE_PTR(image);
+ return 0;
+}
+
+static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ Manager *m = userdata;
+ Image *image;
+ const char *p;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(interface);
+ assert(found);
+
+ p = startswith(path, "/org/freedesktop/machine1/image/");
+ if (!p)
+ return 0;
+
+ e = bus_label_unescape(p);
+ if (!e)
+ return -ENOMEM;
+
+ r = manager_acquire_image(m, e, &image);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return r;
- }
*found = image;
return 1;
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index 4b00203..0c4fab1 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -2,10 +2,12 @@
#pragma once
#include "bus-object.h"
+#include "discover-image.h"
#include "machined.h"
extern const BusObjectImplementation image_object;
+int manager_acquire_image(Manager *m, const char *name, Image **ret);
char *image_bus_path(const char *name);
int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 4620f32..a4f04c0 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -73,11 +73,8 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -106,11 +103,8 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -157,11 +151,8 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -241,7 +232,12 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (streq(us, them))
return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
- r = namespace_open(m->leader.pid, NULL, NULL, &netns_fd, NULL, NULL);
+ r = namespace_open(m->leader.pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return r;
@@ -351,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) {
@@ -375,7 +392,12 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
_cleanup_fclose_ FILE *f = NULL;
pid_t child;
- r = namespace_open(m->leader.pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(m->leader.pid,
+ &pidns_fd,
+ &mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ &root_fd);
if (r < 0)
return r;
@@ -449,11 +471,8 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -541,11 +560,8 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -656,11 +672,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -861,11 +874,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -949,11 +959,8 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -1070,11 +1077,8 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
@@ -1096,7 +1100,12 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
pid_t child;
- r = namespace_open(m->leader.pid, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(m->leader.pid,
+ /* ret_pidns_fd = */ NULL,
+ &mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ &root_fd);
if (r < 0)
return r;
@@ -1273,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",
@@ -1290,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 a013345..fccad71 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 0000000..968da3c
--- /dev/null
+++ b/src/machine/machine-varlink.c
@@ -0,0 +1,173 @@
+/* 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 == -EEXIST)
+ return varlink_error(link, "io.systemd.Machine.MachineExists", NULL);
+ 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 0000000..ce4ec54
--- /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 44ff5c1..0eb9a55 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -27,23 +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
@@ -56,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;
}
@@ -87,30 +104,36 @@ Machine* machine_free(Machine *m) {
while (m->operations)
operation_free(m->operations);
- if (m->in_gc_queue)
+ if (m->in_gc_queue) {
+ assert(m->manager);
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);
+ (void) hashmap_remove(m->manager->machines, m->name);
- if (m->manager->host_machine == m)
- m->manager->host_machine = NULL;
+ if (m->manager->host_machine == m)
+ m->manager->host_machine = NULL;
+ }
if (pidref_is_set(&m->leader)) {
- (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
+ if (m->manager)
+ (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
pidref_done(&m->leader);
}
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);
}
@@ -339,10 +362,12 @@ int machine_load(Machine *m) {
static int machine_start_scope(
Machine *machine,
+ bool allow_pidfd,
sd_bus_message *more_properties,
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
_cleanup_free_ char *escaped = NULL, *unit = NULL;
const char *description;
int r;
@@ -384,7 +409,7 @@ static int machine_start_scope(
if (r < 0)
return r;
- r = bus_append_scope_pidref(m, &machine->leader);
+ r = bus_append_scope_pidref(m, &machine->leader, allow_pidfd);
if (r < 0)
return r;
@@ -410,9 +435,16 @@ static int machine_start_scope(
if (r < 0)
return r;
- r = sd_bus_call(NULL, m, 0, error, &reply);
- if (r < 0)
- return r;
+ r = sd_bus_call(NULL, m, 0, &e, &reply);
+ if (r < 0) {
+ /* If this failed with a property we couldn't write, this is quite likely because the server
+ * doesn't support PIDFDs yet, let's try without. */
+ if (allow_pidfd &&
+ sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+ return machine_start_scope(machine, /* allow_pidfd = */ false, more_properties, error);
+
+ return sd_bus_error_move(error, &e);
+ }
machine->unit = TAKE_PTR(unit);
machine->referenced = true;
@@ -432,7 +464,7 @@ static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_e
assert(m->class != MACHINE_HOST);
if (!m->unit) {
- r = machine_start_scope(m, properties, error);
+ r = machine_start_scope(m, /* allow_pidfd = */ true, properties, error);
if (r < 0)
return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
}
@@ -643,8 +675,9 @@ void machine_release_unit(Machine *m) {
r = manager_unref_unit(m->manager, m->unit, &error);
if (r < 0)
- log_warning_errno(r, "Failed to drop reference to machine scope, ignoring: %s",
- bus_error_message(&error, r));
+ log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to drop reference to machine scope, ignoring: %s",
+ bus_error_message(&error, r));
m->referenced = false;
}
@@ -658,7 +691,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
uid_t uid_base, uid_shift, uid_range;
gid_t gid_base, gid_shift, gid_range;
_cleanup_fclose_ FILE *f = NULL;
- int k, r;
+ int r;
assert(m);
assert(ret);
@@ -690,14 +723,9 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
}
/* Read the first line. There's at least one. */
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EBADMSG;
- }
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r < 0)
+ return r;
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
if (uid_base != 0)
@@ -722,13 +750,12 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
/* Read the first line. There's at least one. */
errno = 0;
- k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
+ r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
+ if (r == EOF)
+ return errno_or_else(ENOMSG);
+ assert(r >= 0);
+ if (r != 3)
return -EBADMSG;
- }
/* If there's more than one line, then we don't support this file. */
r = safe_fgetc(f, NULL);
@@ -757,6 +784,7 @@ static int machine_owns_uid_internal(
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
@@ -778,18 +806,12 @@ static int machine_owns_uid_internal(
for (;;) {
uid_t uid_base, uid_shift, uid_range, converted;
- int k;
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
/* The private user namespace is disabled, ignoring. */
if (uid_shift == 0)
@@ -831,6 +853,7 @@ static int machine_translate_uid_internal(
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
@@ -850,18 +873,12 @@ static int machine_translate_uid_internal(
for (;;) {
uid_t uid_base, uid_shift, uid_range, converted;
- int k;
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
if (uid < uid_base || uid >= uid_base + uid_range)
continue;
@@ -872,6 +889,7 @@ static int machine_translate_uid_internal(
if (ret_host_uid)
*ret_host_uid = converted;
+
return 0;
}
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 30ef93b..f8146fb 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/machinectl.c b/src/machine/machinectl.c
index 418dd00..1b63e6d 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -15,6 +15,7 @@
#include "alloc-util.h"
#include "build.h"
+#include "build-path.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-locator.h"
@@ -62,6 +63,25 @@
#include "verbs.h"
#include "web-util.h"
+typedef enum MachineRunner {
+ RUNNER_NSPAWN,
+ RUNNER_VMSPAWN,
+ _RUNNER_MAX,
+ _RUNNER_INVALID = -EINVAL,
+} MachineRunner;
+
+static const char* const machine_runner_table[_RUNNER_MAX] = {
+ [RUNNER_NSPAWN] = "nspawn",
+ [RUNNER_VMSPAWN] = "vmspawn",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(machine_runner, MachineRunner);
+
+static const char* const machine_runner_unit_prefix_table[_RUNNER_MAX] = {
+ [RUNNER_NSPAWN] = "systemd-nspawn",
+ [RUNNER_VMSPAWN] = "systemd-vmspawn",
+};
+
static char **arg_property = NULL;
static bool arg_all = false;
static BusPrintPropertyFlags arg_print_flags = 0;
@@ -81,6 +101,7 @@ static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_now = false;
static bool arg_force = false;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
+static MachineRunner arg_runner = RUNNER_NSPAWN;
static const char* arg_format = NULL;
static const char *arg_uid = NULL;
static char **arg_setenv = NULL;
@@ -100,19 +121,14 @@ static OutputFlags get_output_flags(void) {
static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- const char *k, *v, **query_res = NULL;
- size_t count = 0, awaited_args = 0;
va_list ap;
int r;
assert(bus);
+ assert(method);
assert(name);
assert(query);
- NULSTR_FOREACH(iter, query)
- awaited_args++;
- query_res = newa0(const char *, awaited_args);
-
r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name);
if (r < 0)
return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
@@ -121,14 +137,23 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name
if (r < 0)
return bus_log_parse_error(r);
+ const char **res;
+ size_t n_fields = 0;
+
+ NULSTR_FOREACH(i, query)
+ n_fields++;
+
+ res = newa0(const char*, n_fields);
+
+ const char *k, *v;
while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
- count = 0;
- NULSTR_FOREACH(iter, query) {
- if (streq(k, iter)) {
- query_res[count] = v;
+ size_t c = 0;
+ NULSTR_FOREACH(i, query) {
+ if (streq(i, k)) {
+ res[c] = v;
break;
}
- count++;
+ c++;
}
}
if (r < 0)
@@ -138,24 +163,17 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name
if (r < 0)
return bus_log_parse_error(r);
+ r = 0;
+
va_start(ap, query);
- for (count = 0; count < awaited_args; count++) {
- char *val, **out;
-
- out = va_arg(ap, char **);
- assert(out);
- if (query_res[count]) {
- val = strdup(query_res[count]);
- if (!val) {
- va_end(ap);
- return -ENOMEM;
- }
- *out = val;
- }
+ FOREACH_ARRAY(i, res, n_fields) {
+ r = strdup_to(va_arg(ap, char**), *i);
+ if (r < 0)
+ break;
}
va_end(ap);
- return 0;
+ return r;
}
static int call_get_addresses(
@@ -184,12 +202,12 @@ static int call_get_addresses(
addresses = strdup(prefix);
if (!addresses)
return log_oom();
- prefix = "";
r = sd_bus_message_enter_container(reply, 'a', "(iay)");
if (r < 0)
return bus_log_parse_error(r);
+ prefix = "";
while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
int family;
const void *a;
@@ -235,7 +253,7 @@ static int show_table(Table *table, const char *word) {
assert(table);
assert(word);
- if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) {
+ if (!table_isempty(table) || OUTPUT_MODE_IS_JSON(arg_output)) {
r = table_set_sort(table, (size_t) 0);
if (r < 0)
return table_log_sort_error(r);
@@ -251,10 +269,10 @@ static int show_table(Table *table, const char *word) {
}
if (arg_legend) {
- if (table_get_rows(table) > 1)
- printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
- else
+ if (table_isempty(table))
printf("No %s.\n", word);
+ else
+ printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
}
return 0;
@@ -587,16 +605,15 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
show_journal_by_unit(
stdout,
i->unit,
- NULL,
+ /* namespace = */ NULL,
arg_output,
- 0,
+ /* n_columns = */ 0,
i->timestamp.monotonic,
arg_lines,
- 0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
- true,
- NULL);
+ /* system_unit = */ true,
+ /* ellipsized = */ NULL);
}
}
@@ -747,22 +764,17 @@ static int print_image_hostname(sd_bus *bus, const char *name) {
static int print_image_machine_id(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- sd_id128_t id = SD_ID128_NULL;
- const void *p;
- size_t size;
+ sd_id128_t id;
int r;
r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name);
if (r < 0)
return r;
- r = sd_bus_message_read_array(reply, 'y', &p, &size);
+ r = bus_message_read_id128(reply, &id);
if (r < 0)
return r;
- if (size == sizeof(sd_id128_t))
- memcpy(&id, p, size);
-
if (!sd_id128_is_null(id))
printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
@@ -1052,6 +1064,12 @@ static int kill_machine(int argc, char *argv[], void *userdata) {
}
static int reboot_machine(int argc, char *argv[], void *userdata) {
+ if (arg_runner == RUNNER_VMSPAWN)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "%s only support supported for --runner=nspawn",
+ streq(argv[0], "reboot") ? "Reboot" : "Restart");
+
arg_kill_whom = "leader";
arg_signal = SIGINT; /* sysvinit + systemd */
@@ -1201,7 +1219,7 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
assert(master >= 0);
assert(name);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT) >= 0);
if (!arg_quiet) {
if (streq(name, ".host"))
@@ -1462,6 +1480,9 @@ static int edit_settings(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Edit is only supported on the host machine.");
+ if (arg_runner == RUNNER_VMSPAWN)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Edit is only supported for --runner=nspawn");
+
r = mac_init();
if (r < 0)
return r;
@@ -1525,6 +1546,9 @@ static int cat_settings(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Cat is only supported on the host machine.");
+ if (arg_runner == RUNNER_VMSPAWN)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Cat is only supported for --runner=nspawn");
+
pager_open(arg_pager_flags);
STRV_FOREACH(name, strv_skip(argv, 1)) {
@@ -1682,12 +1706,13 @@ static int make_service_name(const char *name, char **ret) {
assert(name);
assert(ret);
+ assert(arg_runner >= 0 && arg_runner < (MachineRunner) ELEMENTSOF(machine_runner_unit_prefix_table));
if (!hostname_is_valid(name, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid machine name %s.", name);
- r = unit_name_build("systemd-nspawn", name, ".service", ret);
+ r = unit_name_build(machine_runner_unit_prefix_table[arg_runner], name, ".service", ret);
if (r < 0)
return log_error_errno(r, "Failed to build unit name: %m");
@@ -1846,633 +1871,6 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
return 0;
}
-static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char **our_path = userdata, *line;
- unsigned priority;
- int r;
-
- assert(m);
- assert(our_path);
-
- r = sd_bus_message_read(m, "us", &priority, &line);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
-
- if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
- return 0;
-
- if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
- return 0;
-
- log_full(priority, "%s", line);
- return 0;
-}
-
-static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char **our_path = userdata, *path, *result;
- uint32_t id;
- int r;
-
- assert(m);
- assert(our_path);
-
- r = sd_bus_message_read(m, "uos", &id, &path, &result);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
-
- if (!streq_ptr(*our_path, path))
- return 0;
-
- sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
- return 0;
-}
-
-static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- assert(s);
- assert(si);
-
- if (!arg_quiet)
- log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
-
- sd_event_exit(sd_event_source_get_event(s), EINTR);
- return 0;
-}
-
-static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_(sd_event_unrefp) sd_event* event = NULL;
- const char *path = NULL;
- uint32_t id;
- int r;
-
- assert(bus);
- assert(m);
-
- polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
-
- r = sd_event_default(&event);
- if (r < 0)
- return log_error_errno(r, "Failed to get event loop: %m");
-
- r = sd_bus_attach_event(bus, event, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop: %m");
-
- r = bus_match_signal_async(
- bus,
- &slot_job_removed,
- bus_import_mgr,
- "TransferRemoved",
- match_transfer_removed, NULL, &path);
- if (r < 0)
- return log_error_errno(r, "Failed to request match: %m");
-
- r = sd_bus_match_signal_async(
- bus,
- &slot_log_message,
- "org.freedesktop.import1",
- NULL,
- "org.freedesktop.import1.Transfer",
- "LogMessage",
- match_log_message, NULL, &path);
- if (r < 0)
- return log_error_errno(r, "Failed to request match: %m");
-
- r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0)
- return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "uo", &id, &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-
- if (!arg_quiet)
- log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
-
- (void) sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
- (void) sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
-
- r = sd_event_loop(event);
- if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
-
- return -r;
-}
-
-static int import_tar(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *ll = NULL, *fn = NULL;
- const char *local = NULL, *path = NULL;
- _cleanup_close_ int fd = -EBADF;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- if (argc >= 2)
- path = empty_or_dash_to_null(argv[1]);
-
- if (argc >= 3)
- local = empty_or_dash_to_null(argv[2]);
- else if (path) {
- r = path_extract_filename(path, &fn);
- if (r < 0)
- return log_error_errno(r, "Cannot extract container name from filename: %m");
- if (r == O_DIRECTORY)
- return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
- "Path '%s' refers to directory, but we need a regular file: %m", path);
-
- local = fn;
- }
- if (!local)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Need either path or local name.");
-
- r = tar_strip_suffixes(local, &ll);
- if (r < 0)
- return log_oom();
-
- local = ll;
-
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local name %s is not a suitable machine name.",
- local);
-
- if (path) {
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", path);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTar");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "hsbb",
- fd >= 0 ? fd : STDIN_FILENO,
- local,
- arg_force,
- arg_read_only);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static int import_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *ll = NULL, *fn = NULL;
- const char *local = NULL, *path = NULL;
- _cleanup_close_ int fd = -EBADF;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- if (argc >= 2)
- path = empty_or_dash_to_null(argv[1]);
-
- if (argc >= 3)
- local = empty_or_dash_to_null(argv[2]);
- else if (path) {
- r = path_extract_filename(path, &fn);
- if (r < 0)
- return log_error_errno(r, "Cannot extract container name from filename: %m");
- if (r == O_DIRECTORY)
- return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
- "Path '%s' refers to directory, but we need a regular file: %m", path);
-
- local = fn;
- }
- if (!local)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Need either path or local name.");
-
- r = raw_strip_suffixes(local, &ll);
- if (r < 0)
- return log_oom();
-
- local = ll;
-
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local name %s is not a suitable machine name.",
- local);
-
- if (path) {
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", path);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRaw");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "hsbb",
- fd >= 0 ? fd : STDIN_FILENO,
- local,
- arg_force,
- arg_read_only);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static int import_fs(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- const char *local = NULL, *path = NULL;
- _cleanup_free_ char *fn = NULL;
- _cleanup_close_ int fd = -EBADF;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- if (argc >= 2)
- path = empty_or_dash_to_null(argv[1]);
-
- if (argc >= 3)
- local = empty_or_dash_to_null(argv[2]);
- else if (path) {
- r = path_extract_filename(path, &fn);
- if (r < 0)
- return log_error_errno(r, "Cannot extract container name from filename: %m");
-
- local = fn;
- }
- if (!local)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Need either path or local name.");
-
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local name %s is not a suitable machine name.",
- local);
-
- if (path) {
- fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open directory '%s': %m", path);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystem");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "hsbb",
- fd >= 0 ? fd : STDIN_FILENO,
- local,
- arg_force,
- arg_read_only);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static void determine_compression_from_filename(const char *p) {
- if (arg_format)
- return;
-
- if (!p)
- return;
-
- if (endswith(p, ".xz"))
- arg_format = "xz";
- else if (endswith(p, ".gz"))
- arg_format = "gzip";
- else if (endswith(p, ".bz2"))
- arg_format = "bzip2";
-}
-
-static int export_tar(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_close_ int fd = -EBADF;
- const char *local = NULL, *path = NULL;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- local = argv[1];
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Machine name %s is not valid.", local);
-
- if (argc >= 3)
- path = argv[2];
- path = empty_or_dash_to_null(path);
-
- if (path) {
- determine_compression_from_filename(path);
-
- fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", path);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTar");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "shs",
- local,
- fd >= 0 ? fd : STDOUT_FILENO,
- arg_format);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static int export_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_close_ int fd = -EBADF;
- const char *local = NULL, *path = NULL;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- local = argv[1];
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Machine name %s is not valid.", local);
-
- if (argc >= 3)
- path = argv[2];
- path = empty_or_dash_to_null(path);
-
- if (path) {
- determine_compression_from_filename(path);
-
- fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", path);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRaw");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "shs",
- local,
- fd >= 0 ? fd : STDOUT_FILENO,
- arg_format);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static int pull_tar(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *l = NULL, *ll = NULL;
- const char *local, *remote;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- remote = argv[1];
- if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "URL '%s' is not valid.", remote);
-
- if (argc >= 3)
- local = argv[2];
- else {
- r = import_url_last_component(remote, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to get final component of URL: %m");
-
- local = l;
- }
-
- local = empty_or_dash_to_null(local);
-
- if (local) {
- r = tar_strip_suffixes(local, &ll);
- if (r < 0)
- return log_oom();
-
- local = ll;
-
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local name %s is not a suitable machine name.",
- local);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "sssb",
- remote,
- local,
- import_verify_to_string(arg_verify),
- arg_force);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-static int pull_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *l = NULL, *ll = NULL;
- const char *local, *remote;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- remote = argv[1];
- if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "URL '%s' is not valid.", remote);
-
- if (argc >= 3)
- local = argv[2];
- else {
- r = import_url_last_component(remote, &l);
- if (r < 0)
- return log_error_errno(r, "Failed to get final component of URL: %m");
-
- local = l;
- }
-
- local = empty_or_dash_to_null(local);
-
- if (local) {
- r = raw_strip_suffixes(local, &ll);
- if (r < 0)
- return log_oom();
-
- local = ll;
-
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local name %s is not a suitable machine name.",
- local);
- }
-
- r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "sssb",
- remote,
- local,
- import_verify_to_string(arg_verify),
- arg_force);
- if (r < 0)
- return bus_log_create_error(r);
-
- return transfer_image_common(bus, m);
-}
-
-typedef struct TransferInfo {
- uint32_t id;
- const char *type;
- const char *remote;
- const char *local;
- double progress;
-} TransferInfo;
-
-static int compare_transfer_info(const TransferInfo *a, const TransferInfo *b) {
- return strcmp(a->local, b->local);
-}
-
-static int list_transfers(int argc, char *argv[], void *userdata) {
- size_t max_type = STRLEN("TYPE"), max_local = STRLEN("LOCAL"), max_remote = STRLEN("REMOTE");
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ TransferInfo *transfers = NULL;
- const char *type, *remote, *local;
- sd_bus *bus = userdata;
- uint32_t id, max_id = 0;
- size_t n_transfers = 0;
- double progress;
- int r;
-
- pager_open(arg_pager_flags);
-
- r = bus_call_method(bus, bus_import_mgr, "ListTransfers", &error, &reply, NULL);
- if (r < 0)
- return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, NULL)) > 0) {
- size_t l;
-
- if (!GREEDY_REALLOC(transfers, n_transfers + 1))
- return log_oom();
-
- transfers[n_transfers].id = id;
- transfers[n_transfers].type = type;
- transfers[n_transfers].remote = remote;
- transfers[n_transfers].local = local;
- transfers[n_transfers].progress = progress;
-
- l = strlen(type);
- if (l > max_type)
- max_type = l;
-
- l = strlen(remote);
- if (l > max_remote)
- max_remote = l;
-
- l = strlen(local);
- if (l > max_local)
- max_local = l;
-
- if (id > max_id)
- max_id = id;
-
- n_transfers++;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- typesafe_qsort(transfers, n_transfers, compare_transfer_info);
-
- if (arg_legend && n_transfers > 0)
- printf("%-*s %-*s %-*s %-*s %-*s\n",
- (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
- (int) 7, "PERCENT",
- (int) max_type, "TYPE",
- (int) max_local, "LOCAL",
- (int) max_remote, "REMOTE");
-
- for (size_t j = 0; j < n_transfers; j++)
-
- if (transfers[j].progress < 0)
- printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
- (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
- (int) 7, "n/a",
- (int) max_type, transfers[j].type,
- (int) max_local, transfers[j].local,
- (int) max_remote, transfers[j].remote);
- else
- printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
- (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
- (int) 6, (unsigned) (transfers[j].progress * 100),
- (int) max_type, transfers[j].type,
- (int) max_local, transfers[j].local,
- (int) max_remote, transfers[j].remote);
-
- if (arg_legend) {
- if (n_transfers > 0)
- printf("\n%zu transfers listed.\n", n_transfers);
- else
- printf("No transfers.\n");
- }
-
- return 0;
-}
-
-static int cancel_transfer(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- sd_bus *bus = ASSERT_PTR(userdata);
- int r;
-
- polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
-
- for (int i = 1; i < argc; i++) {
- uint32_t id;
-
- r = safe_atou32(argv[i], &id);
- if (r < 0)
- return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
-
- r = bus_call_method(bus, bus_import_mgr, "CancelTransfer", &error, NULL, "u", id);
- if (r < 0)
- return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, r));
- }
-
- return 0;
-}
-
static int set_limit(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
@@ -2557,6 +1955,52 @@ static int clean_images(int argc, char *argv[], void *userdata) {
return 0;
}
+static int chainload_importctl(int argc, char *argv[]) {
+ int r;
+
+ log_notice("The 'machinectl %1$s' command has been replaced by 'importctl -m %1$s'. Redirecting invocation.", argv[optind]);
+
+ _cleanup_strv_free_ char **c =
+ strv_new("importctl", "--class=machine");
+ if (!c)
+ return log_oom();
+
+ if (FLAGS_SET(arg_pager_flags, PAGER_DISABLE))
+ if (strv_extend(&c, "--no-pager") < 0)
+ return log_oom();
+ if (!arg_legend)
+ if (strv_extend(&c, "--no-legend") < 0)
+ return log_oom();
+ if (arg_read_only)
+ if (strv_extend(&c, "--read-only") < 0)
+ return log_oom();
+ if (arg_force)
+ if (strv_extend(&c, "--force") < 0)
+ return log_oom();
+ if (arg_quiet)
+ if (strv_extend(&c, "--quiet") < 0)
+ return log_oom();
+ if (!arg_ask_password)
+ if (strv_extend(&c, "--no-ask-password") < 0)
+ return log_oom();
+ if (strv_extend_many(&c, "--verify", import_verify_to_string(arg_verify)) < 0)
+ return log_oom();
+ if (arg_format)
+ if (strv_extend_many(&c, "--format", arg_format) < 0)
+ return log_oom();
+
+ if (strv_extend_strv(&c, argv + optind, /* filter_duplicates= */ false) < 0)
+ return log_oom();
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *joined = strv_join(c, " ");
+ log_debug("Chainloading: %s", joined);
+ }
+
+ r = invoke_callout_binary(BINDIR "/importctl", c);
+ return log_error_errno(r, "Failed to invoke 'importctl': %m");
+}
+
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -2601,16 +2045,6 @@ static int help(int argc, char *argv[], void *userdata) {
" remove NAME... Remove an image\n"
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
" clean Remove hidden (or all) images\n"
- "\n%3$sImage Transfer Commands:%4$s\n"
- " pull-tar URL [NAME] Download a TAR container image\n"
- " pull-raw URL [NAME] Download a RAW container or VM image\n"
- " import-tar FILE [NAME] Import a local TAR container image\n"
- " import-raw FILE [NAME] Import a local RAW container or VM image\n"
- " import-fs DIRECTORY [NAME] Import a local directory container image\n"
- " export-tar NAME [FILE] Export a TAR container image locally\n"
- " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
- " list-transfers Show list of downloads in progress\n"
- " cancel-transfer Cancel a download\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -2620,15 +2054,16 @@ static int help(int argc, char *argv[], void *userdata) {
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME Show only properties by this name\n"
+ " --value When showing properties, only print the value\n"
+ " -P NAME Equivalent to --value --property=NAME\n"
" -q --quiet Suppress output\n"
" -a --all Show all properties, including empty ones\n"
- " --value When showing properties, only print the value\n"
" -l --full Do not ellipsize output\n"
" --kill-whom=WHOM Whom to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
" -E --setenv=VAR[=VALUE] Add an environment variable for shell\n"
- " --read-only Create read-only bind mount\n"
+ " --read-only Create read-only bind mount or clone\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
" --max-addresses=INTEGER Number of internet addresses to show at most\n"
@@ -2637,11 +2072,11 @@ static int help(int argc, char *argv[], void *userdata) {
" short-monotonic, short-unix, short-delta,\n"
" json, json-pretty, json-sse, json-seq, cat,\n"
" verbose, export, with-unit)\n"
- " --verify=MODE Verification mode for downloaded images (no,\n"
- " checksum, signature)\n"
- " --force Download image even if already exists\n"
+ " --force Replace target file when copying, if necessary\n"
" --now Start or power off container after enabling or\n"
" disabling it\n"
+ " --runner=RUNNER Select between nspawn and vmspawn as the runner\n"
+ " -V Short for --runner=vmspawn\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -2665,6 +2100,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_MKDIR,
ARG_NO_ASK_PASSWORD,
ARG_VERIFY,
+ ARG_RUNNER,
ARG_NOW,
ARG_FORCE,
ARG_FORMAT,
@@ -2676,8 +2112,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "property", required_argument, NULL, 'p' },
- { "all", no_argument, NULL, 'a' },
{ "value", no_argument, NULL, ARG_VALUE },
+ { "all", no_argument, NULL, 'a' },
{ "full", no_argument, NULL, 'l' },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
@@ -2692,6 +2128,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "output", required_argument, NULL, 'o' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "verify", required_argument, NULL, ARG_VERIFY },
+ { "runner", required_argument, NULL, ARG_RUNNER },
{ "now", no_argument, NULL, ARG_NOW },
{ "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT },
@@ -2712,7 +2149,7 @@ static int parse_argv(int argc, char *argv[]) {
optind = 0;
for (;;) {
- static const char option_string[] = "-hp:als:H:M:qn:o:E:";
+ static const char option_string[] = "-hp:P:als:H:M:qn:o:E:V";
c = getopt_long(argc, argv, option_string + reorder, options, NULL);
if (c < 0)
@@ -2740,7 +2177,7 @@ static int parse_argv(int argc, char *argv[]) {
/* If we already found the "shell" verb on the command line, and now found the next
* non-option argument, then this is the machine name and we should stop processing
* further arguments. */
- optind --; /* don't process this argument, go one step back */
+ optind--; /* don't process this argument, go one step back */
goto done;
}
if (streq(optarg, "shell"))
@@ -2773,14 +2210,20 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case 'p':
+ case 'P':
r = strv_extend(&arg_property, optarg);
if (r < 0)
return log_oom();
- /* If the user asked for a particular
- * property, show it to them, even if it is
- * empty. */
+ /* If the user asked for a particular property, show it to them, even if empty. */
SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_SHOW_EMPTY, true);
+
+ if (c == 'p')
+ break;
+ _fallthrough_;
+
+ case ARG_VALUE:
+ SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
break;
case 'a':
@@ -2788,10 +2231,6 @@ static int parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
- case ARG_VALUE:
- SET_FLAG(arg_print_flags, BUS_PRINT_PROPERTY_ONLY_VALUE, true);
- break;
-
case 'l':
arg_full = true;
break;
@@ -2873,6 +2312,18 @@ static int parse_argv(int argc, char *argv[]) {
arg_verify = r;
break;
+ case 'V':
+ arg_runner = RUNNER_VMSPAWN;
+ break;
+
+ case ARG_RUNNER:
+ r = machine_runner_from_string(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --runner= setting: %s", optarg);
+
+ arg_runner = r;
+ break;
+
case ARG_NOW:
arg_now = true;
break;
@@ -2945,6 +2396,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "show-image", VERB_ANY, VERB_ANY, 0, show_image },
{ "terminate", 2, VERB_ANY, 0, terminate_machine },
{ "reboot", 2, VERB_ANY, 0, reboot_machine },
+ { "restart", 2, VERB_ANY, 0, reboot_machine }, /* Convenience alias */
{ "poweroff", 2, VERB_ANY, 0, poweroff_machine },
{ "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */
{ "kill", 2, VERB_ANY, 0, kill_machine },
@@ -2962,15 +2414,6 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "start", 2, VERB_ANY, 0, start_machine },
{ "enable", 2, VERB_ANY, 0, enable_machine },
{ "disable", 2, VERB_ANY, 0, enable_machine },
- { "import-tar", 2, 3, 0, import_tar },
- { "import-raw", 2, 3, 0, import_raw },
- { "import-fs", 2, 3, 0, import_fs },
- { "export-tar", 2, 3, 0, export_tar },
- { "export-raw", 2, 3, 0, export_raw },
- { "pull-tar", 2, 3, 0, pull_tar },
- { "pull-raw", 2, 3, 0, pull_raw },
- { "list-transfers", VERB_ANY, 1, 0, list_transfers },
- { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
{ "set-limit", 2, 3, 0, set_limit },
{ "clean", VERB_ANY, 1, 0, clean_images },
{}
@@ -2988,13 +2431,19 @@ static int run(int argc, char *argv[]) {
/* The journal merging logic potentially needs a lot of fds. */
(void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
-
sigbus_install();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
+ if (STRPTR_IN_SET(argv[optind],
+ "import-tar", "import-raw", "import-fs",
+ "export-tar", "export-raw",
+ "pull-tar", "pull-raw",
+ "list-transfers", "cancel-transfer"))
+ return chainload_importctl(argc, argv);
+
r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 9fec047..944b52e 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);
}
@@ -546,8 +550,8 @@ static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata,
}
static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_error *error, sd_bus_message_handler_t method) {
- _cleanup_(image_unrefp) Image* i = NULL;
const char *name;
+ Image *i;
int r;
assert(message);
@@ -561,13 +565,12 @@ static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
- r = image_find(IMAGE_MACHINE, name, NULL, &i);
+ r = manager_acquire_image(m, name, &i);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
if (r < 0)
return r;
- i->userdata = m;
return method(message, i, error);
}
@@ -720,11 +723,8 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -855,11 +855,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
@@ -1073,6 +1070,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),
@@ -1504,9 +1506,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 6ca98e2..0d3ae62 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 58a407d..d7087e4 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -96,7 +96,7 @@ static Manager* manager_unref(Manager *m) {
sd_event_source_unref(m->nscd_cache_flush_event);
#endif
- bus_verify_polkit_async_registry_free(m->polkit_registry);
+ hashmap_free(m->polkit_registry);
manager_varlink_done(m);
@@ -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);
@@ -320,17 +327,6 @@ static bool check_idle(void *userdata) {
return hashmap_isempty(m->machines);
}
-static int manager_run(Manager *m) {
- assert(m);
-
- return bus_event_loop_with_idle(
- m->event,
- m->bus,
- "org.freedesktop.machine1",
- DEFAULT_EXIT_USEC,
- check_idle, m);
-}
-
static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
@@ -353,7 +349,7 @@ static int run(int argc, char *argv[]) {
* make sure this check stays in. */
(void) mkdir_label("/run/systemd/machines", 0755);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)
@@ -363,16 +359,20 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to fully start up daemon: %m");
- log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
r = sd_notify(false, NOTIFY_READY);
if (r < 0)
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
- r = manager_run(m);
+ r = bus_event_loop_with_idle(
+ m->event,
+ m->bus,
+ "org.freedesktop.machine1",
+ DEFAULT_EXIT_USEC,
+ check_idle, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run main loop: %m");
- (void) sd_notify(false, NOTIFY_STOPPING);
- log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
- return r;
+ return 0;
}
DEFINE_MAIN_FUNCTION(run);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 280c32b..67abed0 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 b3a1ffc..3150b33 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',
@@ -35,9 +36,9 @@ executables += [
'conditions' : ['ENABLE_MACHINED'],
'sources' : files('machinectl.c'),
'dependencies' : [
- liblz4,
- libxz,
- libzstd,
+ liblz4_cflags,
+ libxz_cflags,
+ libzstd_cflags,
threads,
],
},