summaryrefslogtreecommitdiff
path: root/src/core/unit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/unit.c')
-rw-r--r--src/core/unit.c297
1 files changed, 157 insertions, 140 deletions
diff --git a/src/core/unit.c b/src/core/unit.c
index 932f05baa2..c3056624ef 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -186,7 +186,7 @@ static void unit_init(Unit *u) {
exec_context_init(ec);
ec->keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
- EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
+ EXEC_KEYRING_SHARED : EXEC_KEYRING_INHERIT;
}
kc = unit_get_kill_context(u);
@@ -336,20 +336,25 @@ int unit_set_description(Unit *u, const char *description) {
return 0;
}
-bool unit_check_gc(Unit *u) {
+bool unit_may_gc(Unit *u) {
UnitActiveState state;
int r;
assert(u);
- /* Checks whether the unit is ready to be unloaded for garbage collection. Returns true, when the unit shall
- * stay around, false if there's no reason to keep it loaded. */
+ /* Checks whether the unit is ready to be unloaded for garbage collection.
+ * Returns true when the unit may be collected, and false if there's some
+ * reason to keep it loaded.
+ *
+ * References from other units are *not* checked here. Instead, this is done
+ * in unit_gc_sweep(), but using markers to properly collect dependency loops.
+ */
if (u->job)
- return true;
+ return false;
if (u->nop_job)
- return true;
+ return false;
state = unit_active_state(u);
@@ -359,26 +364,23 @@ bool unit_check_gc(Unit *u) {
UNIT_VTABLE(u)->release_resources(u);
if (u->perpetual)
- return true;
-
- if (u->refs)
- return true;
+ return false;
if (sd_bus_track_count(u->bus_track) > 0)
- return true;
+ return false;
/* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */
switch (u->collect_mode) {
case COLLECT_INACTIVE:
if (state != UNIT_INACTIVE)
- return true;
+ return false;
break;
case COLLECT_INACTIVE_OR_FAILED:
if (!IN_SET(state, UNIT_INACTIVE, UNIT_FAILED))
- return true;
+ return false;
break;
@@ -394,14 +396,13 @@ bool unit_check_gc(Unit *u) {
if (r < 0)
log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
if (r <= 0)
- return true;
+ return false;
}
- if (UNIT_VTABLE(u)->check_gc)
- if (UNIT_VTABLE(u)->check_gc(u))
- return true;
+ if (UNIT_VTABLE(u)->may_gc && !UNIT_VTABLE(u)->may_gc(u))
+ return false;
- return false;
+ return true;
}
void unit_add_to_load_queue(Unit *u) {
@@ -431,7 +432,7 @@ void unit_add_to_gc_queue(Unit *u) {
if (u->in_gc_queue || u->in_cleanup_queue)
return;
- if (unit_check_gc(u))
+ if (!unit_may_gc(u))
return;
LIST_PREPEND(gc_queue, u->manager->gc_unit_queue, u);
@@ -609,6 +610,25 @@ void unit_free(Unit *u) {
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->dependencies[d]);
+ if (u->on_console)
+ manager_unref_console(u->manager);
+
+ unit_release_cgroup(u);
+
+ if (!MANAGER_IS_RELOADING(u->manager))
+ unit_unlink_state_files(u);
+
+ unit_unref_uid_gid(u, false);
+
+ (void) manager_update_failed_units(u->manager, u, false);
+ set_remove(u->manager->startup_units, u);
+
+ unit_unwatch_all_pids(u);
+
+ unit_ref_unset(&u->slice);
+ while (u->refs_by_target)
+ unit_ref_unset(u->refs_by_target);
+
if (u->type != _UNIT_TYPE_INVALID)
LIST_REMOVE(units_by_type, u->manager->units_by_type[u->type], u);
@@ -618,9 +638,6 @@ void unit_free(Unit *u) {
if (u->in_dbus_queue)
LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
- if (u->in_cleanup_queue)
- LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
-
if (u->in_gc_queue)
LIST_REMOVE(gc_queue, u->manager->gc_unit_queue, u);
@@ -630,18 +647,24 @@ void unit_free(Unit *u) {
if (u->in_cgroup_empty_queue)
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
- if (u->on_console)
- manager_unref_console(u->manager);
+ if (u->in_cleanup_queue)
+ LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
- unit_release_cgroup(u);
+ safe_close(u->ip_accounting_ingress_map_fd);
+ safe_close(u->ip_accounting_egress_map_fd);
- if (!MANAGER_IS_RELOADING(u->manager))
- unit_unlink_state_files(u);
+ safe_close(u->ipv4_allow_map_fd);
+ safe_close(u->ipv6_allow_map_fd);
+ safe_close(u->ipv4_deny_map_fd);
+ safe_close(u->ipv6_deny_map_fd);
- unit_unref_uid_gid(u, false);
+ bpf_program_unref(u->ip_bpf_ingress);
+ bpf_program_unref(u->ip_bpf_ingress_installed);
+ bpf_program_unref(u->ip_bpf_egress);
+ bpf_program_unref(u->ip_bpf_egress_installed);
- (void) manager_update_failed_units(u->manager, u, false);
- set_remove(u->manager->startup_units, u);
+ condition_free_list(u->conditions);
+ condition_free_list(u->asserts);
free(u->description);
strv_free(u->documentation);
@@ -654,29 +677,8 @@ void unit_free(Unit *u) {
set_free_free(u->names);
- unit_unwatch_all_pids(u);
-
- condition_free_list(u->conditions);
- condition_free_list(u->asserts);
-
free(u->reboot_arg);
- unit_ref_unset(&u->slice);
-
- while (u->refs)
- unit_ref_unset(u->refs);
-
- safe_close(u->ip_accounting_ingress_map_fd);
- safe_close(u->ip_accounting_egress_map_fd);
-
- safe_close(u->ipv4_allow_map_fd);
- safe_close(u->ipv6_allow_map_fd);
- safe_close(u->ipv4_deny_map_fd);
- safe_close(u->ipv6_deny_map_fd);
-
- bpf_program_unref(u->ip_bpf_ingress);
- bpf_program_unref(u->ip_bpf_egress);
-
free(u);
}
@@ -896,8 +898,8 @@ int unit_merge(Unit *u, Unit *other) {
return r;
/* Redirect all references */
- while (other->refs)
- unit_ref_set(other->refs, u);
+ while (other->refs_by_target)
+ unit_ref_set(other->refs_by_target, other->refs_by_target->source, u);
/* Merge dependencies */
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
@@ -1119,7 +1121,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
"%s\tInactive Enter Timestamp: %s\n"
- "%s\tGC Check Good: %s\n"
+ "%s\tMay GC: %s\n"
"%s\tNeed Daemon Reload: %s\n"
"%s\tTransient: %s\n"
"%s\tPerpetual: %s\n"
@@ -1137,7 +1139,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
- prefix, yes_no(unit_check_gc(u)),
+ prefix, yes_no(unit_may_gc(u)),
prefix, yes_no(unit_need_daemon_reload(u)),
prefix, yes_no(u->transient),
prefix, yes_no(u->perpetual),
@@ -1697,7 +1699,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- xsprintf(buf, format, unit_description(u));
+ (void) snprintf(buf, sizeof buf, format, unit_description(u));
REENABLE_WARNING;
mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
@@ -2326,18 +2328,16 @@ static void unit_update_on_console(Unit *u) {
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
- Manager *m;
bool unexpected;
+ Manager *m;
assert(u);
assert(os < _UNIT_ACTIVE_STATE_MAX);
assert(ns < _UNIT_ACTIVE_STATE_MAX);
- /* Note that this is called for all low-level state changes,
- * even if they might map to the same high-level
- * UnitActiveState! That means that ns == os is an expected
- * behavior here. For example: if a mount point is remounted
- * this function will be called too! */
+ /* Note that this is called for all low-level state changes, even if they might map to the same high-level
+ * UnitActiveState! That means that ns == os is an expected behavior here. For example: if a mount point is
+ * remounted this function will be called too! */
m = u->manager;
@@ -2460,15 +2460,8 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
}
- /* Some names are special */
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
- if (unit_has_name(u, SPECIAL_DBUS_SERVICE))
- /* The bus might have just become available,
- * hence try to connect to it, if we aren't
- * yet connected. */
- bus_init(m, true);
-
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
!MANAGER_IS_RELOADING(m)) {
@@ -2481,8 +2474,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
manager_send_unit_plymouth(m, u);
} else {
- /* We don't care about D-Bus going down here, since we'll get an asynchronous notification for it
- * anyway. */
if (UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os)
@@ -2512,17 +2503,15 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
manager_recheck_journal(m);
+ manager_recheck_dbus(m);
unit_trigger_notify(u);
if (!MANAGER_IS_RELOADING(u->manager)) {
- /* Maybe we finished startup and are now ready for
- * being stopped because unneeded? */
+ /* Maybe we finished startup and are now ready for being stopped because unneeded? */
unit_check_unneeded(u);
- /* Maybe we finished startup, but something we needed
- * has vanished? Let's die then. (This happens when
- * something BindsTo= to a Type=oneshot unit, as these
- * units go directly from starting to inactive,
+ /* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when
+ * something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive,
* without ever entering started.) */
unit_check_binds_to(u);
@@ -2976,8 +2965,7 @@ int unit_set_slice(Unit *u, Unit *slice) {
if (UNIT_ISSET(u->slice) && u->cgroup_realized)
return -EBUSY;
- unit_ref_unset(&u->slice);
- unit_ref_set(&u->slice, slice);
+ unit_ref_set(&u->slice, u, slice);
return 1;
}
@@ -3176,18 +3164,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
assert(fds);
if (unit_can_serialize(u)) {
- ExecRuntime *rt;
-
r = UNIT_VTABLE(u)->serialize(u, f, fds);
if (r < 0)
return r;
-
- rt = unit_get_exec_runtime(u);
- if (rt) {
- r = exec_runtime_serialize(u, rt, f, fds);
- if (r < 0)
- return r;
- }
}
dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp);
@@ -3333,18 +3312,12 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *f
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
- ExecRuntime **rt = NULL;
- size_t offset;
int r;
assert(u);
assert(f);
assert(fds);
- offset = UNIT_VTABLE(u)->exec_runtime_offset;
- if (offset > 0)
- rt = (ExecRuntime**) ((uint8_t*) u + offset);
-
for (;;) {
char line[LINE_MAX], *l, *v;
CGroupIPAccountingMetric m;
@@ -3604,18 +3577,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
}
if (unit_can_serialize(u)) {
- if (rt) {
- r = exec_runtime_deserialize_item(u, rt, l, v, fds);
- if (r < 0) {
- log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
- continue;
- }
-
- /* Returns positive if key was handled by the call */
- if (r > 0)
- continue;
+ r = exec_runtime_deserialize_compat(u, l, v, fds);
+ if (r < 0) {
+ log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
+ continue;
}
+ /* Returns positive if key was handled by the call */
+ if (r > 0)
+ continue;
+
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
@@ -3982,7 +3953,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
r = unit_file_get_state(
u->manager->unit_file_scope,
NULL,
- basename(u->fragment_path),
+ u->id,
&u->unit_file_state);
if (r < 0)
u->unit_file_state = UNIT_FILE_BAD;
@@ -4003,30 +3974,32 @@ int unit_get_unit_file_preset(Unit *u) {
return u->unit_file_preset;
}
-Unit* unit_ref_set(UnitRef *ref, Unit *u) {
+Unit* unit_ref_set(UnitRef *ref, Unit *source, Unit *target) {
assert(ref);
- assert(u);
+ assert(source);
+ assert(target);
- if (ref->unit)
+ if (ref->target)
unit_ref_unset(ref);
- ref->unit = u;
- LIST_PREPEND(refs, u->refs, ref);
- return u;
+ ref->source = source;
+ ref->target = target;
+ LIST_PREPEND(refs_by_target, target->refs_by_target, ref);
+ return target;
}
void unit_ref_unset(UnitRef *ref) {
assert(ref);
- if (!ref->unit)
+ if (!ref->target)
return;
/* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might
* be unreferenced now. */
- unit_add_to_gc_queue(ref->unit);
+ unit_add_to_gc_queue(ref->target);
- LIST_REMOVE(refs, ref->unit->refs, ref);
- ref->unit = NULL;
+ LIST_REMOVE(refs_by_target, ref->target->refs_by_target, ref);
+ ref->source = ref->target = NULL;
}
static int user_from_unit_name(Unit *u, char **ret) {
@@ -4558,22 +4531,15 @@ int unit_kill_context(
} else if (r > 0) {
- /* FIXME: For now, on the legacy hierarchy, we
- * will not wait for the cgroup members to die
- * if we are running in a container or if this
- * is a delegation unit, simply because cgroup
- * notification is unreliable in these
- * cases. It doesn't work at all in
- * containers, and outside of containers it
- * can be confused easily by left-over
- * directories in the cgroup — which however
- * should not exist in non-delegated units. On
- * the unified hierarchy that's different,
- * there we get proper events. Hence rely on
- * them. */
+ /* FIXME: For now, on the legacy hierarchy, we will not wait for the cgroup members to die if
+ * we are running in a container or if this is a delegation unit, simply because cgroup
+ * notification is unreliable in these cases. It doesn't work at all in containers, and outside
+ * of containers it can be confused easily by left-over directories in the cgroup — which
+ * however should not exist in non-delegated units. On the unified hierarchy that's different,
+ * there we get proper events. Hence rely on them. */
if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
- (detect_container() == 0 && !UNIT_CGROUP_BOOL(u, delegate)))
+ (detect_container() == 0 && !unit_cgroup_delegate(u)))
wait_for_exit = true;
if (send_sighup) {
@@ -4611,7 +4577,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
if (!path_is_absolute(path))
return -EINVAL;
- r = hashmap_ensure_allocated(&u->requires_mounts_for, &string_hash_ops);
+ r = hashmap_ensure_allocated(&u->requires_mounts_for, &path_hash_ops);
if (r < 0)
return r;
@@ -4648,7 +4614,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
if (!x) {
char *q;
- r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &string_hash_ops);
+ r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &path_hash_ops);
if (r < 0)
return r;
@@ -4684,6 +4650,7 @@ int unit_setup_exec_runtime(Unit *u) {
Unit *other;
Iterator i;
void *v;
+ int r;
offset = UNIT_VTABLE(u)->exec_runtime_offset;
assert(offset > 0);
@@ -4695,15 +4662,12 @@ int unit_setup_exec_runtime(Unit *u) {
/* Try to get it from somebody else */
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) {
-
- *rt = unit_get_exec_runtime(other);
- if (*rt) {
- exec_runtime_ref(*rt);
- return 0;
- }
+ r = exec_runtime_acquire(u->manager, NULL, other->id, false, rt);
+ if (r == 1)
+ return 1;
}
- return exec_runtime_make(rt, unit_get_exec_context(u), u->id);
+ return exec_runtime_acquire(u->manager, unit_get_exec_context(u), u->id, true, rt);
}
int unit_setup_dynamic_creds(Unit *u) {
@@ -4745,7 +4709,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
assert(where);
r = dir_is_empty(where);
- if (r > 0)
+ if (r > 0 || r == -ENOTDIR)
return;
if (r < 0) {
log_unit_warning_errno(u, r, "Failed to check directory %s: %m", where);
@@ -5025,8 +4989,16 @@ void unit_set_exec_params(Unit *u, ExecParameters *p) {
assert(u);
assert(p);
+ /* Copy parameters from manager */
+ p->environment = u->manager->environment;
+ p->confirm_spawn = manager_get_confirm_spawn(u->manager);
+ p->cgroup_supported = u->manager->cgroup_supported;
+ p->prefix = u->manager->prefix;
+ SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(u->manager));
+
+ /* Copy paramaters from unit */
p->cgroup_path = u->cgroup_path;
- SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, UNIT_CGROUP_BOOL(u, delegate));
+ SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u));
}
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
@@ -5375,6 +5347,51 @@ bool unit_needs_console(Unit *u) {
return exec_context_may_touch_console(ec);
}
+const char *unit_label_path(Unit *u) {
+ const char *p;
+
+ /* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
+ * when validating access checks. */
+
+ p = u->source_path ?: u->fragment_path;
+ if (!p)
+ return NULL;
+
+ /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
+ if (path_equal(p, "/dev/null"))
+ return NULL;
+
+ return p;
+}
+
+int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
+ int r;
+
+ assert(u);
+
+ /* Checks whether the specified PID is generally good for attaching, i.e. a valid PID, not our manager itself,
+ * and not a kernel thread either */
+
+ /* First, a simple range check */
+ if (!pid_is_valid(pid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process identifier " PID_FMT " is not valid.", pid);
+
+ /* Some extra safety check */
+ if (pid == 1 || pid == getpid_cached())
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager processs, refusing.", pid);
+
+ /* Don't even begin to bother with kernel threads */
+ r = is_kernel_thread(pid);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "Process with ID " PID_FMT " does not exist.", pid);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to determine whether process " PID_FMT " is a kernel thread: %m", pid);
+ if (r > 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a kernel thread, refusing.", pid);
+
+ return 0;
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",