chiark / gitweb /
machinectl: add new "start" verb to start a container as a service in nspawn
authorLennart Poettering <lennart@poettering.net>
Mon, 29 Dec 2014 11:41:26 +0000 (12:41 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 29 Dec 2014 16:00:05 +0000 (17:00 +0100)
src/libsystemd/sd-bus/bus-util.c
src/libsystemd/sd-bus/bus-util.h
src/machine/machinectl.c
src/systemctl/systemctl.c

index 2c3da3ce92fc56d9fbf8a9355891c834d0026fc2..c264f2d3fdf1602cb2891070def4b930f9ae2381 100644 (file)
 
 #include <sys/socket.h>
 
 
 #include <sys/socket.h>
 
-#include "systemd/sd-daemon.h"
-
+#include "sd-daemon.h"
+#include "sd-event.h"
 #include "util.h"
 #include "strv.h"
 #include "macro.h"
 #include "def.h"
 #include "path-util.h"
 #include "missing.h"
 #include "util.h"
 #include "strv.h"
 #include "macro.h"
 #include "def.h"
 #include "path-util.h"
 #include "missing.h"
+#include "set.h"
 
 
-#include "sd-event.h"
 #include "sd-bus.h"
 #include "bus-error.h"
 #include "bus-message.h"
 #include "sd-bus.h"
 #include "bus-error.h"
 #include "bus-message.h"
@@ -1554,3 +1554,225 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
 
         return 0;
 }
 
         return 0;
 }
+
+typedef struct BusWaitForJobs {
+        sd_bus *bus;
+        Set *jobs;
+
+        char *name;
+        char *result;
+
+        sd_bus_slot *slot_job_removed;
+        sd_bus_slot *slot_disconnected;
+} BusWaitForJobs;
+
+static int match_disconnected(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        assert(bus);
+        assert(m);
+
+        log_error("Warning! D-Bus connection terminated.");
+        sd_bus_close(bus);
+
+        return 0;
+}
+
+static int match_job_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        const char *path, *unit, *result;
+        BusWaitForJobs *d = userdata;
+        uint32_t id;
+        char *found;
+        int r;
+
+        assert(bus);
+        assert(m);
+        assert(d);
+
+        r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return 0;
+        }
+
+        found = set_remove(d->jobs, (char*) path);
+        if (!found)
+                return 0;
+
+        free(found);
+
+        if (!isempty(result))
+                d->result = strdup(result);
+
+        if (!isempty(unit))
+                d->name = strdup(unit);
+
+        return 0;
+}
+
+void bus_wait_for_jobs_free(BusWaitForJobs *d) {
+        if (!d)
+                return;
+
+        set_free_free(d->jobs);
+
+        sd_bus_slot_unref(d->slot_disconnected);
+        sd_bus_slot_unref(d->slot_job_removed);
+
+        sd_bus_unref(d->bus);
+
+        free(d->name);
+        free(d->result);
+
+        free(d);
+}
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
+        int r;
+
+        assert(bus);
+        assert(ret);
+
+        d = new0(BusWaitForJobs, 1);
+        if (!d)
+                return -ENOMEM;
+
+        d->bus = sd_bus_ref(bus);
+
+        r = sd_bus_add_match(
+                        bus,
+                        &d->slot_job_removed,
+                        "type='signal',"
+                        "sender='org.freedesktop.systemd1',"
+                        "interface='org.freedesktop.systemd1.Manager',"
+                        "member='JobRemoved',"
+                        "path='/org/freedesktop/systemd1'",
+                        match_job_removed, d);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_add_match(
+                        bus,
+                        &d->slot_disconnected,
+                        "type='signal',"
+                        "sender='org.freedesktop.DBus.Local',"
+                        "interface='org.freedesktop.DBus.Local',"
+                        "member='Disconnected'",
+                        match_disconnected, d);
+        if (r < 0)
+                return r;
+
+        *ret = d;
+        d = NULL;
+
+        return 0;
+}
+
+static int bus_process_wait(sd_bus *bus) {
+        int r;
+
+        for (;;) {
+                r = sd_bus_process(bus, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0;
+
+                r = sd_bus_wait(bus, (uint64_t) -1);
+                if (r < 0)
+                        return r;
+        }
+}
+
+static int check_wait_response(BusWaitForJobs *d, bool quiet) {
+        int r = 0;
+
+        assert(d->result);
+
+        if (!quiet) {
+                if (streq(d->result, "canceled"))
+                        log_error("Job for %s canceled.", strna(d->name));
+                else if (streq(d->result, "timeout"))
+                        log_error("Job for %s timed out.", strna(d->name));
+                else if (streq(d->result, "dependency"))
+                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+                else if (streq(d->result, "invalid"))
+                        log_error("Job for %s invalid.", strna(d->name));
+                else if (streq(d->result, "assert"))
+                        log_error("Assertion failed on job for %s.", strna(d->name));
+                else if (streq(d->result, "unsupported"))
+                        log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+                else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
+                        if (d->name) {
+                                bool quotes;
+
+                                quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
+
+                                log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
+                                          d->name,
+                                          quotes ? "'" : "", d->name, quotes ? "'" : "");
+                        } else
+                                log_error("Job failed. See \"journalctl -xe\" for details.");
+                }
+        }
+
+        if (streq(d->result, "canceled"))
+                r = -ECANCELED;
+        else if (streq(d->result, "timeout"))
+                r = -ETIME;
+        else if (streq(d->result, "dependency"))
+                r = -EIO;
+        else if (streq(d->result, "invalid"))
+                r = -ENOEXEC;
+        else if (streq(d->result, "assert"))
+                r = -EPROTO;
+        else if (streq(d->result, "unsupported"))
+                r = -ENOTSUP;
+        else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+                r = -EIO;
+
+        return r;
+}
+
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
+        int r = 0;
+
+        assert(d);
+
+        while (!set_isempty(d->jobs)) {
+                int q;
+
+                q = bus_process_wait(d->bus);
+                if (q < 0)
+                        return log_error_errno(q, "Failed to wait for response: %m");
+
+                if (d->result) {
+                        q = check_wait_response(d, quiet);
+                        /* Return the first error as it is most likely to be
+                         * meaningful. */
+                        if (q < 0 && r == 0)
+                                r = q;
+
+                        log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
+                }
+
+                free(d->name);
+                d->name = NULL;
+
+                free(d->result);
+                d->result = NULL;
+        }
+
+        return r;
+}
+
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
+        int r;
+
+        assert(d);
+
+        r = set_ensure_allocated(&d->jobs, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        return set_put_strdup(d->jobs, path);
+}
index 0609882294febdb88865d7f70104aa6dbfe7351c..31e7fadbb6145bd0e414c3c0e05d3ba4e2c4cf13 100644 (file)
@@ -201,3 +201,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_track*, sd_bus_track_unref);
 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
 
 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
 
 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
+
+typedef struct BusWaitForJobs BusWaitForJobs;
+
+int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
+void bus_wait_for_jobs_free(BusWaitForJobs *d);
+int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
index 17fbe51f9f8486aae5375fb5487cae609c0b62c0..b0c1533491bc7db74b7d6af34d3565c3cf9a0cf7 100644 (file)
@@ -1417,6 +1417,76 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
         return 0;
 }
 
+static int start_machine(int argc, char *argv[], void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        sd_bus *bus = userdata;
+        int r, i;
+
+        assert(bus);
+
+        r = bus_wait_for_jobs_new(bus, &w);
+        if (r < 0)
+                return log_oom();
+
+        for (i = 1; i < argc; i++) {
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+                _cleanup_free_ char *e = NULL, *unit = NULL;
+                const char *object;
+
+                if (!machine_name_is_valid(argv[i])) {
+                        log_error("Invalid machine name %s.", argv[i]);
+                        return -EINVAL;
+                }
+
+                e = unit_name_escape(argv[i]);
+                if (!e)
+                        return log_oom();
+
+                unit = unit_name_build("systemd-nspawn", e, ".service");
+                if (!unit)
+                        return log_oom();
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "StartUnit");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_set_allow_interactive_authorization(m, true);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "ss", unit, "fail");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, &reply);
+                if (r < 0) {
+                        log_error("Failed to start unit: %s", bus_error_message(&error, -r));
+                        return r;
+                }
+
+                r = sd_bus_message_read(reply, "o", &object);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = bus_wait_for_jobs_add(w, object);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        r = bus_wait_for_jobs(w, false);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
 static int help(int argc, char *argv[], void *userdata) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -1440,6 +1510,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  status NAME...              Show VM/container details\n"
                "  show NAME...                Show properties of one or more VMs/containers\n"
                "  login NAME                  Get a login prompt on a container\n"
                "  status NAME...              Show VM/container details\n"
                "  show NAME...                Show properties of one or more VMs/containers\n"
                "  login NAME                  Get a login prompt on a container\n"
+               "  start NAME...               Start container as a service\n"
                "  poweroff NAME...            Power off one or more containers\n"
                "  reboot NAME...              Reboot one or more containers\n"
                "  terminate NAME...           Terminate one or more VMs/containers\n"
                "  poweroff NAME...            Power off one or more containers\n"
                "  reboot NAME...              Reboot one or more containers\n"
                "  terminate NAME...           Terminate one or more VMs/containers\n"
@@ -1594,6 +1665,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
                 { "rename",      3,        3,        0,            rename_image      },
                 { "clone",       3,        3,        0,            clone_image       },
                 { "read-only",   2,        3,        0,            read_only_image   },
                 { "rename",      3,        3,        0,            rename_image      },
                 { "clone",       3,        3,        0,            clone_image       },
                 { "read-only",   2,        3,        0,            read_only_image   },
+                { "start",       2,        VERB_ANY, 0,            start_machine     },
                 {}
         };
 
                 {}
         };
 
index abe23ec235ded319f5a564b1ccaafdebfe94bcce..060bfe28e4e96403260eaad43110712c42a46ac6 100644 (file)
@@ -2406,197 +2406,6 @@ static int unit_find_paths(sd_bus *bus,
         return r;
 }
 
         return r;
 }
 
-typedef struct WaitData {
-        Set *set;
-
-        char *name;
-        char *result;
-} WaitData;
-
-static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data, sd_bus_error *error) {
-        WaitData *d = data;
-
-        assert(bus);
-        assert(m);
-        assert(d);
-
-        log_debug("Got D-Bus request: %s.%s() on %s",
-                  sd_bus_message_get_interface(m),
-                  sd_bus_message_get_member(m),
-                  sd_bus_message_get_path(m));
-
-        if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
-                log_error("Warning! D-Bus connection terminated.");
-                sd_bus_close(bus);
-        } else if (sd_bus_message_is_signal(m, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
-                uint32_t id;
-                const char *path, *result, *unit;
-                char *ret;
-                int r;
-
-                r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
-                if (r >= 0) {
-                        ret = set_remove(d->set, (char*) path);
-                        if (!ret)
-                                return 0;
-
-                        free(ret);
-
-                        if (!isempty(result))
-                                d->result = strdup(result);
-
-                        if (!isempty(unit))
-                                d->name = strdup(unit);
-
-                        return 0;
-                }
-#ifndef NOLEGACY
-                r = sd_bus_message_read(m, "uos", &id, &path, &result);
-                if (r >= 0) {
-                        ret = set_remove(d->set, (char*) path);
-                        if (!ret)
-                                return 0;
-
-                        free(ret);
-
-                        if (*result)
-                                d->result = strdup(result);
-
-                        return 0;
-                }
-#endif
-
-                bus_log_parse_error(r);
-        }
-
-        return 0;
-}
-
-static int enable_wait_for_jobs(sd_bus *bus) {
-        int r;
-
-        assert(bus);
-
-        r = sd_bus_add_match(
-                        bus,
-                        NULL,
-                        "type='signal',"
-                        "sender='org.freedesktop.systemd1',"
-                        "interface='org.freedesktop.systemd1.Manager',"
-                        "member='JobRemoved',"
-                        "path='/org/freedesktop/systemd1'",
-                        NULL, NULL);
-        if (r < 0) {
-                log_error("Failed to add match");
-                return -EIO;
-        }
-
-        /* This is slightly dirty, since we don't undo the match registrations. */
-        return 0;
-}
-
-static int bus_process_wait(sd_bus *bus) {
-        int r;
-
-        for (;;) {
-                r = sd_bus_process(bus, NULL);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        return 0;
-                r = sd_bus_wait(bus, (uint64_t) -1);
-                if (r < 0)
-                        return r;
-        }
-}
-
-static int check_wait_response(WaitData *d) {
-        int r = 0;
-
-        assert(d->result);
-
-        if (!arg_quiet) {
-                if (streq(d->result, "canceled"))
-                        log_error("Job for %s canceled.", strna(d->name));
-                else if (streq(d->result, "timeout"))
-                        log_error("Job for %s timed out.", strna(d->name));
-                else if (streq(d->result, "dependency"))
-                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
-                else if (streq(d->result, "invalid"))
-                        log_error("Job for %s invalid.", strna(d->name));
-                else if (streq(d->result, "assert"))
-                        log_error("Assertion failed on job for %s.", strna(d->name));
-                else if (streq(d->result, "unsupported"))
-                        log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
-                else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
-                        if (d->name) {
-                                bool quotes;
-
-                                quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
-
-                                log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
-                                          d->name,
-                                          quotes ? "'" : "", d->name, quotes ? "'" : "");
-                        } else
-                                log_error("Job failed. See \"journalctl -xe\" for details.");
-                }
-        }
-
-        if (streq(d->result, "canceled"))
-                r = -ECANCELED;
-        else if (streq(d->result, "timeout"))
-                r = -ETIME;
-        else if (streq(d->result, "dependency"))
-                r = -EIO;
-        else if (streq(d->result, "invalid"))
-                r = -ENOEXEC;
-        else if (streq(d->result, "assert"))
-                r = -EPROTO;
-        else if (streq(d->result, "unsupported"))
-                r = -ENOTSUP;
-        else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
-                r = -EIO;
-
-        return r;
-}
-
-static int wait_for_jobs(sd_bus *bus, Set *s) {
-        _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
-        WaitData d = { .set = s };
-        int r = 0, q;
-
-        assert(bus);
-        assert(s);
-
-        q = sd_bus_add_filter(bus, &slot, wait_filter, &d);
-        if (q < 0)
-                return log_oom();
-
-        while (!set_isempty(s)) {
-                q = bus_process_wait(bus);
-                if (q < 0)
-                        return log_error_errno(q, "Failed to wait for response: %m");
-
-                if (d.result) {
-                        q = check_wait_response(&d);
-                        /* Return the first error as it is most likely to be
-                         * meaningful. */
-                        if (q < 0 && r == 0)
-                                r = q;
-                        log_debug("Got result %s/%s for job %s",
-                                  strna(d.result), strerror(-q), strna(d.name));
-                }
-
-                free(d.name);
-                d.name = NULL;
-
-                free(d.result);
-                d.result = NULL;
-        }
-
-        return r;
-}
-
 static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_free_ char *n = NULL, *state = NULL;
 static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_free_ char *n = NULL, *state = NULL;
@@ -2761,7 +2570,7 @@ static int start_unit_one(
                 const char *name,
                 const char *mode,
                 sd_bus_error *error,
                 const char *name,
                 const char *mode,
                 sd_bus_error *error,
-                Set *s) {
+                BusWaitForJobs *w) {
 
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
         const char *path;
 
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
         const char *path;
@@ -2814,15 +2623,9 @@ static int start_unit_one(
         if (need_daemon_reload(bus, name) > 0)
                 warn_unit_file_changed(name);
 
         if (need_daemon_reload(bus, name) > 0)
                 warn_unit_file_changed(name);
 
-        if (s) {
-                char *p;
-
-                p = strdup(path);
-                if (!p)
-                        return log_oom();
-
-                log_debug("Adding %s to the set", p);
-                r = set_consume(s, p);
+        if (w) {
+                log_debug("Adding %s to the set", path);
+                r = bus_wait_for_jobs_add(w, path);
                 if (r < 0)
                         return log_oom();
         }
                 if (r < 0)
                         return log_oom();
         }
@@ -2911,9 +2714,9 @@ static enum action verb_to_action(const char *verb) {
 }
 
 static int start_unit(sd_bus *bus, char **args) {
 }
 
 static int start_unit(sd_bus *bus, char **args) {
-        _cleanup_set_free_free_ Set *s = NULL;
-        _cleanup_strv_free_ char **names = NULL;
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
         const char *method, *mode, *one_name, *suffix = NULL;
         const char *method, *mode, *one_name, *suffix = NULL;
+        _cleanup_strv_free_ char **names = NULL;
         char **name;
         int r = 0;
 
         char **name;
         int r = 0;
 
@@ -2952,20 +2755,16 @@ static int start_unit(sd_bus *bus, char **args) {
         }
 
         if (!arg_no_block) {
         }
 
         if (!arg_no_block) {
-                r = enable_wait_for_jobs(bus);
+                r = bus_wait_for_jobs_new(bus, &w);
                 if (r < 0)
                         return log_error_errno(r, "Could not watch jobs: %m");
                 if (r < 0)
                         return log_error_errno(r, "Could not watch jobs: %m");
-
-                s = set_new(&string_hash_ops);
-                if (!s)
-                        return log_oom();
         }
 
         STRV_FOREACH(name, names) {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
                 int q;
 
         }
 
         STRV_FOREACH(name, names) {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
                 int q;
 
-                q = start_unit_one(bus, method, *name, mode, &error, s);
+                q = start_unit_one(bus, method, *name, mode, &error, w);
                 if (r >= 0 && q < 0)
                         r = translate_bus_error_to_exit_status(q, &error);
         }
                 if (r >= 0 && q < 0)
                         r = translate_bus_error_to_exit_status(q, &error);
         }
@@ -2973,7 +2772,7 @@ static int start_unit(sd_bus *bus, char **args) {
         if (!arg_no_block) {
                 int q;
 
         if (!arg_no_block) {
                 int q;
 
-                q = wait_for_jobs(bus, s);
+                q = bus_wait_for_jobs(w, arg_quiet);
                 if (q < 0)
                         return q;
 
                 if (q < 0)
                         return q;