164 lines
3.6 KiB
Go
164 lines
3.6 KiB
Go
package trafficmode
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func CgroupCandidates(entry string, cgroupRootPath string) []string {
|
|
v := strings.TrimSpace(entry)
|
|
if v == "" {
|
|
return nil
|
|
}
|
|
vc := filepath.Clean(v)
|
|
vals := []string{}
|
|
if filepath.IsAbs(vc) {
|
|
if strings.HasPrefix(vc, cgroupRootPath) {
|
|
vals = append(vals, vc)
|
|
} else {
|
|
vals = append(vals, filepath.Join(cgroupRootPath, strings.TrimPrefix(vc, "/")))
|
|
}
|
|
} else {
|
|
vals = append(vals,
|
|
filepath.Join(cgroupRootPath, strings.TrimPrefix(vc, "/")),
|
|
filepath.Join(cgroupRootPath, "system.slice", strings.TrimPrefix(vc, "/")),
|
|
filepath.Join(cgroupRootPath, "user.slice", strings.TrimPrefix(vc, "/")),
|
|
)
|
|
}
|
|
seen := map[string]struct{}{}
|
|
out := make([]string, 0, len(vals))
|
|
for _, p := range vals {
|
|
cp := filepath.Clean(p)
|
|
if cp == "." || cp == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[cp]; ok {
|
|
continue
|
|
}
|
|
seen[cp] = struct{}{}
|
|
out = append(out, cp)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func ResolveCgroupPath(entry string, cgroupRootPath string) (string, string) {
|
|
for _, cand := range CgroupCandidates(entry, cgroupRootPath) {
|
|
fi, err := os.Stat(cand)
|
|
if err != nil || !fi.IsDir() {
|
|
continue
|
|
}
|
|
return cand, ""
|
|
}
|
|
return "", "cgroup not found: " + strings.TrimSpace(entry)
|
|
}
|
|
|
|
func CollectPIDsFromCgroup(root string) (map[int]struct{}, string) {
|
|
const (
|
|
maxDirs = 5000
|
|
maxPIDs = 50000
|
|
)
|
|
|
|
pids := map[int]struct{}{}
|
|
dirs := 0
|
|
warn := ""
|
|
|
|
_ = filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil || d == nil || !d.IsDir() {
|
|
return nil
|
|
}
|
|
dirs++
|
|
if dirs > maxDirs {
|
|
warn = "cgroup scan truncated by directory limit"
|
|
return filepath.SkipDir
|
|
}
|
|
data, err := os.ReadFile(filepath.Join(path, "cgroup.procs"))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
for _, ln := range strings.Split(string(data), "\n") {
|
|
ln = strings.TrimSpace(ln)
|
|
if ln == "" {
|
|
continue
|
|
}
|
|
pid, err := strconv.Atoi(ln)
|
|
if err != nil || pid <= 0 {
|
|
continue
|
|
}
|
|
pids[pid] = struct{}{}
|
|
if len(pids) > maxPIDs {
|
|
warn = "cgroup scan truncated by pid limit"
|
|
return filepath.SkipDir
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
return pids, warn
|
|
}
|
|
|
|
func UIDRangeForPID(pid int) (string, bool) {
|
|
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
for _, ln := range strings.Split(string(data), "\n") {
|
|
ln = strings.TrimSpace(ln)
|
|
if !strings.HasPrefix(ln, "Uid:") {
|
|
continue
|
|
}
|
|
fields := strings.Fields(ln)
|
|
if len(fields) < 2 {
|
|
return "", false
|
|
}
|
|
v, ok := NormalizeUIDToken(fields[1])
|
|
return v, ok
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func ResolveCgroupUIDRanges(entries []string, cgroupRootPath string) ([]string, string) {
|
|
var uids []string
|
|
var warnings []string
|
|
|
|
for _, entry := range NormalizeCgroupList(entries) {
|
|
root, warn := ResolveCgroupPath(entry, cgroupRootPath)
|
|
if root == "" {
|
|
if warn != "" {
|
|
warnings = append(warnings, warn)
|
|
}
|
|
continue
|
|
}
|
|
pids, scanWarn := CollectPIDsFromCgroup(root)
|
|
if scanWarn != "" {
|
|
warnings = append(warnings, scanWarn)
|
|
}
|
|
if len(pids) == 0 {
|
|
warnings = append(warnings, "cgroup has no processes: "+entry)
|
|
continue
|
|
}
|
|
for pid := range pids {
|
|
uidRange, ok := UIDRangeForPID(pid)
|
|
if !ok || uidRange == "" {
|
|
continue
|
|
}
|
|
uids = append(uids, uidRange)
|
|
}
|
|
}
|
|
seenWarn := map[string]struct{}{}
|
|
uniqWarn := make([]string, 0, len(warnings))
|
|
for _, w := range warnings {
|
|
ww := strings.TrimSpace(w)
|
|
if ww == "" {
|
|
continue
|
|
}
|
|
if _, ok := seenWarn[ww]; ok {
|
|
continue
|
|
}
|
|
seenWarn[ww] = struct{}{}
|
|
uniqWarn = append(uniqWarn, ww)
|
|
}
|
|
return NormalizeUIDList(uids), strings.Join(uniqWarn, "; ")
|
|
}
|