]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/machine/machinectl.c
iovec-util: add exported constant empty but valid (i.e. non-NULL) iovec
[thirdparty/systemd.git] / src / machine / machinectl.c
index 99b1f79460f697619c847c72443a1446f4fa0515..5572115a547b534123333da6d95af4cd1af1593f 100644 (file)
@@ -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"
@@ -33,6 +34,7 @@
 #include "format-table.h"
 #include "hostname-util.h"
 #include "import-util.h"
+#include "in-addr-util.h"
 #include "locale-util.h"
 #include "log.h"
 #include "logs-show.h"
 #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 +102,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 +122,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 +138,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 +164,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 +203,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;
@@ -243,7 +262,7 @@ static int show_table(Table *table, const char *word) {
                 table_set_header(table, arg_legend);
 
                 if (OUTPUT_MODE_IS_JSON(arg_output))
-                        r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
+                        r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | SD_JSON_FORMAT_COLOR_AUTO);
                 else
                         r = table_print(table, NULL);
                 if (r < 0)
@@ -587,16 +606,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 +765,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 +1065,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 */
 
@@ -1462,6 +1481,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 +1547,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 +1707,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 +1872,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) >= 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 +1956,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 +2046,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 +2055,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 +2073,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 +2101,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 +2113,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 +2129,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 +2150,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)
@@ -2773,14 +2211,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 +2232,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 +2313,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;
@@ -2963,15 +2415,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      },
                 {}
@@ -2989,13 +2432,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);