mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
6c5441e5cb
1. Separate path initialization logic from Apply to initPaths,
and call initPaths from NewManager, so:
- we can error out early (in NewManager rather than Apply);
- always have m.paths available (e.g. in Destroy or Exists).
- do not unnecessarily call subsysPath from Apply in case
the paths were already provided.
2. Add a check for non-nil cgroups.Resources to NewManager,
since initPaths, as well as some controller's Apply methods,
need it.
3. Move cgroups.Resources.Unified check from Apply to NewManager,
so we can error out early (same check exists in Set).
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
187 lines
4.2 KiB
Go
187 lines
4.2 KiB
Go
package fs
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/runc/libcontainer/utils"
|
|
)
|
|
|
|
// The absolute path to the root of the cgroup hierarchies.
|
|
var (
|
|
cgroupRootLock sync.Mutex
|
|
cgroupRoot string
|
|
)
|
|
|
|
const defaultCgroupRoot = "/sys/fs/cgroup"
|
|
|
|
func initPaths(cg *configs.Cgroup) (map[string]string, error) {
|
|
root, err := rootPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inner, err := innerPath(cg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
paths := make(map[string]string)
|
|
for _, sys := range subsystems {
|
|
name := sys.Name()
|
|
path, err := subsysPath(root, inner, name)
|
|
if err != nil {
|
|
// The non-presence of the devices subsystem
|
|
// is considered fatal for security reasons.
|
|
if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") {
|
|
continue
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
paths[name] = path
|
|
}
|
|
|
|
return paths, nil
|
|
}
|
|
|
|
func tryDefaultCgroupRoot() string {
|
|
var st, pst unix.Stat_t
|
|
|
|
// (1) it should be a directory...
|
|
err := unix.Lstat(defaultCgroupRoot, &st)
|
|
if err != nil || st.Mode&unix.S_IFDIR == 0 {
|
|
return ""
|
|
}
|
|
|
|
// (2) ... and a mount point ...
|
|
err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
if st.Dev == pst.Dev {
|
|
// parent dir has the same dev -- not a mount point
|
|
return ""
|
|
}
|
|
|
|
// (3) ... of 'tmpfs' fs type.
|
|
var fst unix.Statfs_t
|
|
err = unix.Statfs(defaultCgroupRoot, &fst)
|
|
if err != nil || fst.Type != unix.TMPFS_MAGIC {
|
|
return ""
|
|
}
|
|
|
|
// (4) it should have at least 1 entry ...
|
|
dir, err := os.Open(defaultCgroupRoot)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
names, err := dir.Readdirnames(1)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(names) < 1 {
|
|
return ""
|
|
}
|
|
// ... which is a cgroup mount point.
|
|
err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst)
|
|
if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
|
|
return ""
|
|
}
|
|
|
|
return defaultCgroupRoot
|
|
}
|
|
|
|
// rootPath finds and returns path to the root of the cgroup hierarchies.
|
|
func rootPath() (string, error) {
|
|
cgroupRootLock.Lock()
|
|
defer cgroupRootLock.Unlock()
|
|
|
|
if cgroupRoot != "" {
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
// fast path
|
|
cgroupRoot = tryDefaultCgroupRoot()
|
|
if cgroupRoot != "" {
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
// slow path: parse mountinfo
|
|
mi, err := cgroups.GetCgroupMounts(false)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(mi) < 1 {
|
|
return "", errors.New("no cgroup mount found in mountinfo")
|
|
}
|
|
|
|
// Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"),
|
|
// use its parent directory.
|
|
root := filepath.Dir(mi[0].Mountpoint)
|
|
|
|
if _, err := os.Stat(root); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
cgroupRoot = root
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
func innerPath(c *configs.Cgroup) (string, error) {
|
|
if (c.Name != "" || c.Parent != "") && c.Path != "" {
|
|
return "", errors.New("cgroup: either Path or Name and Parent should be used")
|
|
}
|
|
|
|
// XXX: Do not remove CleanPath. Path safety is important! -- cyphar
|
|
innerPath := utils.CleanPath(c.Path)
|
|
if innerPath == "" {
|
|
cgParent := utils.CleanPath(c.Parent)
|
|
cgName := utils.CleanPath(c.Name)
|
|
innerPath = filepath.Join(cgParent, cgName)
|
|
}
|
|
|
|
return innerPath, nil
|
|
}
|
|
|
|
func subsysPath(root, inner, subsystem string) (string, error) {
|
|
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
|
|
if filepath.IsAbs(inner) {
|
|
mnt, err := cgroups.FindCgroupMountpoint(root, subsystem)
|
|
// If we didn't mount the subsystem, there is no point we make the path.
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
|
|
return filepath.Join(root, filepath.Base(mnt), inner), nil
|
|
}
|
|
|
|
// Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
|
|
// process could in container and shared pid namespace with host, and
|
|
// /proc/1/cgroup could point to whole other world of cgroups.
|
|
parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return filepath.Join(parentPath, inner), nil
|
|
}
|
|
|
|
func apply(path string, pid int) error {
|
|
if path == "" {
|
|
return nil
|
|
}
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
return err
|
|
}
|
|
return cgroups.WriteCgroupProc(path, pid)
|
|
}
|