carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package cgroup
2
3import (
4 "bufio"
5 "errors"
6 "fmt"
7 "os"
8 "path/filepath"
9 "strings"
10)
11
12// hostProcsMaxDepth bounds the walk below a host's cgroup. The platform
13// nests at most a few levels (exec group, role child, generation); a tree
14// deeper than this is not one it made, and is reported rather than walked.
15const hostProcsMaxDepth = 16
16
17func hostProcs(root, host, unit string) (int, error) {
18 if root == "" {
19 return 0, ErrUnknown
20 }
21 if host == "" || strings.ContainsAny(host, "/\x00") || host == "." || host == ".." {
22 return 0, fmt.Errorf("cgroup: %q is not a host name", host)
23 }
24 n, err := countProcs(filepath.Join(root, ExecName(host)), 0)
25 if err != nil {
26 return 0, err
27 }
28 if unit != "" {
29 if !filepath.IsAbs(unit) {
30 return 0, fmt.Errorf("cgroup: the unit cgroup %q is not an absolute path", unit)
31 }
32 m, err := countProcs(filepath.Clean(unit), 0)
33 if err != nil {
34 return 0, err
35 }
36 n += m
37 }
38 return n, nil
39}
40
41// countProcs is the number of pids listed in dir's cgroup.procs and in every
42// cgroup below it. An absent dir is 0. Symlinks are never followed: cgroupfs
43// has none, so a link is not a cgroup.
44func countProcs(dir string, depth int) (int, error) {
45 if depth > hostProcsMaxDepth {
46 return 0, fmt.Errorf("cgroup: %s is nested deeper than %d levels", dir, hostProcsMaxDepth)
47 }
48 st, err := os.Lstat(dir)
49 if errors.Is(err, os.ErrNotExist) {
50 return 0, nil
51 }
52 if err != nil {
53 return 0, err
54 }
55 if !st.IsDir() {
56 return 0, fmt.Errorf("cgroup: %s is not a cgroup directory", dir)
57 }
58 n, err := readProcsFile(filepath.Join(dir, "cgroup.procs"))
59 if err != nil {
60 return 0, err
61 }
62 ents, err := os.ReadDir(dir)
63 if errors.Is(err, os.ErrNotExist) {
64 return n, nil // removed while read: its processes are gone with it
65 }
66 if err != nil {
67 return 0, err
68 }
69 for _, e := range ents {
70 if !e.IsDir() || e.Type()&os.ModeSymlink != 0 {
71 continue
72 }
73 m, err := countProcs(filepath.Join(dir, e.Name()), depth+1)
74 if err != nil {
75 return 0, err
76 }
77 n += m
78 }
79 return n, nil
80}
81
82// readProcsFile counts the non-empty lines of a cgroup.procs file. A file
83// that is gone (the cgroup was removed between the listing and the read)
84// lists nothing.
85func readProcsFile(path string) (int, error) {
86 f, err := os.Open(path)
87 if errors.Is(err, os.ErrNotExist) {
88 return 0, nil
89 }
90 if err != nil {
91 return 0, err
92 }
93 defer f.Close()
94 n := 0
95 sc := bufio.NewScanner(f)
96 for sc.Scan() {
97 if strings.TrimSpace(sc.Text()) != "" {
98 n++
99 }
100 }
101 if err := sc.Err(); err != nil {
102 // A cgroup removed while it was read answers ENODEV.
103 if _, statErr := os.Stat(filepath.Dir(path)); errors.Is(statErr, os.ErrNotExist) {
104 return 0, nil
105 }
106 return 0, err
107 }
108 return n, nil
109}
110