github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/cgroups/fs/apply_raw.go (about)

     1  // +build linux
     2  
     3  package fs
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"sync"
    13  
    14  	"github.com/opencontainers/runc/libcontainer/cgroups"
    15  	"github.com/opencontainers/runc/libcontainer/configs"
    16  	libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
    17  )
    18  
    19  var (
    20  	subsystems = subsystemSet{
    21  		&CpusetGroup{},
    22  		&DevicesGroup{},
    23  		&MemoryGroup{},
    24  		&CpuGroup{},
    25  		&CpuacctGroup{},
    26  		&PidsGroup{},
    27  		&BlkioGroup{},
    28  		&HugetlbGroup{},
    29  		&NetClsGroup{},
    30  		&NetPrioGroup{},
    31  		&PerfEventGroup{},
    32  		&FreezerGroup{},
    33  		&NameGroup{GroupName: "name=systemd", Join: true},
    34  	}
    35  	HugePageSizes, _ = cgroups.GetHugePageSize()
    36  )
    37  
    38  var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
    39  
    40  type subsystemSet []subsystem
    41  
    42  func (s subsystemSet) Get(name string) (subsystem, error) {
    43  	for _, ss := range s {
    44  		if ss.Name() == name {
    45  			return ss, nil
    46  		}
    47  	}
    48  	return nil, errSubsystemDoesNotExist
    49  }
    50  
    51  type subsystem interface {
    52  	// Name returns the name of the subsystem.
    53  	Name() string
    54  	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
    55  	GetStats(path string, stats *cgroups.Stats) error
    56  	// Removes the cgroup represented by 'cgroupData'.
    57  	Remove(*cgroupData) error
    58  	// Creates and joins the cgroup represented by 'cgroupData'.
    59  	Apply(*cgroupData) error
    60  	// Set the cgroup represented by cgroup.
    61  	Set(path string, cgroup *configs.Cgroup) error
    62  }
    63  
    64  type Manager struct {
    65  	mu      sync.Mutex
    66  	Cgroups *configs.Cgroup
    67  	Paths   map[string]string
    68  }
    69  
    70  // The absolute path to the root of the cgroup hierarchies.
    71  var cgroupRootLock sync.Mutex
    72  var cgroupRoot string
    73  
    74  // Gets the cgroupRoot.
    75  func getCgroupRoot() (string, error) {
    76  	cgroupRootLock.Lock()
    77  	defer cgroupRootLock.Unlock()
    78  
    79  	if cgroupRoot != "" {
    80  		return cgroupRoot, nil
    81  	}
    82  
    83  	root, err := cgroups.FindCgroupMountpointDir()
    84  	if err != nil {
    85  		return "", err
    86  	}
    87  
    88  	if _, err := os.Stat(root); err != nil {
    89  		return "", err
    90  	}
    91  
    92  	cgroupRoot = root
    93  	return cgroupRoot, nil
    94  }
    95  
    96  type cgroupData struct {
    97  	root      string
    98  	innerPath string
    99  	config    *configs.Cgroup
   100  	pid       int
   101  }
   102  
   103  func (m *Manager) Apply(pid int) (err error) {
   104  	if m.Cgroups == nil {
   105  		return nil
   106  	}
   107  	m.mu.Lock()
   108  	defer m.mu.Unlock()
   109  
   110  	var c = m.Cgroups
   111  
   112  	d, err := getCgroupData(m.Cgroups, pid)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	if c.Paths != nil {
   118  		paths := make(map[string]string)
   119  		for name, path := range c.Paths {
   120  			_, err := d.path(name)
   121  			if err != nil {
   122  				if cgroups.IsNotFound(err) {
   123  					continue
   124  				}
   125  				return err
   126  			}
   127  			paths[name] = path
   128  		}
   129  		m.Paths = paths
   130  		return cgroups.EnterPid(m.Paths, pid)
   131  	}
   132  
   133  	paths := make(map[string]string)
   134  	for _, sys := range subsystems {
   135  		if err := sys.Apply(d); err != nil {
   136  			return err
   137  		}
   138  		// TODO: Apply should, ideally, be reentrant or be broken up into a separate
   139  		// create and join phase so that the cgroup hierarchy for a container can be
   140  		// created then join consists of writing the process pids to cgroup.procs
   141  		p, err := d.path(sys.Name())
   142  		if err != nil {
   143  			// The non-presence of the devices subsystem is
   144  			// considered fatal for security reasons.
   145  			if cgroups.IsNotFound(err) && sys.Name() != "devices" {
   146  				continue
   147  			}
   148  			return err
   149  		}
   150  		paths[sys.Name()] = p
   151  	}
   152  	m.Paths = paths
   153  	return nil
   154  }
   155  
   156  func (m *Manager) Destroy() error {
   157  	if m.Cgroups.Paths != nil {
   158  		return nil
   159  	}
   160  	m.mu.Lock()
   161  	defer m.mu.Unlock()
   162  	if err := cgroups.RemovePaths(m.Paths); err != nil {
   163  		return err
   164  	}
   165  	m.Paths = make(map[string]string)
   166  	return nil
   167  }
   168  
   169  func (m *Manager) GetPaths() map[string]string {
   170  	m.mu.Lock()
   171  	paths := m.Paths
   172  	m.mu.Unlock()
   173  	return paths
   174  }
   175  
   176  func (m *Manager) GetStats() (*cgroups.Stats, error) {
   177  	m.mu.Lock()
   178  	defer m.mu.Unlock()
   179  	stats := cgroups.NewStats()
   180  	for name, path := range m.Paths {
   181  		sys, err := subsystems.Get(name)
   182  		if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) {
   183  			continue
   184  		}
   185  		if err := sys.GetStats(path, stats); err != nil {
   186  			return nil, err
   187  		}
   188  	}
   189  	return stats, nil
   190  }
   191  
   192  func (m *Manager) Set(container *configs.Config) error {
   193  	// If Paths are set, then we are just joining cgroups paths
   194  	// and there is no need to set any values.
   195  	if m.Cgroups.Paths != nil {
   196  		return nil
   197  	}
   198  
   199  	paths := m.GetPaths()
   200  	for _, sys := range subsystems {
   201  		path := paths[sys.Name()]
   202  		if err := sys.Set(path, container.Cgroups); err != nil {
   203  			return err
   204  		}
   205  	}
   206  
   207  	if m.Paths["cpu"] != "" {
   208  		if err := CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil {
   209  			return err
   210  		}
   211  	}
   212  	return nil
   213  }
   214  
   215  // Freeze toggles the container's freezer cgroup depending on the state
   216  // provided
   217  func (m *Manager) Freeze(state configs.FreezerState) error {
   218  	paths := m.GetPaths()
   219  	dir := paths["freezer"]
   220  	prevState := m.Cgroups.Resources.Freezer
   221  	m.Cgroups.Resources.Freezer = state
   222  	freezer, err := subsystems.Get("freezer")
   223  	if err != nil {
   224  		return err
   225  	}
   226  	err = freezer.Set(dir, m.Cgroups)
   227  	if err != nil {
   228  		m.Cgroups.Resources.Freezer = prevState
   229  		return err
   230  	}
   231  	return nil
   232  }
   233  
   234  func (m *Manager) GetPids() ([]int, error) {
   235  	paths := m.GetPaths()
   236  	return cgroups.GetPids(paths["devices"])
   237  }
   238  
   239  func (m *Manager) GetAllPids() ([]int, error) {
   240  	paths := m.GetPaths()
   241  	return cgroups.GetAllPids(paths["devices"])
   242  }
   243  
   244  func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) {
   245  	root, err := getCgroupRoot()
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	if (c.Name != "" || c.Parent != "") && c.Path != "" {
   251  		return nil, fmt.Errorf("cgroup: either Path or Name and Parent should be used")
   252  	}
   253  
   254  	// XXX: Do not remove this code. Path safety is important! -- cyphar
   255  	cgPath := libcontainerUtils.CleanPath(c.Path)
   256  	cgParent := libcontainerUtils.CleanPath(c.Parent)
   257  	cgName := libcontainerUtils.CleanPath(c.Name)
   258  
   259  	innerPath := cgPath
   260  	if innerPath == "" {
   261  		innerPath = filepath.Join(cgParent, cgName)
   262  	}
   263  
   264  	return &cgroupData{
   265  		root:      root,
   266  		innerPath: innerPath,
   267  		config:    c,
   268  		pid:       pid,
   269  	}, nil
   270  }
   271  
   272  func (raw *cgroupData) parentPath(subsystem, mountpoint, root string) (string, error) {
   273  	// Use GetThisCgroupDir instead of GetInitCgroupDir, because the creating
   274  	// process could in container and shared pid namespace with host, and
   275  	// /proc/1/cgroup could point to whole other world of cgroups.
   276  	initPath, err := cgroups.GetThisCgroupDir(subsystem)
   277  	if err != nil {
   278  		return "", err
   279  	}
   280  	// This is needed for nested containers, because in /proc/self/cgroup we
   281  	// see pathes from host, which don't exist in container.
   282  	relDir, err := filepath.Rel(root, initPath)
   283  	if err != nil {
   284  		return "", err
   285  	}
   286  	return filepath.Join(mountpoint, relDir), nil
   287  }
   288  
   289  func (raw *cgroupData) path(subsystem string) (string, error) {
   290  	mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem)
   291  	// If we didn't mount the subsystem, there is no point we make the path.
   292  	if err != nil {
   293  		return "", err
   294  	}
   295  
   296  	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
   297  	if filepath.IsAbs(raw.innerPath) {
   298  		// Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
   299  		return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil
   300  	}
   301  
   302  	parentPath, err := raw.parentPath(subsystem, mnt, root)
   303  	if err != nil {
   304  		return "", err
   305  	}
   306  
   307  	return filepath.Join(parentPath, raw.innerPath), nil
   308  }
   309  
   310  func (raw *cgroupData) join(subsystem string) (string, error) {
   311  	path, err := raw.path(subsystem)
   312  	if err != nil {
   313  		return "", err
   314  	}
   315  	if err := os.MkdirAll(path, 0755); err != nil {
   316  		return "", err
   317  	}
   318  	if err := cgroups.WriteCgroupProc(path, raw.pid); err != nil {
   319  		return "", err
   320  	}
   321  	return path, nil
   322  }
   323  
   324  func writeFile(dir, file, data string) error {
   325  	// Normally dir should not be empty, one case is that cgroup subsystem
   326  	// is not mounted, we will get empty dir, and we want it fail here.
   327  	if dir == "" {
   328  		return fmt.Errorf("no such directory for %s", file)
   329  	}
   330  	if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil {
   331  		return fmt.Errorf("failed to write %v to %v: %v", data, file, err)
   332  	}
   333  	return nil
   334  }
   335  
   336  func readFile(dir, file string) (string, error) {
   337  	data, err := ioutil.ReadFile(filepath.Join(dir, file))
   338  	return string(data), err
   339  }
   340  
   341  func removePath(p string, err error) error {
   342  	if err != nil {
   343  		return err
   344  	}
   345  	if p != "" {
   346  		return os.RemoveAll(p)
   347  	}
   348  	return nil
   349  }
   350  
   351  func CheckCpushares(path string, c int64) error {
   352  	var cpuShares int64
   353  
   354  	if c == 0 {
   355  		return nil
   356  	}
   357  
   358  	fd, err := os.Open(filepath.Join(path, "cpu.shares"))
   359  	if err != nil {
   360  		return err
   361  	}
   362  	defer fd.Close()
   363  
   364  	_, err = fmt.Fscanf(fd, "%d", &cpuShares)
   365  	if err != nil && err != io.EOF {
   366  		return err
   367  	}
   368  
   369  	if c > cpuShares {
   370  		return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares)
   371  	} else if c < cpuShares {
   372  		return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares)
   373  	}
   374  
   375  	return nil
   376  }