From 4b58153dd22172d817055d2a09a0cdf3f4bd9db3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 30 Aug 2016 23:18:46 +0200 Subject: core: add "invocation ID" concept to service manager This adds a new invocation ID concept to the service manager. The invocation ID identifies each runtime cycle of a unit uniquely. A new randomized 128bit ID is generated each time a unit moves from and inactive to an activating or active state. The primary usecase for this concept is to connect the runtime data PID 1 maintains about a service with the offline data the journal stores about it. Previously we'd use the unit name plus start/stop times, which however is highly racy since the journal will generally process log data after the service already ended. The "invocation ID" kinda matches the "boot ID" concept of the Linux kernel, except that it applies to an individual unit instead of the whole system. The invocation ID is passed to the activated processes as environment variable. It is additionally stored as extended attribute on the cgroup of the unit. The latter is used by journald to automatically retrieve it for each log logged message and attach it to the log entry. The environment variable is very easily accessible, even for unprivileged services. OTOH the extended attribute is only accessible to privileged processes (this is because cgroupfs only supports the "trusted." xattr namespace, not "user."). The environment variable may be altered by services, the extended attribute may not be, hence is the better choice for the journal. Note that reading the invocation ID off the extended attribute from journald is racy, similar to the way reading the unit name for a logging process is. This patch adds APIs to read the invocation ID to sd-id128: sd_id128_get_invocation() may be used in a similar fashion to sd_id128_get_boot(). PID1's own logging is updated to always include the invocation ID when it logs information about a unit. A new bus call GetUnitByInvocationID() is added that allows retrieving a bus path to a unit by its invocation ID. The bus path is built using the invocation ID, thus providing a path for referring to a unit that is valid only for the current runtime cycleof it. Outlook for the future: should the kernel eventually allow passing of cgroup information along AF_UNIX/SOCK_DGRAM messages via a unique cgroup id, then we can alter the invocation ID to be generated as hash from that rather than entirely randomly. This way we can derive the invocation race-freely from the messages. --- src/core/slice.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/core/slice.c') diff --git a/src/core/slice.c b/src/core/slice.c index c7700b8857..03fe797f27 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -187,10 +187,15 @@ static void slice_dump(Unit *u, FILE *f, const char *prefix) { static int slice_start(Unit *u) { Slice *t = SLICE(u); + int r; assert(t); assert(t->state == SLICE_DEAD); + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; + (void) unit_realize_cgroup(u); (void) unit_reset_cpu_usage(u); -- cgit v1.2.3-54-g00ecf From 8e4e851f1ddc98daf69b68998afc5a096ea17893 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Oct 2016 20:37:54 +0200 Subject: core: move initialization of -.slice and init.scope into the unit_load() callbacks Previously, we'd synthesize the root slice unit and the init scope unit in the enumerator callbacks for the unit type. This is problematic if either of them is already referenced from a unit that is loaded as result of another unit type's enumerator logic. Let's clean this up and simply create the two objects from the enumerator callbacks, if they are not around yet. Do the actual filling in of the settings from the unit_load() callbacks, to match how other units are loaded. Fixes: #4322 --- src/core/scope.c | 44 ++++++++++++++++++++++++++++++++------------ src/core/slice.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 22 deletions(-) (limited to 'src/core/slice.c') diff --git a/src/core/scope.c b/src/core/scope.c index e7583f6d89..af0c43c7da 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -147,6 +147,34 @@ static int scope_verify(Scope *s) { return 0; } +static int scope_load_init_scope(Unit *u) { + assert(u); + + if (!unit_has_name(u, SPECIAL_INIT_SCOPE)) + return 0; + + u->transient = true; + u->no_gc = true; + + /* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we + * synthesize it here, instead of relying on the unit file on disk. */ + + u->default_dependencies = false; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; + + SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; + + /* Prettify things, if we can. */ + if (!u->description) + u->description = strdup("System and Service Manager"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd(1)"); + + return 1; +} + static int scope_load(Unit *u) { Scope *s = SCOPE(u); int r; @@ -158,6 +186,9 @@ static int scope_load(Unit *u) { /* Refuse to load non-transient scope units, but allow them while reloading. */ return -ENOENT; + r = scope_load_init_scope(u); + if (r < 0) + return r; r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; @@ -543,25 +574,14 @@ static void scope_enumerate(Manager *m) { r = unit_add_name(u, SPECIAL_INIT_SCOPE); if (r < 0) { unit_free(u); - log_error_errno(r, "Failed to add init.scope name"); + log_error_errno(r, "Failed to add the " SPECIAL_INIT_SCOPE " name: %m"); return; } } u->transient = true; - u->default_dependencies = false; u->no_gc = true; - u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; SCOPE(u)->deserialized_state = SCOPE_RUNNING; - SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; - - /* Prettify things, if we can. */ - if (!u->description) - u->description = strdup("System and Service Manager"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd(1)"); unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); diff --git a/src/core/slice.c b/src/core/slice.c index 03fe797f27..0fef29661f 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -130,6 +130,30 @@ static int slice_verify(Slice *s) { return 0; } +static int slice_load_root_slice(Unit *u) { + assert(u); + + if (!unit_has_name(u, SPECIAL_ROOT_SLICE)) + return 0; + + u->no_gc = true; + + /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its + * special semantics we synthesize it here, instead of relying on the unit file on disk. */ + + u->default_dependencies = false; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; + + if (!u->description) + u->description = strdup("Root Slice"); + if (!u->documentation) + u->documentation = strv_new("man:systemd.special(7)", NULL); + + return 1; +} + static int slice_load(Unit *u) { Slice *s = SLICE(u); int r; @@ -137,6 +161,9 @@ static int slice_load(Unit *u) { assert(s); assert(u->load_state == UNIT_STUB); + r = slice_load_root_slice(u); + if (r < 0) + return r; r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; @@ -283,23 +310,14 @@ static void slice_enumerate(Manager *m) { r = unit_add_name(u, SPECIAL_ROOT_SLICE); if (r < 0) { unit_free(u); - log_error_errno(r, "Failed to add -.slice name"); + log_error_errno(r, "Failed to add the "SPECIAL_ROOT_SLICE " name: %m"); return; } } - u->default_dependencies = false; u->no_gc = true; - u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; SLICE(u)->deserialized_state = SLICE_ACTIVE; - if (!u->description) - u->description = strdup("Root Slice"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd.special(7)"); - unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); } -- cgit v1.2.3-54-g00ecf From f5869324e303a136d7ca802769e8966e8eb26d56 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Oct 2016 21:41:54 +0200 Subject: core: rework the "no_gc" unit flag to become a more generic "perpetual" flag So far "no_gc" was set on -.slice and init.scope, to units that are always running, cannot be stopped and never exist in an "inactive" state. Since these units are the only users of this flag, let's remodel it and rename it "perpetual" and let's derive more funcitonality off it. Specifically, refuse enqueing stop jobs for these units, and report that they are "unstoppable" in the CanStop bus property. --- src/core/dbus-unit.c | 6 ++---- src/core/scope.c | 6 ++---- src/core/slice.c | 10 ++++------ src/core/unit.c | 27 ++++++++++++++++++++++++--- src/core/unit.h | 6 ++++-- 5 files changed, 36 insertions(+), 19 deletions(-) (limited to 'src/core/slice.c') diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 8f34fa1a52..69e249c844 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -263,10 +263,7 @@ static int property_get_can_stop( assert(reply); assert(u); - /* On the lower levels we assume that every unit we can start - * we can also stop */ - - return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop); + return sd_bus_message_append(reply, "b", unit_can_stop(u) && !u->refuse_manual_stop); } static int property_get_can_reload( @@ -760,6 +757,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/scope.c b/src/core/scope.c index af0c43c7da..77f3fdc2aa 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -154,15 +154,13 @@ static int scope_load_init_scope(Unit *u) { return 0; u->transient = true; - u->no_gc = true; + u->perpetual = true; /* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we * synthesize it here, instead of relying on the unit file on disk. */ u->default_dependencies = false; u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; @@ -580,7 +578,7 @@ static void scope_enumerate(Manager *m) { } u->transient = true; - u->no_gc = true; + u->perpetual = true; SCOPE(u)->deserialized_state = SCOPE_RUNNING; unit_add_to_load_queue(u); diff --git a/src/core/slice.c b/src/core/slice.c index 0fef29661f..c505fa1916 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -136,15 +136,13 @@ static int slice_load_root_slice(Unit *u) { if (!unit_has_name(u, SPECIAL_ROOT_SLICE)) return 0; - u->no_gc = true; + u->perpetual = true; /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its * special semantics we synthesize it here, instead of relying on the unit file on disk. */ u->default_dependencies = false; u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; if (!u->description) u->description = strdup("Root Slice"); @@ -302,7 +300,7 @@ static void slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { u = unit_new(m, sizeof(Slice)); - if (!u) { + if (!u) { log_oom(); return; } @@ -310,12 +308,12 @@ static void slice_enumerate(Manager *m) { r = unit_add_name(u, SPECIAL_ROOT_SLICE); if (r < 0) { unit_free(u); - log_error_errno(r, "Failed to add the "SPECIAL_ROOT_SLICE " name: %m"); + log_error_errno(r, "Failed to add the " SPECIAL_ROOT_SLICE " name: %m"); return; } } - u->no_gc = true; + u->perpetual = true; SLICE(u)->deserialized_state = SLICE_ACTIVE; unit_add_to_load_queue(u); diff --git a/src/core/unit.c b/src/core/unit.c index cabb1050a8..8d74b44f73 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -323,7 +323,7 @@ bool unit_check_gc(Unit *u) { if (state != UNIT_INACTIVE) return true; - if (u->no_gc) + if (u->perpetual) return true; if (u->refs) @@ -924,6 +924,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tGC Check Good: %s\n" "%s\tNeed Daemon Reload: %s\n" "%s\tTransient: %s\n" + "%s\tPerpetual: %s\n" "%s\tSlice: %s\n" "%s\tCGroup: %s\n" "%s\tCGroup realized: %s\n" @@ -942,6 +943,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(unit_check_gc(u)), prefix, yes_no(unit_need_daemon_reload(u)), prefix, yes_no(u->transient), + prefix, yes_no(u->perpetual), prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), prefix, yes_no(u->cgroup_realized), @@ -1616,6 +1618,18 @@ int unit_stop(Unit *u) { return UNIT_VTABLE(u)->stop(u); } +bool unit_can_stop(Unit *u) { + assert(u); + + if (!unit_supported(u)) + return false; + + if (u->perpetual) + return false; + + return !!UNIT_VTABLE(u)->stop; +} + /* Errors: * -EBADR: This unit type does not support reloading. * -ENOEXEC: Unit is not started. @@ -2150,13 +2164,20 @@ bool unit_job_is_applicable(Unit *u, JobType j) { case JOB_VERIFY_ACTIVE: case JOB_START: - case JOB_STOP: case JOB_NOP: + /* Note that we don't check unit_can_start() here. That's because .device units and suchlike are not + * startable by us but may appear due to external events, and it thus makes sense to permit enqueing + * jobs for it. */ return true; + case JOB_STOP: + /* Similar as above. However, perpetual units can never be stopped (neither explicitly nor due to + * external events), hence it makes no sense to permit enqueing such a request either. */ + return !u->perpetual; + case JOB_RESTART: case JOB_TRY_RESTART: - return unit_can_start(u); + return unit_can_stop(u) && unit_can_start(u); case JOB_RELOAD: case JOB_TRY_RELOAD: diff --git a/src/core/unit.h b/src/core/unit.h index adcdee6db6..e524553ed3 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -236,6 +236,9 @@ struct Unit { /* Is this a transient unit? */ bool transient; + /* Is this a unit that is always running and cannot be stopped? */ + bool perpetual; + bool in_load_queue:1; bool in_dbus_queue:1; bool in_cleanup_queue:1; @@ -244,8 +247,6 @@ struct Unit { bool sent_dbus_new_signal:1; - bool no_gc:1; - bool in_audit:1; bool cgroup_realized:1; @@ -524,6 +525,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix); bool unit_can_reload(Unit *u) _pure_; bool unit_can_start(Unit *u) _pure_; +bool unit_can_stop(Unit *u) _pure_; bool unit_can_isolate(Unit *u) _pure_; int unit_start(Unit *u); -- cgit v1.2.3-54-g00ecf From a581e45ae8f9bb5c6693c23c78bc070aa15d0c8a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Oct 2016 00:29:05 +0200 Subject: unit: unify some code with new unit_new_for_name() call --- src/core/device.c | 6 +----- src/core/mount.c | 17 +++-------------- src/core/scope.c | 11 ++--------- src/core/slice.c | 11 ++--------- src/core/swap.c | 6 +----- src/core/unit.c | 18 ++++++++++++++++++ src/core/unit.h | 1 + 7 files changed, 28 insertions(+), 42 deletions(-) (limited to 'src/core/slice.c') diff --git a/src/core/device.c b/src/core/device.c index 8a3e888e5e..bd87a447cd 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -331,11 +331,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa if (!u) { delete = true; - u = unit_new(m, sizeof(Device)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Device), e, &u); if (r < 0) goto fail; diff --git a/src/core/mount.c b/src/core/mount.c index 03e5ea1376..0641621d8f 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1404,11 +1404,7 @@ static int mount_setup_unit( if (!u) { delete = true; - u = unit_new(m, sizeof(Mount)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Mount), e, &u); if (r < 0) goto fail; @@ -1614,16 +1610,9 @@ static void synthesize_root_mount(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_MOUNT); if (!u) { - u = unit_new(m, sizeof(Mount)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_ROOT_MOUNT); + r = unit_new_for_name(m, sizeof(Mount), SPECIAL_ROOT_MOUNT, &u); if (r < 0) { - unit_free(u); - log_error_errno(r, "Failed to add the " SPECIAL_ROOT_MOUNT " name: %m"); + log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m"); return; } } diff --git a/src/core/scope.c b/src/core/scope.c index 77f3fdc2aa..d6e1f8e392 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -563,16 +563,9 @@ static void scope_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_INIT_SCOPE); if (!u) { - u = unit_new(m, sizeof(Scope)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_INIT_SCOPE); + r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u); if (r < 0) { - unit_free(u); - log_error_errno(r, "Failed to add the " SPECIAL_INIT_SCOPE " name: %m"); + log_error_errno(r, "Failed to allocate the special " SPECIAL_INIT_SCOPE " unit: %m"); return; } } diff --git a/src/core/slice.c b/src/core/slice.c index c505fa1916..ed5d3fd701 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -299,16 +299,9 @@ static void slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { - u = unit_new(m, sizeof(Slice)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_ROOT_SLICE); + r = unit_new_for_name(m, sizeof(Slice), SPECIAL_ROOT_SLICE, &u); if (r < 0) { - unit_free(u); - log_error_errno(r, "Failed to add the " SPECIAL_ROOT_SLICE " name: %m"); + log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_SLICE " unit: %m"); return; } } diff --git a/src/core/swap.c b/src/core/swap.c index b592abb9fb..2228a254bb 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -381,11 +381,7 @@ static int swap_setup_unit( if (!u) { delete = true; - u = unit_new(m, sizeof(Swap)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Swap), e, &u); if (r < 0) goto fail; diff --git a/src/core/unit.c b/src/core/unit.c index 8d74b44f73..068d319206 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -109,6 +109,24 @@ Unit *unit_new(Manager *m, size_t size) { return u; } +int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) { + Unit *u; + int r; + + u = unit_new(m, size); + if (!u) + return -ENOMEM; + + r = unit_add_name(u, name); + if (r < 0) { + unit_free(u); + return r; + } + + *ret = u; + return r; +} + bool unit_has_name(Unit *u, const char *name) { assert(u); assert(name); diff --git a/src/core/unit.h b/src/core/unit.h index e524553ed3..899cd62c92 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -481,6 +481,7 @@ DEFINE_CAST(SCOPE, Scope); Unit *unit_new(Manager *m, size_t size); void unit_free(Unit *u); +int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret); int unit_add_name(Unit *u, const char *name); int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); -- cgit v1.2.3-54-g00ecf