From a35ed8f72ef4915408f0ccbbc951cc1cd87b25ed Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 12 Aug 2026 06:53:14 +0200 Subject: [PATCH 1/2] cgroups: non-root service cannot create child cgroups The kernel delegation docs require write access on the directory so the delegatee can mkdir() children. Signed-off-by: Joachim Wiberg --- src/cgroup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cgroup.c b/src/cgroup.c index 58e68875..378d5b90 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -339,6 +339,10 @@ static int cgroup_create(const char *group, const char *name, const char *cfg, NULL }; + /* non-root delegated services needs permission to mkdir() */ + if (chown(path, uid, gid)) + warn("Failed chown %s to %d:%d", path, uid, gid); + for (int i = 0; files[i]; i++) { snprintf(filepath, sizeof(filepath), "%s/%s", path, files[i]); if (chown(filepath, uid, gid)) From 1843c24cdafaa5a72a8ee4a52c4552fddfe852f7 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Wed, 12 Aug 2026 07:17:41 +0200 Subject: [PATCH 2/2] cgroup: move PID 1 out of the init/ group A cgroup holding processes cannot enable controllers for its children, so init/ had to stay a leaf. The hotplug helpers 10-hotplug.conf.in places there ended up in groups where cpu.weight and friends could never be set. Keeping PID 1 in the root cgroup makes init/ a domain like the others. It also unbreaks lxc-based runtimes: liblxc bases the container tree on PID 1's cgroup and only special-cases systemd's init.scope/, so under Finit it landed containers in init/, with no controllers available. Issue #497 Signed-off-by: Joachim Wiberg --- doc/config/cgroups.md | 4 +++- src/cgroup.c | 23 +++++------------------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/doc/config/cgroups.md b/doc/config/cgroups.md index 2e17d061..26bda2ac 100644 --- a/doc/config/cgroups.md +++ b/doc/config/cgroups.md @@ -42,7 +42,9 @@ the above three. We leave `init/` and `user/` as-is reducing weight of cgroup maint { cpu.weight = 100 } By default, the `system/` cgroup is selected for almost everything. The -`init/` cgroup is reserved for PID 1 itself and its closest relatives. +`init/` cgroup is reserved for the early boot helpers Finit drives +itself, like udev and mdev. PID 1 runs in the root cgroup, since a +cgroup holding processes cannot delegate controllers to its children. The `user/` cgroup is for local TTY logins spawned by getty. Joining a Cgroup diff --git a/src/cgroup.c b/src/cgroup.c index 378d5b90..79922e5e 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -414,9 +414,6 @@ int cgroup_service(const char *name, int pid, struct cgroup *cg, char *username, if (!strcmp(cg->name, "root")) return fnwrite(str("%d", pid), FINIT_CGPATH "/cgroup.procs"); - if (!strcmp(cg->name, "init")) - return fnwrite(str("%d", pid), FINIT_CGPATH "/init/cgroup.procs"); - snprintf(path, sizeof(path), "/sys/fs/cgroup/%s", cg->name); if (fisdir(path)) group = cg->name; @@ -818,7 +815,6 @@ int cgroup_del_svc(svc_t *svc, const char *name) return cgroup_del(path); } -/* the top-level init cgroup is a leaf, that's ensured in cgroup_init() */ void cgroup_config(void) { struct cg *cg; @@ -828,15 +824,12 @@ void cgroup_config(void) TAILQ_FOREACH(cg, &cgroups, link) { char path[256]; - int leaf = 0; if (!cg->active) continue; - if (!strcmp(cg->name, "init")) - leaf = 1; /* reserved */ snprintf(path, sizeof(path), "%s/%s", FINIT_CGPATH, cg->name); - group_init(path, leaf, cg->cfg); + group_init(path, 0, cg->cfg); strlcat(path, "/cgroup.events", sizeof(path)); iwatch_add(&iw_cgroup, path, 0); @@ -940,20 +933,14 @@ void cgroup_init(uev_ctx_t *ctx) goto abort; } - /* Default (protected) groups, PID 1, services, and user/login processes */ + /* + * Protected groups. PID 1 stays in the root cgroup, a group with + * processes in it cannot delegate controllers to its children. + */ cgroup_add("init", "cpu.weight:100", 1); cgroup_add("system", "cpu.weight:9800", 1); cgroup_add("user", "cpu.weight:100", 1); cgroup_config(); - - /* Move ourselves to init (best effort, otherwise run in 'root' group */ - if (fnwrite("1", FINIT_CGPATH "/init/cgroup.procs")) { - err(1, "Failed moving PID 1 to cgroup %s", FINIT_CGPATH "/init"); - uev_io_stop(&cgw); - iwatch_exit(&iw_cgroup); - close(fd); - goto abort; - } } /**