From 7d6848f883ff80884276b6deaf9a00f3a89cbc17 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 16 Jul 2025 11:10:55 -0700 Subject: [PATCH 1/5] script/setup_rootless.sh: chown nit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following warning (seen on Fedora 42 and Ubuntu 24.04): + sudo chown -R rootless.rootless /home/rootless chown: warning: '.' should be ':': ‘rootless.rootless’ Signed-off-by: Kir Kolyshkin --- script/setup_rootless.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/setup_rootless.sh b/script/setup_rootless.sh index 842d2ecf16b..6ecdbc4d04f 100755 --- a/script/setup_rootless.sh +++ b/script/setup_rootless.sh @@ -12,4 +12,4 @@ ssh-keygen -t ecdsa -N "" -f "$HOME/.ssh/rootless.key" sudo mkdir -p -m 0700 /home/rootless/.ssh sudo cp "$HOME/.ssh/rootless.key" /home/rootless/.ssh/id_ecdsa sudo cp "$HOME/.ssh/rootless.key.pub" /home/rootless/.ssh/authorized_keys -sudo chown -R rootless.rootless /home/rootless +sudo chown -R rootless:rootless /home/rootless From b39e0d6468fe3438d9e88920a3f290f3223c58fd Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 15 Jul 2025 16:37:34 -0700 Subject: [PATCH 2/5] libct: factor out addIntoCgroup from setnsProcess.start Signed-off-by: Kir Kolyshkin --- libcontainer/process_linux.go | 52 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index d8acb275193..ba39ad9eed4 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -244,6 +244,34 @@ func (p *setnsProcess) setFinalCPUAffinity() error { return nil } +func (p *setnsProcess) addIntoCgroup() error { + for _, path := range p.cgroupPaths { + if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { + // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. + // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 + // Try to join the cgroup of InitProcessPid. + if cgroups.IsCgroup2UnifiedMode() && p.initProcessPid != 0 { + initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) + initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) + if initCgErr == nil { + if initCgPath, ok := initCg[""]; ok { + initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) + logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", + p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) + // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. + err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) + } + } + } + if err != nil { + return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) + } + } + } + + return nil +} + func (p *setnsProcess) start() (retErr error) { defer p.comm.closeParent() @@ -277,28 +305,8 @@ func (p *setnsProcess) start() (retErr error) { if err := p.execSetns(); err != nil { return fmt.Errorf("error executing setns process: %w", err) } - for _, path := range p.cgroupPaths { - if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { - // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. - // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 - // Try to join the cgroup of InitProcessPid. - if cgroups.IsCgroup2UnifiedMode() && p.initProcessPid != 0 { - initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) - initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) - if initCgErr == nil { - if initCgPath, ok := initCg[""]; ok { - initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) - logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", - p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) - // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. - err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) - } - } - } - if err != nil { - return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) - } - } + if err := p.addIntoCgroup(); err != nil { + return err } // Set final CPU affinity right after the process is moved into container's cgroup. if err := p.setFinalCPUAffinity(); err != nil { From 5560020cbbfcd25ec5b9cab17f094697fbfae162 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 25 Jul 2025 17:00:45 -0700 Subject: [PATCH 3/5] libct: split addIntoCgroup into V1 and V2 The main idea is to maintain the code separately (and eventually kill V1 implementation). Signed-off-by: Kir Kolyshkin --- libcontainer/process_linux.go | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index ba39ad9eed4..5b4b4def622 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -244,34 +244,50 @@ func (p *setnsProcess) setFinalCPUAffinity() error { return nil } -func (p *setnsProcess) addIntoCgroup() error { +func (p *setnsProcess) addIntoCgroupV1() error { for _, path := range p.cgroupPaths { if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { - // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. - // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 - // Try to join the cgroup of InitProcessPid. - if cgroups.IsCgroup2UnifiedMode() && p.initProcessPid != 0 { - initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) - initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) - if initCgErr == nil { - if initCgPath, ok := initCg[""]; ok { - initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) - logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", - p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) - // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. - err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) - } + return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) + } + } + + return nil +} + +func (p *setnsProcess) addIntoCgroupV2() error { + path := p.cgroupPaths[""] + if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { + // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. + // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 + // Try to join the cgroup of InitProcessPid. + if p.initProcessPid != 0 { + initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) + initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) + if initCgErr == nil { + if initCgPath, ok := initCg[""]; ok { + initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) + logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", + p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) + // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. + err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) } } - if err != nil { - return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) - } + } + if err != nil { + return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) } } return nil } +func (p *setnsProcess) addIntoCgroup() error { + if cgroups.IsCgroup2UnifiedMode() { + return p.addIntoCgroupV2() + } + return p.addIntoCgroupV1() +} + func (p *setnsProcess) start() (retErr error) { defer p.comm.closeParent() From 5730a141f1702dc60682620d4815e1cd194dda06 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 25 Jul 2025 17:19:00 -0700 Subject: [PATCH 4/5] libct: move exec sub-cgroup handling down the line Remove cgroupPaths field from struct setnsProcess, because: - we can get base cgroup paths from p.manager.GetPaths(); - we can get sub-cgroup paths from p.process.SubCgroupPaths. But mostly because we are going to need separate cgroup paths when adopting cgroups.AddPid. Signed-off-by: Kir Kolyshkin --- libcontainer/container_linux.go | 31 ------------------------- libcontainer/process_linux.go | 40 ++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 7f0c51f31cf..554ca9b6e50 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -7,7 +7,6 @@ import ( "io" "os" "os/exec" - "path" "path/filepath" "reflect" "strconv" @@ -655,40 +654,10 @@ func (c *Container) newSetnsProcess(p *Process, cmd *exec.Cmd, comm *processComm bootstrapData: data, container: c, }, - cgroupPaths: state.CgroupPaths, rootlessCgroups: c.config.RootlessCgroups, intelRdtPath: state.IntelRdtPath, initProcessPid: state.InitProcessPid, } - if len(p.SubCgroupPaths) > 0 { - if add, ok := p.SubCgroupPaths[""]; ok { - // cgroup v1: using the same path for all controllers. - // cgroup v2: the only possible way. - for k := range proc.cgroupPaths { - subPath := path.Join(proc.cgroupPaths[k], add) - if !strings.HasPrefix(subPath, proc.cgroupPaths[k]) { - return nil, fmt.Errorf("%s is not a sub cgroup path", add) - } - proc.cgroupPaths[k] = subPath - } - // cgroup v2: do not try to join init process's cgroup - // as a fallback (see (*setnsProcess).start). - proc.initProcessPid = 0 - } else { - // Per-controller paths. - for ctrl, add := range p.SubCgroupPaths { - if val, ok := proc.cgroupPaths[ctrl]; ok { - subPath := path.Join(val, add) - if !strings.HasPrefix(subPath, val) { - return nil, fmt.Errorf("%s is not a sub cgroup path", add) - } - proc.cgroupPaths[ctrl] = subPath - } else { - return nil, fmt.Errorf("unknown controller %s in SubCgroupPaths", ctrl) - } - } - } - } return proc, nil } diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 5b4b4def622..9e68022702f 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -6,12 +6,15 @@ import ( "errors" "fmt" "io" + "maps" "net" "os" "os/exec" + "path" "path/filepath" "runtime" "strconv" + "strings" "sync" "time" @@ -153,7 +156,6 @@ func (p *containerProcess) wait() (*os.ProcessState, error) { //nolint:unparam type setnsProcess struct { containerProcess - cgroupPaths map[string]string rootlessCgroups bool intelRdtPath string initProcessPid int @@ -245,7 +247,20 @@ func (p *setnsProcess) setFinalCPUAffinity() error { } func (p *setnsProcess) addIntoCgroupV1() error { - for _, path := range p.cgroupPaths { + paths := maps.Clone(p.manager.GetPaths()) + for ctrl, sub := range p.process.SubCgroupPaths { + base, ok := paths[ctrl] + if !ok { + return fmt.Errorf("unknown controller %s in SubCgroupPaths", ctrl) + } + cgPath := path.Join(base, sub) + if !strings.HasPrefix(cgPath, base) { + return fmt.Errorf("%s is not a sub cgroup path", sub) + } + paths[ctrl] = cgPath + } + + for _, path := range paths { if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) } @@ -255,19 +270,28 @@ func (p *setnsProcess) addIntoCgroupV1() error { } func (p *setnsProcess) addIntoCgroupV2() error { - path := p.cgroupPaths[""] - if err := cgroups.WriteCgroupProc(path, p.pid()); err != nil && !p.rootlessCgroups { + base := p.manager.Path("") + sub := "" + if p.process.SubCgroupPaths != nil { + sub = p.process.SubCgroupPaths[""] + } + cgPath := path.Join(base, sub) + if !strings.HasPrefix(cgPath, base) { + return fmt.Errorf("%s is not a sub cgroup path", sub) + } + + if err := cgroups.WriteCgroupProc(cgPath, p.pid()); err != nil && !p.rootlessCgroups { // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 - // Try to join the cgroup of InitProcessPid. - if p.initProcessPid != 0 { + // Try to join the cgroup of InitProcessPid, unless sub-cgroup is explicitly set. + if p.initProcessPid != 0 && sub == "" { initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid) initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile) if initCgErr == nil { if initCgPath, ok := initCg[""]; ok { initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) - logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)", - p.pid(), p.cgroupPaths, err, initCg, initCgDirpath) + logrus.Debugf("adding pid %d to cgroup %s failed (%v), attempting to join %s", + p.pid(), cgPath, err, initCgDirpath) // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) } From 37b5acc2d7af7a0a098e3f6543ee05c0bd4417fb Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 25 Jul 2025 17:34:35 -0700 Subject: [PATCH 5/5] libct: use manager.AddPid to add exec to cgroup The main benefit here is when we are using a systemd cgroup driver, we actually ask systemd to add a PID, rather than doing it ourselves. This way, we can add rootless exec PID to a cgroup. This requires newer opencontainers/cgroups and coreos/go-systemd. Signed-off-by: Kir Kolyshkin --- go.mod | 2 +- go.sum | 4 +-- libcontainer/container_linux_test.go | 4 +++ libcontainer/process_linux.go | 34 +++++++++++-------- tests/integration/exec.bats | 12 +++---- .../opencontainers/cgroups/cgroups.go | 5 +++ .../opencontainers/cgroups/fs/fs.go | 29 ++++++++++++++++ .../opencontainers/cgroups/fs2/fs2.go | 13 +++++++ .../opencontainers/cgroups/systemd/common.go | 15 ++++++++ .../opencontainers/cgroups/systemd/v1.go | 19 +++++++++++ .../opencontainers/cgroups/systemd/v2.go | 10 ++++++ vendor/modules.txt | 2 +- 12 files changed, 124 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index 978207c5397..6f1188b2847 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/moby/sys/user v0.4.0 github.com/moby/sys/userns v0.1.0 github.com/mrunalp/fileutils v0.5.1 - github.com/opencontainers/cgroups v0.0.4 + github.com/opencontainers/cgroups v0.0.5 github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0 github.com/opencontainers/selinux v1.12.0 github.com/seccomp/libseccomp-golang v0.11.1 diff --git a/go.sum b/go.sum index 8b3857e42ba..b72da610ad9 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q= github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/opencontainers/cgroups v0.0.4 h1:XVj8P/IHVms/j+7eh8ggdkTLAxjz84ZzuFyGoE28DR4= -github.com/opencontainers/cgroups v0.0.4/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= +github.com/opencontainers/cgroups v0.0.5 h1:DRITAqcOnY0uSBzIpt1RYWLjh5DPDiqUs4fY6Y0ktls= +github.com/opencontainers/cgroups v0.0.5/go.mod h1:oWVzJsKK0gG9SCRBfTpnn16WcGEqDI8PAcpMGbqWxcs= github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0 h1:RLn0YfUWkiqPGtgUANvJrcjIkCHGRl3jcz/c557M28M= github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go index e6bdd86ba2a..0d0dc4451d3 100644 --- a/libcontainer/container_linux_test.go +++ b/libcontainer/container_linux_test.go @@ -32,6 +32,10 @@ func (m *mockCgroupManager) Apply(pid int) error { return nil } +func (m *mockCgroupManager) AddPid(_ string, _ int) error { + return nil +} + func (m *mockCgroupManager) Set(_ *cgroups.Resources) error { return nil } diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 9e68022702f..852633d7dad 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -247,6 +247,18 @@ func (p *setnsProcess) setFinalCPUAffinity() error { } func (p *setnsProcess) addIntoCgroupV1() error { + if sub, ok := p.process.SubCgroupPaths[""]; ok || len(p.process.SubCgroupPaths) == 0 { + // Either same sub-cgroup for all paths, or no sub-cgroup. + err := p.manager.AddPid(sub, p.pid()) + if err != nil && !p.rootlessCgroups { + return fmt.Errorf("error adding pid %d to cgroups: %w", p.pid(), err) + } + return nil + } + + // Per-controller sub-cgroup paths. Not supported by AddPid (or systemd), + // so we have to calculate and check all sub-cgroup paths, and write + // directly to cgroupfs. paths := maps.Clone(p.manager.GetPaths()) for ctrl, sub := range p.process.SubCgroupPaths { base, ok := paths[ctrl] @@ -255,7 +267,7 @@ func (p *setnsProcess) addIntoCgroupV1() error { } cgPath := path.Join(base, sub) if !strings.HasPrefix(cgPath, base) { - return fmt.Errorf("%s is not a sub cgroup path", sub) + return fmt.Errorf("bad sub cgroup path: %s", sub) } paths[ctrl] = cgPath } @@ -270,18 +282,10 @@ func (p *setnsProcess) addIntoCgroupV1() error { } func (p *setnsProcess) addIntoCgroupV2() error { - base := p.manager.Path("") - sub := "" - if p.process.SubCgroupPaths != nil { - sub = p.process.SubCgroupPaths[""] - } - cgPath := path.Join(base, sub) - if !strings.HasPrefix(cgPath, base) { - return fmt.Errorf("%s is not a sub cgroup path", sub) - } - - if err := cgroups.WriteCgroupProc(cgPath, p.pid()); err != nil && !p.rootlessCgroups { - // On cgroup v2 + nesting + domain controllers, WriteCgroupProc may fail with EBUSY. + sub := p.process.SubCgroupPaths[""] + err := p.manager.AddPid(sub, p.pid()) + if err != nil && !p.rootlessCgroups { + // On cgroup v2 + nesting + domain controllers, adding to initial cgroup may fail with EBUSY. // https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643 // Try to join the cgroup of InitProcessPid, unless sub-cgroup is explicitly set. if p.initProcessPid != 0 && sub == "" { @@ -290,8 +294,8 @@ func (p *setnsProcess) addIntoCgroupV2() error { if initCgErr == nil { if initCgPath, ok := initCg[""]; ok { initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath) - logrus.Debugf("adding pid %d to cgroup %s failed (%v), attempting to join %s", - p.pid(), cgPath, err, initCgDirpath) + logrus.Debugf("adding pid %d to cgroup failed (%v), attempting to join %s", + p.pid(), err, initCgDirpath) // NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container. err = cgroups.WriteCgroupProc(initCgDirpath, p.pid()) } diff --git a/tests/integration/exec.bats b/tests/integration/exec.bats index 43fe4c3cde1..35e1cad285e 100644 --- a/tests/integration/exec.bats +++ b/tests/integration/exec.bats @@ -226,17 +226,17 @@ function check_exec_debug() { # Check we can't join parent cgroup. runc exec --cgroup ".." test_busybox cat /proc/self/cgroup [ "$status" -ne 0 ] - [[ "$output" == *" .. is not a sub cgroup path"* ]] + [[ "$output" == *"bad sub cgroup path"* ]] # Check we can't join non-existing subcgroup. runc exec --cgroup nonexistent test_busybox cat /proc/self/cgroup [ "$status" -ne 0 ] - [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] + [[ "$output" == *" adding pid "*"o such file or directory"* ]] # Check we can't join non-existing subcgroup (for a particular controller). runc exec --cgroup cpu:nonexistent test_busybox cat /proc/self/cgroup [ "$status" -ne 0 ] - [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] + [[ "$output" == *" adding pid "*"o such file or directory"* ]] # Check we can't specify non-existent controller. runc exec --cgroup whaaat:/ test_busybox true @@ -277,12 +277,12 @@ function check_exec_debug() { # Check we can't join parent cgroup. runc exec --cgroup ".." test_busybox cat /proc/self/cgroup [ "$status" -ne 0 ] - [[ "$output" == *" .. is not a sub cgroup path"* ]] + [[ "$output" == *"bad sub cgroup path"* ]] # Check we can't join non-existing subcgroup. runc exec --cgroup nonexistent test_busybox cat /proc/self/cgroup [ "$status" -ne 0 ] - [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] + [[ "$output" == *" adding pid "*"o such file or directory"* ]] # Check we can join top-level cgroup (implicit). runc exec test_busybox grep '^0::/$' /proc/self/cgroup @@ -318,7 +318,7 @@ function check_exec_debug() { # Check that --cgroup / disables the init cgroup fallback. runc exec --cgroup / test_busybox true [ "$status" -ne 0 ] - [[ "$output" == *" adding pid "*" to cgroups"*"/cgroup.procs: device or resource busy"* ]] + [[ "$output" == *" adding pid "*" to cgroups"*"evice or resource busy"* ]] # Check that explicit --cgroup foobar works. runc exec --cgroup foobar test_busybox grep '^0::/foobar$' /proc/self/cgroup diff --git a/vendor/github.com/opencontainers/cgroups/cgroups.go b/vendor/github.com/opencontainers/cgroups/cgroups.go index 1f127550c08..5a97bd36b71 100644 --- a/vendor/github.com/opencontainers/cgroups/cgroups.go +++ b/vendor/github.com/opencontainers/cgroups/cgroups.go @@ -29,6 +29,11 @@ type Manager interface { // can be used to merely create a cgroup. Apply(pid int) error + // AddPid adds a process with a given pid to an existing cgroup. + // The subcgroup argument is either empty, or a path relative to + // a cgroup under under the manager's cgroup. + AddPid(subcgroup string, pid int) error + // GetPids returns the PIDs of all processes inside the cgroup. GetPids() ([]int, error) diff --git a/vendor/github.com/opencontainers/cgroups/fs/fs.go b/vendor/github.com/opencontainers/cgroups/fs/fs.go index 23a8fb8742f..625931193ec 100644 --- a/vendor/github.com/opencontainers/cgroups/fs/fs.go +++ b/vendor/github.com/opencontainers/cgroups/fs/fs.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "os" + "path" + "strings" "sync" "golang.org/x/sys/unix" @@ -139,6 +141,33 @@ func (m *Manager) Apply(pid int) (retErr error) { return retErr } +// AddPid adds a process with a given pid to an existing cgroup. +// The subcgroup argument is either empty, or a path relative to +// a cgroup under under the manager's cgroup. +func (m *Manager) AddPid(subcgroup string, pid int) (retErr error) { + m.mu.Lock() + defer m.mu.Unlock() + + c := m.cgroups + + for _, dir := range m.paths { + path := path.Join(dir, subcgroup) + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("bad sub cgroup path: %s", subcgroup) + } + + if err := cgroups.WriteCgroupProc(path, pid); err != nil { + if isIgnorableError(c.Rootless, err) && c.Path == "" { + retErr = cgroups.ErrRootless + continue + } + return err + } + } + + return retErr +} + func (m *Manager) Destroy() error { m.mu.Lock() defer m.mu.Unlock() diff --git a/vendor/github.com/opencontainers/cgroups/fs2/fs2.go b/vendor/github.com/opencontainers/cgroups/fs2/fs2.go index c5d5a1f8ec3..356d087985c 100644 --- a/vendor/github.com/opencontainers/cgroups/fs2/fs2.go +++ b/vendor/github.com/opencontainers/cgroups/fs2/fs2.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "strings" "github.com/opencontainers/cgroups" @@ -83,6 +84,18 @@ func (m *Manager) Apply(pid int) error { return nil } +// AddPid adds a process with a given pid to an existing cgroup. +// The subcgroup argument is either empty, or a path relative to +// a cgroup under under the manager's cgroup. +func (m *Manager) AddPid(subcgroup string, pid int) error { + path := filepath.Join(m.dirPath, subcgroup) + if !strings.HasPrefix(path, m.dirPath) { + return fmt.Errorf("bad sub cgroup path: %s", subcgroup) + } + + return cgroups.WriteCgroupProc(path, pid) +} + func (m *Manager) GetPids() ([]int, error) { return cgroups.GetPids(m.dirPath) } diff --git a/vendor/github.com/opencontainers/cgroups/systemd/common.go b/vendor/github.com/opencontainers/cgroups/systemd/common.go index 875a589e3d8..537defbf2d1 100644 --- a/vendor/github.com/opencontainers/cgroups/systemd/common.go +++ b/vendor/github.com/opencontainers/cgroups/systemd/common.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "os" + "path" "strconv" "strings" "sync" @@ -208,6 +209,20 @@ func stopUnit(cm *dbusConnManager, unitName string) error { return nil } +func addPid(cm *dbusConnManager, unitName, subcgroup string, pid int) error { + absSubcgroup := subcgroup + if !path.IsAbs(absSubcgroup) { + absSubcgroup = "/" + subcgroup + } + if absSubcgroup != path.Clean(absSubcgroup) { + return fmt.Errorf("bad sub cgroup path: %s", subcgroup) + } + + return cm.retryOnDisconnect(func(c *systemdDbus.Conn) error { + return c.AttachProcessesToUnit(context.TODO(), unitName, absSubcgroup, []uint32{uint32(pid)}) + }) +} + func resetFailedUnit(cm *dbusConnManager, name string) error { return cm.retryOnDisconnect(func(c *systemdDbus.Conn) error { return c.ResetFailedUnitContext(context.TODO(), name) diff --git a/vendor/github.com/opencontainers/cgroups/systemd/v1.go b/vendor/github.com/opencontainers/cgroups/systemd/v1.go index b8959adbfa6..5500a53ac0f 100644 --- a/vendor/github.com/opencontainers/cgroups/systemd/v1.go +++ b/vendor/github.com/opencontainers/cgroups/systemd/v1.go @@ -215,6 +215,25 @@ func (m *LegacyManager) Apply(pid int) error { return nil } +// AddPid adds a process with a given pid to an existing cgroup. +// The subcgroup argument is either empty, or a path relative to +// a cgroup under under the manager's cgroup. +func (m *LegacyManager) AddPid(subcgroup string, pid int) error { + m.mu.Lock() + defer m.mu.Unlock() + + if err := addPid(m.dbus, getUnitName(m.cgroups), subcgroup, pid); err != nil { + return err + } + + // Since systemd only joins controllers it knows, use cgroupfs for the rest. + fsMgr, err := fs.NewManager(m.cgroups, m.paths) + if err != nil { + return err + } + return fsMgr.AddPid(subcgroup, pid) +} + func (m *LegacyManager) Destroy() error { m.mu.Lock() defer m.mu.Unlock() diff --git a/vendor/github.com/opencontainers/cgroups/systemd/v2.go b/vendor/github.com/opencontainers/cgroups/systemd/v2.go index 636b9cb01b5..c2f2e87f3ba 100644 --- a/vendor/github.com/opencontainers/cgroups/systemd/v2.go +++ b/vendor/github.com/opencontainers/cgroups/systemd/v2.go @@ -383,6 +383,16 @@ func cgroupFilesToChown() ([]string, error) { return filesToChown, nil } +// AddPid adds a process with a given pid to an existing cgroup. +// The subcgroup argument is either empty, or a path relative to +// a cgroup under under the manager's cgroup. +func (m *UnifiedManager) AddPid(subcgroup string, pid int) error { + m.mu.Lock() + defer m.mu.Unlock() + + return addPid(m.dbus, getUnitName(m.cgroups), subcgroup, pid) +} + func (m *UnifiedManager) Destroy() error { m.mu.Lock() defer m.mu.Unlock() diff --git a/vendor/modules.txt b/vendor/modules.txt index a86e8a19419..95a463ebb31 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -51,7 +51,7 @@ github.com/moby/sys/userns # github.com/mrunalp/fileutils v0.5.1 ## explicit; go 1.13 github.com/mrunalp/fileutils -# github.com/opencontainers/cgroups v0.0.4 +# github.com/opencontainers/cgroups v0.0.5 ## explicit; go 1.23.0 github.com/opencontainers/cgroups github.com/opencontainers/cgroups/devices