summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libsystemd/sd-bus/bus-util.c228
-rw-r--r--src/libsystemd/sd-bus/bus-util.h9
-rw-r--r--src/machine/machinectl.c72
-rw-r--r--src/systemctl/systemctl.c219
4 files changed, 315 insertions, 213 deletions
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
index 2c3da3ce92..c264f2d3fd 100644
--- a/src/libsystemd/sd-bus/bus-util.c
+++ b/src/libsystemd/sd-bus/bus-util.c
@@ -21,16 +21,16 @@
#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 "set.h"
-#include "sd-event.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;
}
+
+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);
+}
diff --git a/src/libsystemd/sd-bus/bus-util.h b/src/libsystemd/sd-bus/bus-util.h
index 0609882294..31e7fadbb6 100644
--- a/src/libsystemd/sd-bus/bus-util.h
+++ b/src/libsystemd/sd-bus/bus-util.h
@@ -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);
+
+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);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 17fbe51f9f..b0c1533491 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1417,6 +1417,76 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
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"
@@ -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"
+ " 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"
@@ -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 },
+ { "start", 2, VERB_ANY, 0, start_machine },
{}
};
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index abe23ec235..060bfe28e4 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2406,197 +2406,6 @@ static int unit_find_paths(sd_bus *bus,
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;
@@ -2761,7 +2570,7 @@ static int start_unit_one(
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;
@@ -2814,15 +2623,9 @@ static int start_unit_one(
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();
}
@@ -2911,9 +2714,9 @@ static enum action verb_to_action(const char *verb) {
}
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;
+ _cleanup_strv_free_ char **names = NULL;
char **name;
int r = 0;
@@ -2952,20 +2755,16 @@ static int start_unit(sd_bus *bus, char **args) {
}
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");
-
- 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;
- 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);
}
@@ -2973,7 +2772,7 @@ static int start_unit(sd_bus *bus, char **args) {
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;