]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/unit.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / core / unit.c
index 4a5fa4005cac22afdd4d5bd6e5fa2d0dda1bee7f..9ee50d38823b092f583261d3f273e1bac6a48bf2 100644 (file)
@@ -128,7 +128,7 @@ Unit *unit_new(Manager *m, size_t size) {
 }
 
 int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) {
-        Unit *u;
+        _cleanup_(unit_freep) Unit *u = NULL;
         int r;
 
         u = unit_new(m, size);
@@ -136,12 +136,11 @@ int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) {
                 return -ENOMEM;
 
         r = unit_add_name(u, name);
-        if (r < 0) {
-                unit_free(u);
+        if (r < 0)
                 return r;
-        }
 
-        *ret = u;
+        *ret = TAKE_PTR(u);
+
         return r;
 }
 
@@ -186,7 +185,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);
@@ -267,13 +266,11 @@ int unit_add_name(Unit *u, const char *text) {
         if (u->type == _UNIT_TYPE_INVALID) {
                 u->type = t;
                 u->id = s;
-                u->instance = i;
+                u->instance = TAKE_PTR(i);
 
                 LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u);
 
                 unit_init(u);
-
-                i = NULL;
         }
 
         s = NULL;
@@ -336,20 +333,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 +361,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 +393,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 +429,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 +607,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 +635,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 +644,27 @@ 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);
+        if (u->in_target_deps_queue)
+                LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
 
-        if (!MANAGER_IS_RELOADING(u->manager))
-                unit_unlink_state_files(u);
+        safe_close(u->ip_accounting_ingress_map_fd);
+        safe_close(u->ip_accounting_egress_map_fd);
 
-        unit_unref_uid_gid(u, false);
+        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);
 
-        (void) manager_update_failed_units(u->manager, u, false);
-        set_remove(u->manager->startup_units, u);
+        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);
+
+        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);
 }
 
@@ -708,10 +710,8 @@ static int set_complete_move(Set **s, Set **other) {
 
         if (*s)
                 return set_move(*s, *other);
-        else {
-                *s = *other;
-                *other = NULL;
-        }
+        else
+                *s = TAKE_PTR(*other);
 
         return 0;
 }
@@ -725,10 +725,8 @@ static int hashmap_complete_move(Hashmap **s, Hashmap **other) {
 
         if (*s)
                 return hashmap_move(*s, *other);
-        else {
-                *s = *other;
-                *other = NULL;
-        }
+        else
+                *s = TAKE_PTR(*other);
 
         return 0;
 }
@@ -896,8 +894,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 +1117,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 +1135,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),
@@ -1341,6 +1339,18 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
         return unit_load_dropin(unit_follow_merge(u));
 }
 
+void unit_add_to_target_deps_queue(Unit *u) {
+        Manager *m = u->manager;
+
+        assert(u);
+
+        if (u->in_target_deps_queue)
+                return;
+
+        LIST_PREPEND(target_deps_queue, m->target_deps_queue, u);
+        u->in_target_deps_queue = true;
+}
+
 int unit_add_default_target_dependency(Unit *u, Unit *target) {
         assert(u);
         assert(target);
@@ -1367,35 +1377,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
         return unit_add_dependency(target, UNIT_AFTER, u, true, UNIT_DEPENDENCY_DEFAULT);
 }
 
-static int unit_add_target_dependencies(Unit *u) {
-
-        static const UnitDependency deps[] = {
-                UNIT_REQUIRED_BY,
-                UNIT_REQUISITE_OF,
-                UNIT_WANTED_BY,
-                UNIT_BOUND_BY
-        };
-
-        unsigned k;
-        int r = 0;
-
-        assert(u);
-
-        for (k = 0; k < ELEMENTSOF(deps); k++) {
-                Unit *target;
-                Iterator i;
-                void *v;
-
-                HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]], i) {
-                        r = unit_add_default_target_dependency(u, target);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        return r;
-}
-
 static int unit_add_slice_dependencies(Unit *u) {
         UnitDependencyMask mask;
         assert(u);
@@ -1524,10 +1505,7 @@ int unit_load(Unit *u) {
         }
 
         if (u->load_state == UNIT_LOADED) {
-
-                r = unit_add_target_dependencies(u);
-                if (r < 0)
-                        goto fail;
+                unit_add_to_target_deps_queue(u);
 
                 r = unit_add_slice_dependencies(u);
                 if (r < 0)
@@ -1697,7 +1675,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 +2304,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 +2436,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 +2450,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 +2479,16 @@ 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 +2942,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;
 }
 
@@ -3965,7 +3930,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;
@@ -3986,30 +3951,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) {
@@ -4027,8 +3994,7 @@ static int user_from_unit_name(Unit *u, char **ret) {
                 return r;
 
         if (valid_user_group_name(n)) {
-                *ret = n;
-                n = NULL;
+                *ret = TAKE_PTR(n);
                 return 0;
         }
 
@@ -4230,7 +4196,7 @@ char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) {
 char* unit_concat_strv(char **l, UnitWriteFlags flags) {
         _cleanup_free_ char *result = NULL;
         size_t n = 0, allocated = 0;
-        char **i, *ret;
+        char **i;
 
         /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a
          * way suitable for ExecStart= stanzas */
@@ -4265,10 +4231,7 @@ char* unit_concat_strv(char **l, UnitWriteFlags flags) {
 
         result[n] = 0;
 
-        ret = result;
-        result = NULL;
-
-        return ret;
+        return TAKE_PTR(result);
 }
 
 int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const char *data) {
@@ -4541,22 +4504,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) {
@@ -4579,7 +4535,8 @@ int unit_kill_context(
 }
 
 int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) {
-        char prefix[strlen(path) + 1], *p;
+        _cleanup_free_ char *p = NULL;
+        char *prefix;
         UnitDependencyInfo di;
         int r;
 
@@ -4594,7 +4551,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;
 
@@ -4602,36 +4559,32 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
         if (!p)
                 return -ENOMEM;
 
-        path_kill_slashes(p);
+        path = path_kill_slashes(p);
 
-        if (!path_is_normalized(p)) {
-                free(p);
+        if (!path_is_normalized(path))
                 return -EPERM;
-        }
 
-        if (hashmap_contains(u->requires_mounts_for, p)) {
-                free(p);
+        if (hashmap_contains(u->requires_mounts_for, path))
                 return 0;
-        }
 
         di = (UnitDependencyInfo) {
                 .origin_mask = mask
         };
 
-        r = hashmap_put(u->requires_mounts_for, p, di.data);
-        if (r < 0) {
-                free(p);
+        r = hashmap_put(u->requires_mounts_for, path, di.data);
+        if (r < 0)
                 return r;
-        }
+        p = NULL;
 
-        PATH_FOREACH_PREFIX_MORE(prefix, p) {
+        prefix = alloca(strlen(path) + 1);
+        PATH_FOREACH_PREFIX_MORE(prefix, path) {
                 Set *x;
 
                 x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
                 if (!x) {
-                        char *q;
+                        _cleanup_free_ char *q = NULL;
 
-                        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;
 
@@ -4640,17 +4593,15 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
                                 return -ENOMEM;
 
                         x = set_new(NULL);
-                        if (!x) {
-                                free(q);
+                        if (!x)
                                 return -ENOMEM;
-                        }
 
                         r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
                         if (r < 0) {
-                                free(q);
                                 set_free(x);
                                 return r;
                         }
+                        q = NULL;
                 }
 
                 r = set_put(x, u);
@@ -4726,7 +4677,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);
@@ -5006,8 +4957,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) {
@@ -5373,6 +5332,34 @@ const char *unit_label_path(Unit *u) {
         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",