...

Source file src/github.com/opencontainers/runc/libcontainer/cgroups/fs/paths.go

Documentation: github.com/opencontainers/runc/libcontainer/cgroups/fs

     1  package fs
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"path/filepath"
     7  	"sync"
     8  
     9  	"golang.org/x/sys/unix"
    10  
    11  	"github.com/opencontainers/runc/libcontainer/cgroups"
    12  	"github.com/opencontainers/runc/libcontainer/configs"
    13  	"github.com/opencontainers/runc/libcontainer/utils"
    14  )
    15  
    16  // The absolute path to the root of the cgroup hierarchies.
    17  var (
    18  	cgroupRootLock sync.Mutex
    19  	cgroupRoot     string
    20  )
    21  
    22  const defaultCgroupRoot = "/sys/fs/cgroup"
    23  
    24  func initPaths(cg *configs.Cgroup) (map[string]string, error) {
    25  	root, err := rootPath()
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  
    30  	inner, err := innerPath(cg)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	paths := make(map[string]string)
    36  	for _, sys := range subsystems {
    37  		name := sys.Name()
    38  		path, err := subsysPath(root, inner, name)
    39  		if err != nil {
    40  			// The non-presence of the devices subsystem
    41  			// is considered fatal for security reasons.
    42  			if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") {
    43  				continue
    44  			}
    45  
    46  			return nil, err
    47  		}
    48  		paths[name] = path
    49  	}
    50  
    51  	return paths, nil
    52  }
    53  
    54  func tryDefaultCgroupRoot() string {
    55  	var st, pst unix.Stat_t
    56  
    57  	// (1) it should be a directory...
    58  	err := unix.Lstat(defaultCgroupRoot, &st)
    59  	if err != nil || st.Mode&unix.S_IFDIR == 0 {
    60  		return ""
    61  	}
    62  
    63  	// (2) ... and a mount point ...
    64  	err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst)
    65  	if err != nil {
    66  		return ""
    67  	}
    68  
    69  	if st.Dev == pst.Dev {
    70  		// parent dir has the same dev -- not a mount point
    71  		return ""
    72  	}
    73  
    74  	// (3) ... of 'tmpfs' fs type.
    75  	var fst unix.Statfs_t
    76  	err = unix.Statfs(defaultCgroupRoot, &fst)
    77  	if err != nil || fst.Type != unix.TMPFS_MAGIC {
    78  		return ""
    79  	}
    80  
    81  	// (4) it should have at least 1 entry ...
    82  	dir, err := os.Open(defaultCgroupRoot)
    83  	if err != nil {
    84  		return ""
    85  	}
    86  	defer dir.Close()
    87  	names, err := dir.Readdirnames(1)
    88  	if err != nil {
    89  		return ""
    90  	}
    91  	if len(names) < 1 {
    92  		return ""
    93  	}
    94  	// ... which is a cgroup mount point.
    95  	err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst)
    96  	if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
    97  		return ""
    98  	}
    99  
   100  	return defaultCgroupRoot
   101  }
   102  
   103  // rootPath finds and returns path to the root of the cgroup hierarchies.
   104  func rootPath() (string, error) {
   105  	cgroupRootLock.Lock()
   106  	defer cgroupRootLock.Unlock()
   107  
   108  	if cgroupRoot != "" {
   109  		return cgroupRoot, nil
   110  	}
   111  
   112  	// fast path
   113  	cgroupRoot = tryDefaultCgroupRoot()
   114  	if cgroupRoot != "" {
   115  		return cgroupRoot, nil
   116  	}
   117  
   118  	// slow path: parse mountinfo
   119  	mi, err := cgroups.GetCgroupMounts(false)
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  	if len(mi) < 1 {
   124  		return "", errors.New("no cgroup mount found in mountinfo")
   125  	}
   126  
   127  	// Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"),
   128  	// use its parent directory.
   129  	root := filepath.Dir(mi[0].Mountpoint)
   130  
   131  	if _, err := os.Stat(root); err != nil {
   132  		return "", err
   133  	}
   134  
   135  	cgroupRoot = root
   136  	return cgroupRoot, nil
   137  }
   138  
   139  func innerPath(c *configs.Cgroup) (string, error) {
   140  	if (c.Name != "" || c.Parent != "") && c.Path != "" {
   141  		return "", errors.New("cgroup: either Path or Name and Parent should be used")
   142  	}
   143  
   144  	// XXX: Do not remove CleanPath. Path safety is important! -- cyphar
   145  	innerPath := utils.CleanPath(c.Path)
   146  	if innerPath == "" {
   147  		cgParent := utils.CleanPath(c.Parent)
   148  		cgName := utils.CleanPath(c.Name)
   149  		innerPath = filepath.Join(cgParent, cgName)
   150  	}
   151  
   152  	return innerPath, nil
   153  }
   154  
   155  func subsysPath(root, inner, subsystem string) (string, error) {
   156  	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
   157  	if filepath.IsAbs(inner) {
   158  		mnt, err := cgroups.FindCgroupMountpoint(root, subsystem)
   159  		// If we didn't mount the subsystem, there is no point we make the path.
   160  		if err != nil {
   161  			return "", err
   162  		}
   163  
   164  		// Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
   165  		return filepath.Join(root, filepath.Base(mnt), inner), nil
   166  	}
   167  
   168  	// Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
   169  	// process could in container and shared pid namespace with host, and
   170  	// /proc/1/cgroup could point to whole other world of cgroups.
   171  	parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
   172  	if err != nil {
   173  		return "", err
   174  	}
   175  
   176  	return filepath.Join(parentPath, inner), nil
   177  }
   178  
   179  func apply(path string, pid int) error {
   180  	if path == "" {
   181  		return nil
   182  	}
   183  	if err := os.MkdirAll(path, 0o755); err != nil {
   184  		return err
   185  	}
   186  	return cgroups.WriteCgroupProc(path, pid)
   187  }
   188  

View as plain text