From 2c7792cb12cfb20a3da819228cd244f648b186e0 Mon Sep 17 00:00:00 2001 From: mayankpande88 Date: Thu, 8 Oct 2026 13:29:10 +0530 Subject: [PATCH] fix: cache root-cgroup processes as ignored again, only /init.scope is skipped #374 stopped caching every pid whose cgroup Id is empty, which covers the root cgroup as well as /init.scope. On hosts without systemd, daemons can run in the root cgroup, and each of their connect, listen and file-open events then re-read /proc//cgroup. Cgroup now records whether the process is in /init.scope, and only those pids skip the ignore cache. Also drop the inline cleanup in getOrCreateContainer: it checked the entry it had just written, so it never deleted anything. --- cgroup/cgroup.go | 15 ++++++++++++++- cgroup/cgroup_test.go | 22 +++++++++++++++------- containers/registry.go | 10 +++------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/cgroup/cgroup.go b/cgroup/cgroup.go index fbad1c63..5e97980d 100644 --- a/cgroup/cgroup.go +++ b/cgroup/cgroup.go @@ -67,6 +67,16 @@ type Cgroup struct { ContainerId string subsystems map[string]string + + // initScope: the process is in systemd's /init.scope, where systemd + // forks a unit's process before moving it to the unit's cgroup. + initScope bool +} + +// InitScope reports whether the process is in systemd's /init.scope. Its Id +// is empty then, as for the root cgroup. +func (cg *Cgroup) InitScope() bool { + return cg.initScope } func (cg *Cgroup) getId() string { @@ -139,7 +149,10 @@ func NewFromProcessCgroupFile(filePath string) (*Cgroup, error) { } p := path.Join(baseCgroupPath, cgPath) switch p { - case "/", "/init.scope": + case "/init.scope": + cg.initScope = true + continue + case "/": continue } cg.subsystems[cgType] = p diff --git a/cgroup/cgroup_test.go b/cgroup/cgroup_test.go index d9145a07..c518c32c 100644 --- a/cgroup/cgroup_test.go +++ b/cgroup/cgroup_test.go @@ -238,16 +238,24 @@ func TestContainerByCgroup(t *testing.T) { as.Nil(err) } -// The registry relies on a process in systemd's /init.scope, or in the root -// cgroup, having an empty Id: it is not cached as ignored, so its exec after -// systemd moves it to a unit's cgroup is seen. +// The registry does not cache a process in systemd's /init.scope as ignored, +// so its exec after systemd moves it to a unit's cgroup is seen. A process in +// the root cgroup (hosts without systemd) is cached as usual. func TestInitScopeAndRootHaveEmptyId(t *testing.T) { - for _, content := range []string{"0::/init.scope\n", "0::/\n"} { + for _, c := range []struct { + content string + initScope bool + }{ + {"0::/init.scope\n", true}, + {"1:name=systemd:/init.scope\n2:cpu,cpuacct:/\n", true}, + {"0::/\n", false}, + } { f := path.Join(t.TempDir(), "cgroup") - require.NoError(t, os.WriteFile(f, []byte(content), 0644)) + require.NoError(t, os.WriteFile(f, []byte(c.content), 0644)) cg, err := NewFromProcessCgroupFile(f) require.NoError(t, err) - assert.Equal(t, "", cg.Id, content) - assert.Equal(t, ContainerTypeStandaloneProcess, cg.ContainerType, content) + assert.Equal(t, "", cg.Id, c.content) + assert.Equal(t, ContainerTypeStandaloneProcess, cg.ContainerType, c.content) + assert.Equal(t, c.initScope, cg.InitScope(), c.content) } } diff --git a/containers/registry.go b/containers/registry.go index 96e85255..4366c700 100644 --- a/containers/registry.go +++ b/containers/registry.go @@ -643,19 +643,15 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container { // systemd forks a unit's process inside its own /init.scope and // moves it to the unit's cgroup before exec. Caching the pid as // ignored here would drop that exec, and a unit that does nothing - // else would never be detected. /init.scope and the root cgroup - // both parse to an empty Id (cgroup.go skips them). - if cg.Id == "" && pid != 1 { + // else would never be detected. Processes in the root cgroup (hosts + // without systemd) are cached as before. + if cg.InitScope() && pid != 1 { klog.V(5).InfoS("ignoring without persisting", "cg", cg.Id, "pid", pid) } else { klog.V(5).InfoS("ignoring", "cg", cg.Id, "pid", pid) r.containerLock.Lock() t := time.Now() r.containersByPidIgnored[pid] = &t - // Clean up stale ignored PIDs while we have the lock - if oldT := r.containersByPidIgnored[pid]; oldT != nil && time.Since(*oldT) >= IgnoredContainersCacheTTL { - delete(r.containersByPidIgnored, pid) - } r.containerLock.Unlock() } return nil