]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cgroup: when we fail to clean up a cgroup, let's ask PID 1 for help
authorLennart Poettering <lennart@poettering.net>
Tue, 12 Nov 2024 11:03:05 +0000 (12:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 8 Jan 2025 14:27:25 +0000 (15:27 +0100)
src/core/cgroup.c

index 345dc5cfbbdb098c666e585c7f86ea2c79de90ee..e959b307c6767f43ee217dcccd52ae9a1c769f1c 100644 (file)
@@ -3610,6 +3610,51 @@ static bool unit_maybe_release_cgroup(Unit *u) {
         return false;
 }
 
+static int unit_prune_cgroup_via_bus(Unit *u) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(u);
+        assert(u->manager);
+
+        if (MANAGER_IS_SYSTEM(u->manager))
+                return -EINVAL;
+
+        if (!u->manager->system_bus)
+                return -EIO;
+
+        CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+        if (!crt || !crt->cgroup_path)
+                return -EOWNERDEAD;
+
+        /* Determine this unit's cgroup path relative to our cgroup root */
+        const char *pp = path_startswith(crt->cgroup_path, u->manager->cgroup_root);
+        if (!pp)
+                return -EINVAL;
+
+        _cleanup_free_ char *absolute = NULL;
+        if (!path_is_absolute(pp)) { /* RemoveSubgroupFromUnit() wants an absolute path */
+                absolute = strjoin("/", pp);
+                if (!absolute)
+                        return -ENOMEM;
+
+                pp = absolute;
+        }
+
+        r = bus_call_method(u->manager->system_bus,
+                            bus_systemd_mgr,
+                            "RemoveSubgroupFromUnit",
+                            &error, NULL,
+                            "sst",
+                            NULL /* empty unit name means client's unit, i.e. us */,
+                            pp,
+                            (uint64_t) 0);
+        if (r < 0)
+                return log_unit_debug_errno(u, r, "Failed to trim cgroup via the bus: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
 void unit_prune_cgroup(Unit *u) {
         bool is_root_slice;
         int r;
@@ -3641,13 +3686,21 @@ void unit_prune_cgroup(Unit *u) {
         is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
 
         r = cg_trim_everywhere(u->manager->cgroup_supported, crt->cgroup_path, !is_root_slice);
-        if (r < 0)
-                /* One reason we could have failed here is, that the cgroup still contains a process.
-                 * However, if the cgroup becomes removable at a later time, it might be removed when
-                 * the containing slice is stopped. So even if we failed now, this unit shouldn't assume
-                 * that the cgroup is still realized the next time it is started. Do not return early
-                 * on error, continue cleanup. */
-                log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path));
+        if (r < 0) {
+                int k = unit_prune_cgroup_via_bus(u);
+
+                if (k >= 0)
+                        log_unit_debug_errno(u, r, "Failed to destroy cgroup %s on our own (%m), but worked when talking to PID 1.", empty_to_root(crt->cgroup_path));
+                else {
+                        /* One reason we could have failed here is, that the cgroup still contains a process.
+                         * However, if the cgroup becomes removable at a later time, it might be removed when
+                         * the containing slice is stopped. So even if we failed now, this unit shouldn't
+                         * assume that the cgroup is still realized the next time it is started. Do not
+                         * return early on error, continue cleanup. */
+                        log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r,
+                                            "Failed to destroy cgroup %s, ignoring: %m", empty_to_root(crt->cgroup_path));
+                }
+        }
 
         if (is_root_slice)
                 return;