github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/fscommon/utils.go (about) 1 package fscommon 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "path" 8 "strconv" 9 "strings" 10 11 "github.com/opencontainers/runc/libcontainer/cgroups" 12 ) 13 14 var ( 15 // Deprecated: use cgroups.OpenFile instead. 16 OpenFile = cgroups.OpenFile 17 // Deprecated: use cgroups.ReadFile instead. 18 ReadFile = cgroups.ReadFile 19 // Deprecated: use cgroups.WriteFile instead. 20 WriteFile = cgroups.WriteFile 21 ) 22 23 // ParseError records a parse error details, including the file path. 24 type ParseError struct { 25 Path string 26 File string 27 Err error 28 } 29 30 func (e *ParseError) Error() string { 31 return "unable to parse " + path.Join(e.Path, e.File) + ": " + e.Err.Error() 32 } 33 34 func (e *ParseError) Unwrap() error { return e.Err } 35 36 // ParseUint converts a string to an uint64 integer. 37 // Negative values are returned at zero as, due to kernel bugs, 38 // some of the memory cgroup stats can be negative. 39 func ParseUint(s string, base, bitSize int) (uint64, error) { 40 value, err := strconv.ParseUint(s, base, bitSize) 41 if err != nil { 42 intValue, intErr := strconv.ParseInt(s, base, bitSize) 43 // 1. Handle negative values greater than MinInt64 (and) 44 // 2. Handle negative values lesser than MinInt64 45 if intErr == nil && intValue < 0 { 46 return 0, nil 47 } else if errors.Is(intErr, strconv.ErrRange) && intValue < 0 { 48 return 0, nil 49 } 50 51 return value, err 52 } 53 54 return value, nil 55 } 56 57 // ParseKeyValue parses a space-separated "name value" kind of cgroup 58 // parameter and returns its key as a string, and its value as uint64 59 // (ParseUint is used to convert the value). For example, 60 // "io_service_bytes 1234" will be returned as "io_service_bytes", 1234. 61 func ParseKeyValue(t string) (string, uint64, error) { 62 parts := strings.SplitN(t, " ", 3) 63 if len(parts) != 2 { 64 return "", 0, fmt.Errorf("line %q is not in key value format", t) 65 } 66 67 value, err := ParseUint(parts[1], 10, 64) 68 if err != nil { 69 return "", 0, err 70 } 71 72 return parts[0], value, nil 73 } 74 75 // GetValueByKey reads a key-value pairs from the specified cgroup file, 76 // and returns a value of the specified key. ParseUint is used for value 77 // conversion. 78 func GetValueByKey(path, file, key string) (uint64, error) { 79 content, err := cgroups.ReadFile(path, file) 80 if err != nil { 81 return 0, err 82 } 83 84 lines := strings.Split(content, "\n") 85 for _, line := range lines { 86 arr := strings.Split(line, " ") 87 if len(arr) == 2 && arr[0] == key { 88 val, err := ParseUint(arr[1], 10, 64) 89 if err != nil { 90 err = &ParseError{Path: path, File: file, Err: err} 91 } 92 return val, err 93 } 94 } 95 96 return 0, nil 97 } 98 99 // GetCgroupParamUint reads a single uint64 value from the specified cgroup file. 100 // If the value read is "max", the math.MaxUint64 is returned. 101 func GetCgroupParamUint(path, file string) (uint64, error) { 102 contents, err := GetCgroupParamString(path, file) 103 if err != nil { 104 return 0, err 105 } 106 contents = strings.TrimSpace(contents) 107 if contents == "max" { 108 return math.MaxUint64, nil 109 } 110 111 res, err := ParseUint(contents, 10, 64) 112 if err != nil { 113 return res, &ParseError{Path: path, File: file, Err: err} 114 } 115 return res, nil 116 } 117 118 // GetCgroupParamInt reads a single int64 value from specified cgroup file. 119 // If the value read is "max", the math.MaxInt64 is returned. 120 func GetCgroupParamInt(path, file string) (int64, error) { 121 contents, err := cgroups.ReadFile(path, file) 122 if err != nil { 123 return 0, err 124 } 125 contents = strings.TrimSpace(contents) 126 if contents == "max" { 127 return math.MaxInt64, nil 128 } 129 130 res, err := strconv.ParseInt(contents, 10, 64) 131 if err != nil { 132 return res, &ParseError{Path: path, File: file, Err: err} 133 } 134 return res, nil 135 } 136 137 // GetCgroupParamString reads a string from the specified cgroup file. 138 func GetCgroupParamString(path, file string) (string, error) { 139 contents, err := cgroups.ReadFile(path, file) 140 if err != nil { 141 return "", err 142 } 143 144 return strings.TrimSpace(contents), nil 145 }