1 package cgroups
2
3 import (
4 "errors"
5 "fmt"
6 "os"
7 "path/filepath"
8 "strings"
9 "sync"
10 "syscall"
11
12 securejoin "github.com/cyphar/filepath-securejoin"
13 "github.com/moby/sys/mountinfo"
14 "golang.org/x/sys/unix"
15 )
16
17
18
19
20 const (
21 CgroupNamePrefix = "name="
22 defaultPrefix = "/sys/fs/cgroup"
23 )
24
25 var (
26 errUnified = errors.New("not implemented for cgroup v2 unified hierarchy")
27 ErrV1NoUnified = errors.New("invalid configuration: cannot use unified on cgroup v1")
28
29 readMountinfoOnce sync.Once
30 readMountinfoErr error
31 cgroupMountinfo []*mountinfo.Info
32 )
33
34 type NotFoundError struct {
35 Subsystem string
36 }
37
38 func (e *NotFoundError) Error() string {
39 return fmt.Sprintf("mountpoint for %s not found", e.Subsystem)
40 }
41
42 func NewNotFoundError(sub string) error {
43 return &NotFoundError{
44 Subsystem: sub,
45 }
46 }
47
48 func IsNotFound(err error) bool {
49 var nfErr *NotFoundError
50 return errors.As(err, &nfErr)
51 }
52
53 func tryDefaultPath(cgroupPath, subsystem string) string {
54 if !strings.HasPrefix(defaultPrefix, cgroupPath) {
55 return ""
56 }
57
58
59 subsystem = strings.TrimPrefix(subsystem, CgroupNamePrefix)
60
61
62
63 path, err := securejoin.SecureJoin(defaultPrefix, subsystem)
64 if err != nil {
65 return ""
66 }
67
68
69 st, err := os.Lstat(path)
70 if err != nil || !st.IsDir() {
71 return ""
72 }
73
74
75 pst, err := os.Lstat(filepath.Dir(path))
76 if err != nil {
77 return ""
78 }
79
80 if st.Sys().(*syscall.Stat_t).Dev == pst.Sys().(*syscall.Stat_t).Dev {
81
82 return ""
83 }
84
85
86 fst := unix.Statfs_t{}
87 err = unix.Statfs(path, &fst)
88 if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
89 return ""
90 }
91
92 return path
93 }
94
95
96
97
98
99
100 func readCgroupMountinfo() ([]*mountinfo.Info, error) {
101 readMountinfoOnce.Do(func() {
102 cgroupMountinfo, readMountinfoErr = mountinfo.GetMounts(
103 mountinfo.FSTypeFilter("cgroup"),
104 )
105 })
106
107 return cgroupMountinfo, readMountinfoErr
108 }
109
110
111 func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) {
112 if IsCgroup2UnifiedMode() {
113 return "", errUnified
114 }
115
116
117 if len(subsystem) == 0 {
118 return hybridMountpoint, nil
119 }
120
121
122 if path := tryDefaultPath(cgroupPath, subsystem); path != "" {
123 return path, nil
124 }
125
126 mnt, _, err := FindCgroupMountpointAndRoot(cgroupPath, subsystem)
127 return mnt, err
128 }
129
130 func FindCgroupMountpointAndRoot(cgroupPath, subsystem string) (string, string, error) {
131 if IsCgroup2UnifiedMode() {
132 return "", "", errUnified
133 }
134
135 mi, err := readCgroupMountinfo()
136 if err != nil {
137 return "", "", err
138 }
139
140 return findCgroupMountpointAndRootFromMI(mi, cgroupPath, subsystem)
141 }
142
143 func findCgroupMountpointAndRootFromMI(mounts []*mountinfo.Info, cgroupPath, subsystem string) (string, string, error) {
144 for _, mi := range mounts {
145 if strings.HasPrefix(mi.Mountpoint, cgroupPath) {
146 for _, opt := range strings.Split(mi.VFSOptions, ",") {
147 if opt == subsystem {
148 return mi.Mountpoint, mi.Root, nil
149 }
150 }
151 }
152 }
153
154 return "", "", NewNotFoundError(subsystem)
155 }
156
157 func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) {
158 if len(m.Subsystems) == 0 {
159 return "", errors.New("no subsystem for mount")
160 }
161
162 return getControllerPath(m.Subsystems[0], cgroups)
163 }
164
165 func getCgroupMountsHelper(ss map[string]bool, mounts []*mountinfo.Info, all bool) ([]Mount, error) {
166 res := make([]Mount, 0, len(ss))
167 numFound := 0
168 for _, mi := range mounts {
169 m := Mount{
170 Mountpoint: mi.Mountpoint,
171 Root: mi.Root,
172 }
173 for _, opt := range strings.Split(mi.VFSOptions, ",") {
174 seen, known := ss[opt]
175 if !known || (!all && seen) {
176 continue
177 }
178 ss[opt] = true
179 opt = strings.TrimPrefix(opt, CgroupNamePrefix)
180 m.Subsystems = append(m.Subsystems, opt)
181 numFound++
182 }
183 if len(m.Subsystems) > 0 || all {
184 res = append(res, m)
185 }
186 if !all && numFound >= len(ss) {
187 break
188 }
189 }
190 return res, nil
191 }
192
193 func getCgroupMountsV1(all bool) ([]Mount, error) {
194 mi, err := readCgroupMountinfo()
195 if err != nil {
196 return nil, err
197 }
198
199 allSubsystems, err := ParseCgroupFile("/proc/self/cgroup")
200 if err != nil {
201 return nil, err
202 }
203
204 allMap := make(map[string]bool)
205 for s := range allSubsystems {
206 allMap[s] = false
207 }
208
209 return getCgroupMountsHelper(allMap, mi, all)
210 }
211
212
213 func GetOwnCgroup(subsystem string) (string, error) {
214 if IsCgroup2UnifiedMode() {
215 return "", errUnified
216 }
217 cgroups, err := ParseCgroupFile("/proc/self/cgroup")
218 if err != nil {
219 return "", err
220 }
221
222 return getControllerPath(subsystem, cgroups)
223 }
224
225 func GetOwnCgroupPath(subsystem string) (string, error) {
226 cgroup, err := GetOwnCgroup(subsystem)
227 if err != nil {
228 return "", err
229 }
230
231
232 if len(subsystem) == 0 {
233 return hybridMountpoint, nil
234 }
235
236 return getCgroupPathHelper(subsystem, cgroup)
237 }
238
239 func GetInitCgroup(subsystem string) (string, error) {
240 if IsCgroup2UnifiedMode() {
241 return "", errUnified
242 }
243 cgroups, err := ParseCgroupFile("/proc/1/cgroup")
244 if err != nil {
245 return "", err
246 }
247
248 return getControllerPath(subsystem, cgroups)
249 }
250
251 func GetInitCgroupPath(subsystem string) (string, error) {
252 cgroup, err := GetInitCgroup(subsystem)
253 if err != nil {
254 return "", err
255 }
256
257 return getCgroupPathHelper(subsystem, cgroup)
258 }
259
260 func getCgroupPathHelper(subsystem, cgroup string) (string, error) {
261 mnt, root, err := FindCgroupMountpointAndRoot("", subsystem)
262 if err != nil {
263 return "", err
264 }
265
266
267
268 relCgroup, err := filepath.Rel(root, cgroup)
269 if err != nil {
270 return "", err
271 }
272
273 return filepath.Join(mnt, relCgroup), nil
274 }
275
276 func getControllerPath(subsystem string, cgroups map[string]string) (string, error) {
277 if IsCgroup2UnifiedMode() {
278 return "", errUnified
279 }
280
281 if p, ok := cgroups[subsystem]; ok {
282 return p, nil
283 }
284
285 if p, ok := cgroups[CgroupNamePrefix+subsystem]; ok {
286 return p, nil
287 }
288
289 return "", NewNotFoundError(subsystem)
290 }
291
View as plain text