From 89f7c8465cd1ab37347dd0c15920bce31e8225df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Feb 2014 17:15:38 +0100 Subject: [PATCH 1/1] machined: optionally, allow registration of pre-existing units (scopes or services) as machine with machined --- man/systemd-nspawn.xml | 28 ++++++++++++++-- src/login/sd-login.c | 27 ++++++++++++++- src/machine/machine-dbus.c | 3 +- src/machine/machine.c | 57 ++++++++++++++++++++++--------- src/machine/machine.h | 2 +- src/machine/machinectl.c | 16 ++++----- src/machine/machined-dbus.c | 67 ++++++++++++++++++++++++++++++++----- src/nspawn/nspawn.c | 67 +++++++++++++++++++++++++++---------- src/shared/cgroup-util.c | 37 +++++--------------- 9 files changed, 220 insertions(+), 84 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 47c3183ee..9d8db83e8 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -465,8 +465,8 @@ should be enabled when the container runs a full Operating System (more specifically: an init system), and is - useful to ensure the container is - accesible via + useful to ensure that the container is + accessible via machinectl1 and shown by tools such as ps1. If @@ -478,6 +478,30 @@ . + + + + + Instead of creating a + transient scope unit to run the + container in, simply register the + service or scope unit + systemd-nspawn has + been invoked in in + systemd-machined8. This + has no effect if + is + used. This switch should be used if + systemd-nspawn is + invoked from within an a service unit, + and the service unit's sole purpose + is to run a single + systemd-nspawn + container. This option is not + available if run from a user + session. + + diff --git a/src/login/sd-login.c b/src/login/sd-login.c index c79576562..ef67040eb 100644 --- a/src/login/sd-login.c +++ b/src/login/sd-login.c @@ -630,7 +630,32 @@ _public_ int sd_get_uids(uid_t **users) { } _public_ int sd_get_machine_names(char ***machines) { - return get_files_in_directory("/run/systemd/machines/", machines); + char **l = NULL, **a, **b; + int r; + + r = get_files_in_directory("/run/systemd/machines/", &l); + if (r < 0) + return r; + + if (l) { + r = 0; + + /* Filter out the unit: symlinks */ + for (a = l, b = l; *a; a++) { + if (startswith(*a, "unit:")) + free(*a); + else { + *b = *a; + b++; + r++; + } + } + + *b = NULL; + } + + *machines = l; + return r; } static inline int MONITOR_TO_FD(sd_login_monitor *m) { diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 90eb001bb..df96ccf9d 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -132,7 +132,8 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, scope), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/machine/machine.c b/src/machine/machine.c index 0791ba8a9..e45c44349 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -76,9 +76,9 @@ void machine_free(Machine *m) { if (m->in_gc_queue) LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); - if (m->scope) { - hashmap_remove(m->manager->machine_units, m->scope); - free(m->scope); + if (m->unit) { + hashmap_remove(m->manager->machine_units, m->unit); + free(m->unit); } free(m->scope_job); @@ -123,8 +123,8 @@ int machine_save(Machine *m) { "NAME=%s\n", m->name); - if (m->scope) - fprintf(f, "SCOPE=%s\n", m->scope); + if (m->unit) + fprintf(f, "SCOPE=%s\n", m->unit); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */ if (m->scope_job) fprintf(f, "SCOPE_JOB=%s\n", m->scope_job); @@ -159,6 +159,16 @@ int machine_save(Machine *m) { unlink(temp_path); } + if (m->unit) { + char *sl; + + /* Create a symlink from the unit name to the machine + * name, so that we can quickly find the machine for + * each given unit */ + sl = strappenda("/run/systemd/machines/unit:", m->unit); + symlink(m->name, sl); + } + finish: if (r < 0) log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r)); @@ -166,6 +176,21 @@ finish: return r; } +static void machine_unlink(Machine *m) { + assert(m); + + if (m->unit) { + + char *sl; + + sl = strappenda("/run/systemd/machines/unit:", m->unit); + unlink(sl); + } + + if (m->state_file) + unlink(m->state_file); +} + int machine_load(Machine *m) { _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL; int r; @@ -173,7 +198,7 @@ int machine_load(Machine *m) { assert(m); r = parse_env_file(m->state_file, NEWLINE, - "SCOPE", &m->scope, + "SCOPE", &m->unit, "SCOPE_JOB", &m->scope_job, "SERVICE", &m->service, "ROOT", &m->root_directory, @@ -225,7 +250,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er assert(m); - if (!m->scope) { + if (!m->unit) { _cleanup_free_ char *escaped = NULL; char *scope, *description, *job; @@ -245,15 +270,15 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er free(scope); return r; } else { - m->scope = scope; + m->unit = scope; free(m->scope_job); m->scope_job = job; } } - if (m->scope) - hashmap_put(m->manager->machine_units, m->scope, m); + if (m->unit) + hashmap_put(m->manager->machine_units, m->unit, m); return r; } @@ -302,10 +327,10 @@ static int machine_stop_scope(Machine *m) { assert(m); - if (!m->scope) + if (!m->unit) return 0; - r = manager_stop_unit(m->manager, m->scope, &error, &job); + r = manager_stop_unit(m->manager, m->unit, &error, &job); if (r < 0) { log_error("Failed to stop machine scope: %s", bus_error_message(&error, r)); return r; @@ -334,7 +359,7 @@ int machine_stop(Machine *m) { if (k < 0) r = k; - unlink(m->state_file); + machine_unlink(m); machine_add_to_gc_queue(m); if (m->started) @@ -354,7 +379,7 @@ bool machine_check_gc(Machine *m, bool drop_not_started) { if (m->scope_job && manager_job_is_active(m->manager, m->scope_job)) return true; - if (m->scope && manager_unit_is_active(m->manager, m->scope)) + if (m->unit && manager_unit_is_active(m->manager, m->unit)) return true; return false; @@ -382,10 +407,10 @@ MachineState machine_get_state(Machine *s) { int machine_kill(Machine *m, KillWho who, int signo) { assert(m); - if (!m->scope) + if (!m->unit) return -ESRCH; - return manager_kill_unit(m->manager, m->scope, who, signo, NULL); + return manager_kill_unit(m->manager, m->unit, who, signo, NULL); } static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { diff --git a/src/machine/machine.h b/src/machine/machine.h index 62e4b2b34..f4aefc550 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -63,7 +63,7 @@ struct Machine { char *service; char *root_directory; - char *scope; + char *unit; char *scope_job; pid_t leader; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index b3a70188a..703fb3a3a 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -107,7 +107,7 @@ static int list_machines(sd_bus *bus, char **args, unsigned n) { return 0; } -static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) { +static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *path = NULL; @@ -129,7 +129,7 @@ static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - "org.freedesktop.systemd1.Scope", + endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", "ControlGroup", &error, &reply, @@ -168,7 +168,7 @@ typedef struct MachineStatusInfo { sd_id128_t id; char *class; char *service; - char *scope; + char *unit; char *root_directory; pid_t leader; usec_t timestamp; @@ -219,9 +219,9 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { if (i->root_directory) printf("\t Root: %s\n", i->root_directory); - if (i->scope) { - printf("\t Unit: %s\n", i->scope); - show_scope_cgroup(bus, i->scope, i->leader); + if (i->unit) { + printf("\t Unit: %s\n", i->unit); + show_unit_cgroup(bus, i->unit, i->leader); } } @@ -231,7 +231,7 @@ static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_ { "Name", "s", NULL, offsetof(MachineStatusInfo, name) }, { "Class", "s", NULL, offsetof(MachineStatusInfo, class) }, { "Service", "s", NULL, offsetof(MachineStatusInfo, service) }, - { "Scope", "s", NULL, offsetof(MachineStatusInfo, scope) }, + { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) }, { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) }, { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) }, { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) }, @@ -264,7 +264,7 @@ static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_ free(info.name); free(info.class); free(info.service); - free(info.scope); + free(info.unit); free(info.root_directory); return r; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 7c7293b29..4a75c34dd 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -169,9 +169,8 @@ static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *user return sd_bus_send(bus, reply, NULL); } -static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, Machine **_m, sd_bus_error *error) { const char *name, *service, *class, *root_directory; - Manager *manager = userdata; MachineClass c; uint32_t leader; sd_id128_t id; @@ -180,9 +179,9 @@ static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *use size_t n; int r; - assert(bus); - assert(message); assert(manager); + assert(message); + assert(_m); r = sd_bus_message_read(message, "s", &name); if (r < 0) @@ -218,10 +217,6 @@ static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *use if (!isempty(root_directory) && !path_is_absolute(root_directory)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path"); - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - return r; - if (leader == 0) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; @@ -263,17 +258,70 @@ static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *use } } + *_m = m; + + return 1; + +fail: + machine_add_to_gc_queue(m); + return r; +} + +static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *manager = userdata; + Machine *m = NULL; + int r; + + r = method_create_or_register_machine(manager, message, &m, error); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + goto fail; + r = machine_start(m, message, error); if (r < 0) goto fail; m->create_message = sd_bus_message_ref(message); - return 1; fail: machine_add_to_gc_queue(m); + return r; +} + +static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *manager = userdata; + _cleanup_free_ char *p = NULL; + Machine *m = NULL; + int r; + + r = method_create_or_register_machine(manager, message, &m, error); + if (r < 0) + return r; + + r = cg_pid_get_unit(m->leader, &m->unit); + if (r < 0) { + r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r)); + goto fail; + } + r = machine_start(m, NULL, error); + if (r < 0) + goto fail; + + p = machine_bus_path(m); + if (!p) { + r = -ENOMEM; + goto fail; + } + + return sd_bus_reply_method_return(message, "o", p); + +fail: + machine_add_to_gc_queue(m); return r; } @@ -347,6 +395,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0), + SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_SIGNAL("MachineNew", "so", 0), diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0a81f9729..97ef6c799 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -120,6 +120,7 @@ static char **arg_setenv = NULL; static bool arg_quiet = false; static bool arg_share_system = false; static bool arg_register = true; +static bool arg_keep_unit = false; static int help(void) { @@ -152,6 +153,8 @@ static int help(void) { " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n" " --setenv=NAME=VALUE Pass an environment variable to PID 1\n" " --register=BOOLEAN Register container as machine\n" + " --keep-unit Do not register a scope for the machine, reuse\n" + " the service unit nspawn is running in\n" " -q --quiet Do not show status information\n", program_invocation_short_name); @@ -172,7 +175,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_BIND_RO, ARG_SETENV, ARG_SHARE_SYSTEM, - ARG_REGISTER + ARG_REGISTER, + ARG_KEEP_UNIT }; static const struct option options[] = { @@ -197,6 +201,7 @@ static int parse_argv(int argc, char *argv[]) { { "quiet", no_argument, NULL, 'q' }, { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, { "register", required_argument, NULL, ARG_REGISTER }, + { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, {} }; @@ -410,6 +415,10 @@ static int parse_argv(int argc, char *argv[]) { arg_register = r; break; + case ARG_KEEP_UNIT: + arg_keep_unit = true; + break; + case '?': return -EINVAL; @@ -426,6 +435,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) { + log_error("--keep-unit may not be used when invoked from a user session."); + return -EINVAL; + } + return 1; } @@ -1086,22 +1100,41 @@ static int register_machine(pid_t pid) { return r; } - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CreateMachine", - &error, - NULL, - "sayssusa(sv)", - arg_machine, - SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), - "nspawn", - "container", - (uint32_t) pid, - strempty(arg_directory), - !isempty(arg_slice), "Slice", "s", arg_slice); + if (arg_keep_unit) { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "RegisterMachine", + &error, + NULL, + "sayssus", + arg_machine, + SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), + "nspawn", + "container", + (uint32_t) pid, + strempty(arg_directory)); + } else { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CreateMachine", + &error, + NULL, + "sayssusa(sv)", + arg_machine, + SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), + "nspawn", + "container", + (uint32_t) pid, + strempty(arg_directory), + !isempty(arg_slice), "Slice", "s", arg_slice); + } + if (r < 0) { log_error("Failed to register machine: %s", bus_error_message(&error, r)); return r; diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index e6ceb9945..9692a07f5 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -1282,39 +1282,18 @@ int cg_pid_get_user_unit(pid_t pid, char **unit) { } int cg_path_get_machine_name(const char *path, char **machine) { - const char *e, *n, *x; - char *s, *r; - size_t l; - - assert(path); - assert(machine); - - /* Skip slices, if there are any */ - e = skip_slices(path); - - n = strchrnul(e, '/'); - if (e == n) - return -ENOENT; - - s = strndupa(e, n - e); - s = cg_unescape(s); - - x = startswith(s, "machine-"); - if (!x) - return -ENOENT; - if (!endswith(x, ".scope")) - return -ENOENT; + _cleanup_free_ char *u = NULL, *sl = NULL; + int r; - l = strlen(x); - if (l <= 6) - return -ENOENT; + r = cg_path_get_unit(path, &u); + if (r < 0) + return r; - r = strndup(x, l - 6); - if (!r) + sl = strjoin("/run/systemd/machines/unit:", u, NULL); + if (!sl) return -ENOMEM; - *machine = r; - return 0; + return readlink_malloc(sl, machine); } int cg_pid_get_machine_name(pid_t pid, char **machine) { -- 2.30.2