github.com/criyle/go-sandbox@v0.10.3/pkg/cgroup/cgroup_info_linux.go (about)

     1  package cgroup
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  const numberOfControllers = 5
    13  
    14  // Controllers defines enabled controller of a cgroup
    15  type Controllers struct {
    16  	CPU     bool
    17  	CPUSet  bool
    18  	CPUAcct bool
    19  	Memory  bool
    20  	Pids    bool
    21  }
    22  
    23  // Set changes the enabled status of a specific controller
    24  func (c *Controllers) Set(ct string, value bool) {
    25  	switch ct {
    26  	case CPU:
    27  		c.CPU = value
    28  	case CPUSet:
    29  		c.CPUSet = value
    30  	case CPUAcct:
    31  		c.CPUAcct = value
    32  	case Memory:
    33  		c.Memory = value
    34  	case Pids:
    35  		c.Pids = value
    36  	}
    37  }
    38  
    39  // Intersect reset the specific controller if it is not enabled in the other
    40  func (c *Controllers) Intersect(o *Controllers) {
    41  	c.CPU = c.CPU && o.CPU
    42  	c.CPUSet = c.CPUSet && o.CPUSet
    43  	c.CPUAcct = c.CPUAcct && o.CPUAcct
    44  	c.Memory = c.Memory && o.Memory
    45  	c.Pids = c.Pids && o.Pids
    46  }
    47  
    48  // Contains returns true if the current controller enabled all controllers in the other controller
    49  func (c *Controllers) Contains(o *Controllers) bool {
    50  	return (c.CPU || !o.CPU) && (c.CPUSet || !o.CPUSet) && (c.CPUAcct || !o.CPUAcct) &&
    51  		(c.Memory || !o.Memory) && (c.Pids || !o.Pids)
    52  }
    53  
    54  // Names returns a list of string of all enabled container names
    55  func (c *Controllers) Names() []string {
    56  	names := make([]string, 0, numberOfControllers)
    57  	for _, v := range []struct {
    58  		e bool
    59  		n string
    60  	}{
    61  		{c.CPU, CPU},
    62  		{c.CPUAcct, CPUAcct},
    63  		{c.CPUSet, CPUSet},
    64  		{c.Memory, Memory},
    65  		{c.Pids, Pids},
    66  	} {
    67  		if v.e {
    68  			names = append(names, v.n)
    69  		}
    70  	}
    71  	return names
    72  }
    73  
    74  func (c *Controllers) String() string {
    75  	return "[" + strings.Join(c.Names(), ", ") + "]"
    76  }
    77  
    78  // Info reads the cgroup mount info from /proc/cgroups
    79  type Info struct {
    80  	Hierarchy  int
    81  	NumCgroups int
    82  	Enabled    bool
    83  }
    84  
    85  // GetCgroupV1Info read /proc/cgroups and return the result
    86  func GetCgroupV1Info() (map[string]Info, error) {
    87  	f, err := os.Open(procCgroupsPath)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	defer f.Close()
    92  
    93  	rt := make(map[string]Info)
    94  	s := bufio.NewScanner(f)
    95  	for s.Scan() {
    96  		text := s.Text()
    97  		if text[0] == '#' {
    98  			continue
    99  		}
   100  		parts := strings.Fields(text)
   101  		if len(parts) < 4 {
   102  			continue
   103  		}
   104  
   105  		// format: subsys_name hierarchy num_cgroups enabled
   106  		name := parts[0]
   107  		hierarchy, err := strconv.Atoi(parts[1])
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		numCgroups, err := strconv.Atoi(parts[2])
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  		enabled := parts[3] != "0"
   116  		rt[name] = Info{
   117  			Hierarchy:  hierarchy,
   118  			NumCgroups: numCgroups,
   119  			Enabled:    enabled,
   120  		}
   121  	}
   122  	if err := s.Err(); err != nil {
   123  		return nil, err
   124  	}
   125  	return rt, nil
   126  }
   127  
   128  // GetCurrentCgroupPrefix returns the cgroup prefix of current process
   129  func GetCurrentCgroupPrefix() (string, error) {
   130  	c, err := os.ReadFile(procSelfCgroup)
   131  	if err != nil {
   132  		return "", err
   133  	}
   134  	firstLine, _, _ := strings.Cut(string(c), "\n")
   135  	f := strings.Split(firstLine, ":")
   136  	if len(f) < 3 {
   137  		return "", fmt.Errorf("invalid " + procSelfCgroup)
   138  	}
   139  	return f[2][1:], nil
   140  }
   141  
   142  // GetAvailableController returns available cgroup controller in the system
   143  func GetAvailableController() (*Controllers, error) {
   144  	if DetectedCgroupType == TypeV1 {
   145  		return GetAvailableControllerV1()
   146  	}
   147  	return GetAvailableControllerV2()
   148  }
   149  
   150  // GetAvailableControllerWithPrefix returns available cgroup controller within the cgroup prefix
   151  func GetAvailableControllerWithPrefix(prefix string) (*Controllers, error) {
   152  	if DetectedCgroupType == TypeV1 {
   153  		return GetAvailableControllerV1()
   154  	}
   155  	return getAvailableControllerV2(prefix)
   156  }
   157  
   158  // GetAvailableControllerV1 reads /proc/cgroups and get all available controller as set
   159  func GetAvailableControllerV1() (*Controllers, error) {
   160  	info, err := GetCgroupV1Info()
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	rt := &Controllers{}
   166  	for k, v := range info {
   167  		if !v.Enabled {
   168  			continue
   169  		}
   170  		rt.Set(k, true)
   171  	}
   172  	return rt, nil
   173  }
   174  
   175  // GetAvailableControllerV2 reads /sys/fs/cgroup/cgroup.controllers to get all controller
   176  func GetAvailableControllerV2() (*Controllers, error) {
   177  	return getAvailableControllerV2(".")
   178  }
   179  
   180  func getAvailableControllerV2(prefix string) (*Controllers, error) {
   181  	return getAvailableControllerV2path(path.Join(basePath, prefix, cgroupControllers))
   182  }
   183  
   184  func getAvailableControllerV2path(p string) (*Controllers, error) {
   185  	c, err := readFile(p)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	m := &Controllers{}
   191  	f := strings.Fields(string(c))
   192  	for _, v := range f {
   193  		m.Set(v, true)
   194  	}
   195  	return m, nil
   196  }