1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-12 09:17:44 +03:00

machinectl: add new "start" verb to start a container as a service in nspawn

This commit is contained in:
Lennart Poettering 2014-12-29 12:41:26 +01:00
parent 6a140df004
commit ebd011d95b
4 changed files with 315 additions and 213 deletions

View File

@ -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);
}

View 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);
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);

View File

@ -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 },
{}
};

View File

@ -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;