github.com/intel/goresctrl@v0.5.0/pkg/cgroups/cgroupcontrol.go (about)

     1  // Copyright 2020-2021 Intel Corporation. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cgroups
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path"
    24  	"strings"
    25  	"syscall"
    26  )
    27  
    28  // Controller is our enumerated type for cgroup controllers.
    29  type Controller int
    30  
    31  // Group represents a control group.
    32  type Group string
    33  
    34  // nolint
    35  const (
    36  	// UnkownController represents a controller of unknown type.
    37  	UnknownController Controller = iota
    38  	// blkio cgroup controller.
    39  	Blkio
    40  	// cpu cgroup controller.
    41  	Cpu
    42  	// cpuacct cgroup controller.
    43  	Cpuacct
    44  	// cpuset cgroup controller.
    45  	Cpuset
    46  	// devices cgroup controller.
    47  	Devices
    48  	// freezer cgroup controller.
    49  	Freezer
    50  	// hugetlb cgroup controller.
    51  	Hugetlb
    52  	// memory cgroup controller.
    53  	Memory
    54  	// net_cls cgroup controller.
    55  	NetCls
    56  	// net_prio cgroup controller.
    57  	NetPrio
    58  	// per_event cgroup controller.
    59  	PerfEvent
    60  	// pids cgroup controller.
    61  	Pids
    62  )
    63  
    64  var (
    65  	// controllerNames maps controllers to names/relative paths.
    66  	controllerNames = map[Controller]string{
    67  		Blkio:     "blkio",
    68  		Cpu:       "cpu",
    69  		Cpuacct:   "cpuacct",
    70  		Cpuset:    "cpuset",
    71  		Devices:   "devices",
    72  		Freezer:   "freezer",
    73  		Hugetlb:   "hugetlb",
    74  		Memory:    "memory",
    75  		NetCls:    "net_cls",
    76  		NetPrio:   "net_prio",
    77  		PerfEvent: "perf_event",
    78  		Pids:      "pids",
    79  	}
    80  
    81  	// controllerNames maps controllers to names/relative paths.
    82  	controllerDirs = map[string]Controller{
    83  		"blkio":      Blkio,
    84  		"cpu":        Cpu,
    85  		"cpuacct":    Cpuacct,
    86  		"cpuset":     Cpuset,
    87  		"devices":    Devices,
    88  		"freezer":    Freezer,
    89  		"hugetlb":    Hugetlb,
    90  		"memory":     Memory,
    91  		"net_cls":    NetCls,
    92  		"net_prio":   NetPrio,
    93  		"perf_event": PerfEvent,
    94  		"pids":       Pids,
    95  	}
    96  )
    97  
    98  // String returns the name of the given controller.
    99  func (c Controller) String() string {
   100  	if name, ok := controllerNames[c]; ok {
   101  		return name
   102  	}
   103  	return "unknown"
   104  }
   105  
   106  // Path returns the absolute path of the given controller.
   107  func (c Controller) Path() string {
   108  	return cgroupPath(c.String())
   109  }
   110  
   111  // RelPath returns the relative path of the given controller.
   112  func (c Controller) RelPath() string {
   113  	return c.String()
   114  }
   115  
   116  // Group returns the given group for the controller.
   117  func (c Controller) Group(group string) Group {
   118  	return Group(cgroupPath(c.String(), group))
   119  }
   120  
   121  // AsGroup returns the group for the given absolute directory path.
   122  func AsGroup(absDir string) Group {
   123  	return Group(absDir)
   124  }
   125  
   126  // Controller returns the controller for the group.
   127  func (g Group) Controller() Controller {
   128  	relPath := strings.TrimPrefix(string(g), cgroupPath()+"/")
   129  	split := strings.SplitN(relPath, "/", 2)
   130  	if len(split) > 0 {
   131  		return controllerDirs[split[0]]
   132  	}
   133  	return UnknownController
   134  }
   135  
   136  // GetTasks reads the pids of threads currently assigned to the group.
   137  func (g Group) GetTasks() ([]string, error) {
   138  	return g.readPids(Tasks)
   139  }
   140  
   141  // GetProcesses reads the pids of processes currently assigned to the group.
   142  func (g Group) GetProcesses() ([]string, error) {
   143  	return g.readPids(Procs)
   144  }
   145  
   146  // AddTasks writes the given thread pids to the group.
   147  func (g Group) AddTasks(pids ...string) error {
   148  	return g.writePids(Tasks, pids...)
   149  }
   150  
   151  // AddProcesses writes the given process pids to the group.
   152  func (g Group) AddProcesses(pids ...string) error {
   153  	return g.writePids(Procs, pids...)
   154  }
   155  
   156  // Write writes the formatted data to the groups entry.
   157  func (g Group) Write(entry, format string, args ...interface{}) error {
   158  	entryPath := path.Join(string(g), entry)
   159  	f, err := fsi.OpenFile(entryPath, os.O_WRONLY, 0644)
   160  	if err != nil {
   161  		return g.errorf("%q: failed to open for writing: %v", entry, err)
   162  	}
   163  	defer f.Close()
   164  
   165  	data := fmt.Sprintf(format, args...)
   166  	if _, err := f.Write([]byte(data)); err != nil {
   167  		return g.errorf("%q: failed to write %q: %v", entry, data, err)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  // Read the groups entry and return contents in a string
   174  func (g Group) Read(entry string) (string, error) {
   175  	var buf bytes.Buffer
   176  	entryPath := path.Join(string(g), entry)
   177  	f, err := fsi.OpenFile(entryPath, os.O_RDONLY, 0644)
   178  	if err != nil {
   179  		return "", g.errorf("%q: failed to open for reading: %v", entry, err)
   180  	}
   181  	defer f.Close()
   182  	if _, err := buf.ReadFrom(f); err != nil {
   183  		return "", err
   184  	}
   185  	return buf.String(), nil
   186  }
   187  
   188  // readPids reads pids from a cgroup's tasks or procs entry.
   189  func (g Group) readPids(entry string) ([]string, error) {
   190  	var pids []string
   191  
   192  	pidFile := path.Join(string(g), entry)
   193  
   194  	f, err := fsi.OpenFile(pidFile, os.O_RDONLY, 0644)
   195  	if err != nil {
   196  		return nil, g.errorf("failed to open %q: %v", entry, err)
   197  	}
   198  	defer f.Close()
   199  
   200  	s := bufio.NewScanner(f)
   201  	for s.Scan() {
   202  		pids = append(pids, s.Text())
   203  	}
   204  	if s.Err() != nil {
   205  		return nil, g.errorf("failed to read %q: %v", entry, err)
   206  	}
   207  
   208  	return pids, nil
   209  }
   210  
   211  // writePids writes pids to a cgroup's tasks or procs entry.
   212  func (g Group) writePids(entry string, pids ...string) error {
   213  	pidFile := path.Join(string(g), entry)
   214  
   215  	f, err := fsi.OpenFile(pidFile, os.O_WRONLY, 0644)
   216  	if err != nil {
   217  		return g.errorf("failed to write pids to %q: %v", pidFile, err)
   218  	}
   219  	defer f.Close()
   220  
   221  	for _, pid := range pids {
   222  		if _, err := f.Write([]byte(pid)); err != nil {
   223  			if !errors.Is(err, syscall.ESRCH) {
   224  				return g.errorf("failed to write pid %s to %q: %v",
   225  					pidFile, pid, err)
   226  			}
   227  		}
   228  	}
   229  
   230  	return nil
   231  }
   232  
   233  // error returns a formatted group-specific error.
   234  func (g Group) errorf(format string, args ...interface{}) error {
   235  	name := strings.TrimPrefix(string(g), cgroupPath()+"/")
   236  	return fmt.Errorf("cgroup "+name+": "+format, args...)
   237  }