github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/utils/cgroups/controllers.go (about)

     1  /*
     2  Copyright 2018 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cgroups
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/Mirantis/virtlet/pkg/fs"
    26  	"github.com/Mirantis/virtlet/pkg/utils"
    27  )
    28  
    29  const (
    30  	cgroupfs = "/sys/fs/cgroup"
    31  )
    32  
    33  // Controller represents a named controller for a process
    34  type Controller struct {
    35  	fsys fs.FileSystem
    36  	name string
    37  	path string
    38  }
    39  
    40  // Manager provides an interface to operate on linux cgroups
    41  type Manager interface {
    42  	// GetProcessControllers returns the mapping between controller types and
    43  	// their paths inside cgroup fs for the specified PID.
    44  	GetProcessControllers() (map[string]string, error)
    45  	// GetProcessController returns a named resource Controller for the specified PID.
    46  	GetProcessController(controllerName string) (*Controller, error)
    47  	// MoveProcess move the process to the path under a cgroup controller
    48  	MoveProcess(controller, path string) error
    49  }
    50  
    51  // RealManager provides an implementation of Manager which is
    52  // using default linux system paths to access info about cgroups for processes.
    53  type RealManager struct {
    54  	fsys fs.FileSystem
    55  	pid  string
    56  }
    57  
    58  var _ Manager = &RealManager{}
    59  
    60  // NewManager returns an instance of RealManager
    61  func NewManager(pid interface{}, fsys fs.FileSystem) Manager {
    62  	if fsys == nil {
    63  		fsys = fs.RealFileSystem
    64  	}
    65  	return &RealManager{fsys: fsys, pid: utils.Stringify(pid)}
    66  }
    67  
    68  // GetProcessControllers is an implementation of GetProcessControllers method
    69  // of Manager interface.
    70  func (c *RealManager) GetProcessControllers() (map[string]string, error) {
    71  	fr, err := c.fsys.GetDelimitedReader(filepath.Join("/proc", c.pid, "cgroup"))
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	defer fr.Close()
    76  
    77  	ctrls := make(map[string]string)
    78  
    79  	for {
    80  		line, err := fr.ReadString('\n')
    81  		if err != nil {
    82  			if err != io.EOF {
    83  				return nil, err
    84  			}
    85  		}
    86  
    87  		// strip eol
    88  		line = strings.Trim(line, "\n")
    89  		if line == "" {
    90  			break
    91  		}
    92  
    93  		// split entries like:
    94  		// "6:memory:/user.slice/user-xxx.slice/session-xx.scope"
    95  		parts := strings.SplitN(line, ":", 3)
    96  
    97  		name := parts[1]
    98  		if strings.HasPrefix(name, "name=") {
    99  			// Handle named cgroup hierarchies like name=systemd
   100  			// The corresponding directory tree will be /sys/fs/cgroup/systemd
   101  			name = name[5:]
   102  		}
   103  
   104  		// use second part as controller name and third as its path
   105  		ctrls[name] = parts[2]
   106  
   107  		if err == io.EOF {
   108  			break
   109  		}
   110  	}
   111  
   112  	return ctrls, nil
   113  }
   114  
   115  // GetProcessController is an implementation of GetProcessController method
   116  // of Manager interface.
   117  func (c *RealManager) GetProcessController(controllerName string) (*Controller, error) {
   118  	controllers, err := c.GetProcessControllers()
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	controllerPath, ok := controllers[controllerName]
   124  	if !ok {
   125  		return nil, fmt.Errorf("controller %q for process %v not found", controllerName, c.pid)
   126  	}
   127  
   128  	return &Controller{
   129  		fsys: c.fsys,
   130  		name: controllerName,
   131  		path: controllerPath,
   132  	}, nil
   133  }
   134  
   135  // MoveProcess implements MoveProcess method of Manager
   136  func (c *RealManager) MoveProcess(controller, path string) error {
   137  	return c.fsys.WriteFile(
   138  		filepath.Join(cgroupfs, controller, path, "cgroup.procs"),
   139  		[]byte(utils.Stringify(c.pid)),
   140  		0644,
   141  	)
   142  }
   143  
   144  // Set sets the value of a controller setting
   145  func (c *Controller) Set(name string, value interface{}) error {
   146  	return c.fsys.WriteFile(
   147  		filepath.Join(cgroupfs, c.name, c.path, c.name+"."+name),
   148  		[]byte(utils.Stringify(value)),
   149  		0644,
   150  	)
   151  }