github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/common/cgroup_util.go (about) 1 // Copyright 2014 The rkt Authors 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 //+build linux 16 17 package common 18 19 // adapted from systemd/src/shared/cgroup-util.c 20 // TODO this should be moved to go-systemd 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "regexp" 27 "strings" 28 ) 29 30 const ( 31 unitNameMax = 256 32 ) 33 34 var ( 35 validChars = regexp.MustCompile(`[a-zA-Z0-9:\-_\.\\]+`) 36 ) 37 38 // cgEscape implements very minimal escaping for names to be used as file names 39 // in the cgroup tree: any name which might conflict with a kernel name or is 40 // prefixed with '_' is prefixed with a '_'. That way, when reading cgroup 41 // names it is sufficient to remove a single prefixing underscore if there is 42 // one. 43 func cgEscape(p string) string { 44 needPrefix := false 45 46 switch { 47 case strings.HasPrefix(p, "_"): 48 fallthrough 49 case strings.HasPrefix(p, "."): 50 fallthrough 51 case p == "notify_on_release": 52 fallthrough 53 case p == "release_agent": 54 fallthrough 55 case p == "tasks": 56 needPrefix = true 57 case strings.Contains(p, "."): 58 sp := strings.Split(p, ".") 59 if sp[0] == "cgroup" { 60 needPrefix = true 61 } else { 62 n := sp[0] 63 if checkHierarchy(n) { 64 needPrefix = true 65 } 66 } 67 } 68 69 if needPrefix { 70 return "_" + p 71 } 72 73 return p 74 } 75 76 func filenameIsValid(p string) bool { 77 switch { 78 case p == "", p == ".", p == "..", strings.Contains(p, "/"): 79 return false 80 default: 81 return true 82 } 83 } 84 85 func checkHierarchy(p string) bool { 86 if !filenameIsValid(p) { 87 return true 88 } 89 90 cc := filepath.Join("/sys/fs/cgroup", p) 91 if _, err := os.Stat(cc); os.IsNotExist(err) { 92 return false 93 } 94 95 return true 96 } 97 98 func cgUnescape(p string) string { 99 if p[0] == '_' { 100 return p[1:] 101 } 102 103 return p 104 } 105 106 func sliceNameIsValid(n string) bool { 107 if n == "" { 108 return false 109 } 110 111 if len(n) >= unitNameMax { 112 return false 113 } 114 115 if !strings.Contains(n, ".") { 116 return false 117 } 118 119 if validChars.FindString(n) != n { 120 return false 121 } 122 123 if strings.Contains(n, "@") { 124 return false 125 } 126 127 return true 128 } 129 130 // SliceToPath explodes a slice name to its corresponding path in the cgroup 131 // hierarchy. For example, a slice named "foo-bar-baz.slice" corresponds to the 132 // path "foo.slice/foo-bar.slice/foo-bar-baz.slice". See systemd.slice(5) 133 func SliceToPath(unit string) (string, error) { 134 if unit == "-.slice" { 135 return "", nil 136 } 137 138 if !strings.HasSuffix(unit, ".slice") { 139 return "", fmt.Errorf("not a slice") 140 } 141 142 if !sliceNameIsValid(unit) { 143 return "", fmt.Errorf("invalid slice name") 144 } 145 146 prefix := unitnameToPrefix(unit) 147 148 // don't allow initial dashes 149 if prefix[0] == '-' { 150 return "", fmt.Errorf("initial dash") 151 } 152 153 prefixParts := strings.Split(prefix, "-") 154 155 var curSlice string 156 var slicePath string 157 for _, slicePart := range prefixParts { 158 if slicePart == "" { 159 return "", fmt.Errorf("trailing or double dash") 160 } 161 162 if curSlice != "" { 163 curSlice = curSlice + "-" 164 } 165 curSlice = curSlice + slicePart 166 167 curSliceDir := curSlice + ".slice" 168 escaped := cgEscape(curSliceDir) 169 170 slicePath = filepath.Join(slicePath, escaped) 171 } 172 173 return slicePath, nil 174 } 175 176 func unitnameToPrefix(unit string) string { 177 idx := strings.Index(unit, "@") 178 if idx == -1 { 179 idx = strings.LastIndex(unit, ".") 180 } 181 182 return unit[:idx] 183 }