containerd/vendor/github.com/containerd/cgroups/utils.go
Michael Crosby 822ae18b14 Update cgroups to 1152b960fcee041f50df15cdc67c29db
```
1152b960fcee041f50df15cdc67c29dbccf801ef (HEAD -> master, origin/master)
Merge pull request #73 from gliptak/gofmt1
afd5981a16647b45b6dba3a50a88418b576cc17d Gofmt cgroup_test
65ce98b3dfeb0a9a8fecd7e4ebffb24ad0bfe28f Merge pull request #69 from
cclerget/master-weight-pointer
0f372c6d4a65a49c72b0afbd1aee6214637958bf Merge pull request #71 from
JoeWrightss/patch-1
f48bd85c9cbc306fada0cebc3a646a1f1fe99afe Fixs return error message
10cd53efd916e22b9bdea67223d287684f57f1f4 Merge pull request #70 from
gliptak/patch-1
64bade4cea6c438ee51a7a12528225946b42c6ca Take value instead of pointer
value
b49c4713f3824e81bfa67faddcdde1414171b54e Correct ineffassign warning
3bc6dde829bc2dc8d4097ce8ad5acc275de3df06 Merge pull request #68 from
cclerget/master-net_prio-typo
6b552a86e60e31903d3f8f3f494eda71f562cc54 Fix net_prio typo
c0437c3dd5958f74d7f54e9f5def749850b9d6a1 Merge pull request #67 from
gpanouts/get-all-cgroup-tasks
a31a0ff985237eddf30d9fe30a3643c7da4ae912 Add functionality for
retrieving all tasks of a cgroup
82cb49fc1779971dfef4ad696f1453f6f44987b1 Merge pull request #63 from
ChrsMark/lenient-subsystems-checking
7d825b29aecc02bb1e9bede427f8ed62bbc3030d Add test for cgroups load when
missing hierarchy in one subsystem
f6cbfb45aec6a2590c7e7f4b84a080602b3e642d Change Load function in order
to be more lenient on subsystems' checking
965bb1da4db7c8ce2690108c5a081562ce7493cb Merge pull request #66 from
crosbymichael/systemdci
ab9ec0e4abde2c2cb999719ff43af2d3b5830f75 (fork/systemdci, systemdci) Add
go-systemd dep for CI testing
0e94a83b6eb6cf4bc05d7f91ec1eaad57a77d3b6 Merge pull request #59 from
gliptak/patch-1
4479d118c89b5500a08cce7a78bbe822229c1e65 Merge pull request #62 from
estesp/fix-gofmt
9beb998c23f510b1e6670ad7791807eb9aff6741 Merge pull request #61 from
gliptak/patch-3
9a09e5899acc95fabcc620d6489fec674e6dddfa Fix gofmt of systemd.go
84e6e6ed2afdf661cd9dbf47c6f3412b546bc67f Merge pull request #60 from
gliptak/patch-2
e13f6cc3b9637c36e6a8af393b561127498f4be5 Add GoReportCard badge to
README
d124595ee85c245e7c1443fe402adf7ce4f7f6a4 Add Go 1.11 to Travis
d961ab930c38eb8bedcded479f1708b2ef4984c5 Correct typo
d2400726cfa7904fb79e3b896ec0e6ae500a76bd Merge pull request #57 from
estesp/project-update
e4cf832b95deb7ce898ece716307abc35cbd0a09 Add project references and use
common project travis
8baeff6b9d069acde48ef1bedec7e0f8ba684f05 Merge pull request #56 from
grantseltzer/patch-1
9de57ffeb46f6179333d7939436d92dcb5631e5f Add godoc badge to README.md
5017d4e9a9cf2d4381db99eacd9baf84b95bfb14 Merge pull request #54 from
WeiZhang555/bugfix
13aaafdc37e772059d3234ec762303537f440c5b Bugfix: can't write to cpuset
cgroup
58556f5ad8448d99a6f7bea69ea4bdb7747cfeb0 Merge pull request #53 from
baude/systemdslicedelegate
15ed73c1c075e6590ecf56170acedcba0da8167e systemd-239+ no longer allows
delegate slice
3024bc7cc0c88af4b32d38a14444f38e65ab169f Merge pull request #52 from
Sykomaniac/bugfix/slice-name
2596f332e449ea374f0f24a977437116714ce7ef Remove call to unitName
2e2922e146ed53ccf4481c245187b6afe244fded Merge pull request #51 from
containerd/type
0f3de2f77d3b76b3871242fbab2a6116179229af (type) Fix empty device type
```

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2018-12-10 11:58:45 -05:00

325 lines
7.2 KiB
Go

/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cgroups
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
units "github.com/docker/go-units"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
var isUserNS = runningInUserNS()
// runningInUserNS detects whether we are currently running in a user namespace.
// Copied from github.com/lxc/lxd/shared/util.go
func runningInUserNS() bool {
file, err := os.Open("/proc/self/uid_map")
if err != nil {
// This kernel-provided file only exists if user namespaces are supported
return false
}
defer file.Close()
buf := bufio.NewReader(file)
l, _, err := buf.ReadLine()
if err != nil {
return false
}
line := string(l)
var a, b, c int64
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
/*
* We assume we are in the initial user namespace if we have a full
* range - 4294967295 uids starting at uid 0.
*/
if a == 0 && b == 0 && c == 4294967295 {
return false
}
return true
}
// defaults returns all known groups
func defaults(root string) ([]Subsystem, error) {
h, err := NewHugetlb(root)
if err != nil && !os.IsNotExist(err) {
return nil, err
}
s := []Subsystem{
NewNamed(root, "systemd"),
NewFreezer(root),
NewPids(root),
NewNetCls(root),
NewNetPrio(root),
NewPerfEvent(root),
NewCputset(root),
NewCpu(root),
NewCpuacct(root),
NewMemory(root),
NewBlkio(root),
NewRdma(root),
}
// only add the devices cgroup if we are not in a user namespace
// because modifications are not allowed
if !isUserNS {
s = append(s, NewDevices(root))
}
// add the hugetlb cgroup if error wasn't due to missing hugetlb
// cgroup support on the host
if err == nil {
s = append(s, h)
}
return s, nil
}
// remove will remove a cgroup path handling EAGAIN and EBUSY errors and
// retrying the remove after a exp timeout
func remove(path string) error {
delay := 10 * time.Millisecond
for i := 0; i < 5; i++ {
if i != 0 {
time.Sleep(delay)
delay *= 2
}
if err := os.RemoveAll(path); err == nil {
return nil
}
}
return fmt.Errorf("cgroups: unable to remove path %q", path)
}
// readPids will read all the pids of processes in a cgroup by the provided path
func readPids(path string, subsystem Name) ([]Process, error) {
f, err := os.Open(filepath.Join(path, cgroupProcs))
if err != nil {
return nil, err
}
defer f.Close()
var (
out []Process
s = bufio.NewScanner(f)
)
for s.Scan() {
if t := s.Text(); t != "" {
pid, err := strconv.Atoi(t)
if err != nil {
return nil, err
}
out = append(out, Process{
Pid: pid,
Subsystem: subsystem,
Path: path,
})
}
}
return out, nil
}
// readTasksPids will read all the pids of tasks in a cgroup by the provided path
func readTasksPids(path string, subsystem Name) ([]Task, error) {
f, err := os.Open(filepath.Join(path, cgroupTasks))
if err != nil {
return nil, err
}
defer f.Close()
var (
out []Task
s = bufio.NewScanner(f)
)
for s.Scan() {
if t := s.Text(); t != "" {
pid, err := strconv.Atoi(t)
if err != nil {
return nil, err
}
out = append(out, Task{
Pid: pid,
Subsystem: subsystem,
Path: path,
})
}
}
return out, nil
}
func hugePageSizes() ([]string, error) {
var (
pageSizes []string
sizeList = []string{"B", "kB", "MB", "GB", "TB", "PB"}
)
files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
if err != nil {
return nil, err
}
for _, st := range files {
nameArray := strings.Split(st.Name(), "-")
pageSize, err := units.RAMInBytes(nameArray[1])
if err != nil {
return nil, err
}
pageSizes = append(pageSizes, units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList))
}
return pageSizes, nil
}
func readUint(path string) (uint64, error) {
v, err := ioutil.ReadFile(path)
if err != nil {
return 0, err
}
return parseUint(strings.TrimSpace(string(v)), 10, 64)
}
func parseUint(s string, base, bitSize int) (uint64, error) {
v, err := strconv.ParseUint(s, base, bitSize)
if err != nil {
intValue, intErr := strconv.ParseInt(s, base, bitSize)
// 1. Handle negative values greater than MinInt64 (and)
// 2. Handle negative values lesser than MinInt64
if intErr == nil && intValue < 0 {
return 0, nil
} else if intErr != nil &&
intErr.(*strconv.NumError).Err == strconv.ErrRange &&
intValue < 0 {
return 0, nil
}
return 0, err
}
return v, nil
}
func parseKV(raw string) (string, uint64, error) {
parts := strings.Fields(raw)
switch len(parts) {
case 2:
v, err := parseUint(parts[1], 10, 64)
if err != nil {
return "", 0, err
}
return parts[0], v, nil
default:
return "", 0, ErrInvalidFormat
}
}
func parseCgroupFile(path string) (map[string]string, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return parseCgroupFromReader(f)
}
func parseCgroupFromReader(r io.Reader) (map[string]string, error) {
var (
cgroups = make(map[string]string)
s = bufio.NewScanner(r)
)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
var (
text = s.Text()
parts = strings.SplitN(text, ":", 3)
)
if len(parts) < 3 {
return nil, fmt.Errorf("invalid cgroup entry: %q", text)
}
for _, subs := range strings.Split(parts[1], ",") {
if subs != "" {
cgroups[subs] = parts[2]
}
}
}
return cgroups, nil
}
func getCgroupDestination(subsystem string) (string, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return "", err
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if err := s.Err(); err != nil {
return "", err
}
fields := strings.Fields(s.Text())
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
if opt == subsystem {
return fields[3], nil
}
}
}
return "", ErrNoCgroupMountDestination
}
func pathers(subystems []Subsystem) []pather {
var out []pather
for _, s := range subystems {
if p, ok := s.(pather); ok {
out = append(out, p)
}
}
return out
}
func initializeSubsystem(s Subsystem, path Path, resources *specs.LinuxResources) error {
if c, ok := s.(creator); ok {
p, err := path(s.Name())
if err != nil {
return err
}
if err := c.Create(p, resources); err != nil {
return err
}
} else if c, ok := s.(pather); ok {
p, err := path(s.Name())
if err != nil {
return err
}
// do the default create if the group does not have a custom one
if err := os.MkdirAll(c.Path(p), defaultDirPerm); err != nil {
return err
}
}
return nil
}
func cleanPath(path string) string {
if path == "" {
return ""
}
path = filepath.Clean(path)
if !filepath.IsAbs(path) {
path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path))
}
return filepath.Clean(path)
}