gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

utils.go (6459B)


      1 /*
      2    Copyright The containerd Authors.
      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 cgroup1
     18 
     19 import (
     20 	"bufio"
     21 	"fmt"
     22 	"os"
     23 	"path/filepath"
     24 	"strconv"
     25 	"strings"
     26 	"time"
     27 
     28 	"github.com/containerd/cgroups/v3"
     29 	units "github.com/docker/go-units"
     30 	specs "github.com/opencontainers/runtime-spec/specs-go"
     31 )
     32 
     33 // defaults returns all known groups
     34 func defaults(root string) ([]Subsystem, error) {
     35 	h, err := NewHugetlb(root)
     36 	if err != nil && !os.IsNotExist(err) {
     37 		return nil, err
     38 	}
     39 	s := []Subsystem{
     40 		NewNamed(root, "systemd"),
     41 		NewFreezer(root),
     42 		NewPids(root),
     43 		NewNetCls(root),
     44 		NewNetPrio(root),
     45 		NewPerfEvent(root),
     46 		NewCpuset(root),
     47 		NewCpu(root),
     48 		NewCpuacct(root),
     49 		NewMemory(root),
     50 		NewBlkio(root),
     51 		NewRdma(root),
     52 	}
     53 	// only add the devices cgroup if we are not in a user namespace
     54 	// because modifications are not allowed
     55 	if !cgroups.RunningInUserNS() {
     56 		s = append(s, NewDevices(root))
     57 	}
     58 	// add the hugetlb cgroup if error wasn't due to missing hugetlb
     59 	// cgroup support on the host
     60 	if err == nil {
     61 		s = append(s, h)
     62 	}
     63 	return s, nil
     64 }
     65 
     66 // remove will remove a cgroup path handling EAGAIN and EBUSY errors and
     67 // retrying the remove after a exp timeout
     68 func remove(path string) error {
     69 	delay := 10 * time.Millisecond
     70 	for i := 0; i < 5; i++ {
     71 		if i != 0 {
     72 			time.Sleep(delay)
     73 			delay *= 2
     74 		}
     75 		if err := os.RemoveAll(path); err == nil {
     76 			return nil
     77 		}
     78 	}
     79 	return fmt.Errorf("cgroups: unable to remove path %q", path)
     80 }
     81 
     82 // readPids will read all the pids of processes or tasks in a cgroup by the provided path
     83 func readPids(path string, subsystem Name, pType procType) ([]Process, error) {
     84 	f, err := os.Open(filepath.Join(path, pType))
     85 	if err != nil {
     86 		return nil, err
     87 	}
     88 	defer f.Close()
     89 	var (
     90 		out []Process
     91 		s   = bufio.NewScanner(f)
     92 	)
     93 	for s.Scan() {
     94 		if t := s.Text(); t != "" {
     95 			pid, err := strconv.Atoi(t)
     96 			if err != nil {
     97 				return nil, err
     98 			}
     99 			out = append(out, Process{
    100 				Pid:       pid,
    101 				Subsystem: subsystem,
    102 				Path:      path,
    103 			})
    104 		}
    105 	}
    106 	if err := s.Err(); err != nil {
    107 		// failed to read all pids?
    108 		return nil, err
    109 	}
    110 	return out, nil
    111 }
    112 
    113 func hugePageSizes() ([]string, error) {
    114 	var (
    115 		pageSizes []string
    116 		sizeList  = []string{"B", "KB", "MB", "GB", "TB", "PB"}
    117 	)
    118 	files, err := os.ReadDir("/sys/kernel/mm/hugepages")
    119 	if err != nil {
    120 		return nil, err
    121 	}
    122 	for _, st := range files {
    123 		nameArray := strings.Split(st.Name(), "-")
    124 		pageSize, err := units.RAMInBytes(nameArray[1])
    125 		if err != nil {
    126 			return nil, err
    127 		}
    128 		pageSizes = append(pageSizes, units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList))
    129 	}
    130 	return pageSizes, nil
    131 }
    132 
    133 func readUint(path string) (uint64, error) {
    134 	v, err := os.ReadFile(path)
    135 	if err != nil {
    136 		return 0, err
    137 	}
    138 	return parseUint(strings.TrimSpace(string(v)), 10, 64)
    139 }
    140 
    141 func parseUint(s string, base, bitSize int) (uint64, error) {
    142 	v, err := strconv.ParseUint(s, base, bitSize)
    143 	if err != nil {
    144 		intValue, intErr := strconv.ParseInt(s, base, bitSize)
    145 		// 1. Handle negative values greater than MinInt64 (and)
    146 		// 2. Handle negative values lesser than MinInt64
    147 		if intErr == nil && intValue < 0 {
    148 			return 0, nil
    149 		} else if intErr != nil &&
    150 			intErr.(*strconv.NumError).Err == strconv.ErrRange &&
    151 			intValue < 0 {
    152 			return 0, nil
    153 		}
    154 		return 0, err
    155 	}
    156 	return v, nil
    157 }
    158 
    159 func parseKV(raw string) (string, uint64, error) {
    160 	parts := strings.Fields(raw)
    161 	switch len(parts) {
    162 	case 2:
    163 		v, err := parseUint(parts[1], 10, 64)
    164 		if err != nil {
    165 			return "", 0, err
    166 		}
    167 		return parts[0], v, nil
    168 	default:
    169 		return "", 0, ErrInvalidFormat
    170 	}
    171 }
    172 
    173 // ParseCgroupFile parses the given cgroup file, typically /proc/self/cgroup
    174 // or /proc/<pid>/cgroup, into a map of subsystems to cgroup paths, e.g.
    175 //
    176 //	"cpu": "/user.slice/user-1000.slice"
    177 //	"pids": "/user.slice/user-1000.slice"
    178 //
    179 // etc.
    180 //
    181 // The resulting map does not have an element for cgroup v2 unified hierarchy.
    182 // Use [cgroups.ParseCgroupFileUnified] to get the unified path.
    183 func ParseCgroupFile(path string) (map[string]string, error) {
    184 	x, _, err := ParseCgroupFileUnified(path)
    185 	return x, err
    186 }
    187 
    188 // ParseCgroupFileUnified returns legacy subsystem paths as the first value,
    189 // and returns the unified path as the second value.
    190 //
    191 // Deprecated: use [cgroups.ParseCgroupFileUnified] instead .
    192 func ParseCgroupFileUnified(path string) (map[string]string, string, error) {
    193 	return cgroups.ParseCgroupFileUnified(path)
    194 }
    195 
    196 func getCgroupDestination(subsystem string) (string, error) {
    197 	f, err := os.Open("/proc/self/mountinfo")
    198 	if err != nil {
    199 		return "", err
    200 	}
    201 	defer f.Close()
    202 	s := bufio.NewScanner(f)
    203 	for s.Scan() {
    204 		fields := strings.Split(s.Text(), " ")
    205 		if len(fields) < 10 {
    206 			// broken mountinfo?
    207 			continue
    208 		}
    209 		if fields[len(fields)-3] != "cgroup" {
    210 			continue
    211 		}
    212 		for _, opt := range strings.Split(fields[len(fields)-1], ",") {
    213 			if opt == subsystem {
    214 				return fields[3], nil
    215 			}
    216 		}
    217 	}
    218 	if err := s.Err(); err != nil {
    219 		return "", err
    220 	}
    221 	return "", ErrNoCgroupMountDestination
    222 }
    223 
    224 func pathers(subystems []Subsystem) []pather {
    225 	var out []pather
    226 	for _, s := range subystems {
    227 		if p, ok := s.(pather); ok {
    228 			out = append(out, p)
    229 		}
    230 	}
    231 	return out
    232 }
    233 
    234 func initializeSubsystem(s Subsystem, path Path, resources *specs.LinuxResources) error {
    235 	if c, ok := s.(creator); ok {
    236 		p, err := path(s.Name())
    237 		if err != nil {
    238 			return err
    239 		}
    240 		if err := c.Create(p, resources); err != nil {
    241 			return err
    242 		}
    243 	} else if c, ok := s.(pather); ok {
    244 		p, err := path(s.Name())
    245 		if err != nil {
    246 			return err
    247 		}
    248 		// do the default create if the group does not have a custom one
    249 		if err := os.MkdirAll(c.Path(p), defaultDirPerm); err != nil {
    250 			return err
    251 		}
    252 	}
    253 	return nil
    254 }
    255 
    256 func cleanPath(path string) string {
    257 	if path == "" {
    258 		return ""
    259 	}
    260 	path = filepath.Clean(path)
    261 	if !filepath.IsAbs(path) {
    262 		path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path))
    263 	}
    264 	return path
    265 }