58 lines
1.5 KiB
Go
58 lines
1.5 KiB
Go
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// Package system captures one cached, injectable view of live host state.
|
|
package system
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model"
|
|
)
|
|
|
|
// Capture reads process state once from procRoot. Transient per-process read
|
|
// failures degrade to empty fields, matching Python's fail-open Process view.
|
|
func Capture(procRoot string) (model.State, error) {
|
|
if procRoot == "" {
|
|
procRoot = "/proc"
|
|
}
|
|
entries, err := os.ReadDir(procRoot)
|
|
if err != nil {
|
|
return model.State{}, err
|
|
}
|
|
processes := make([]model.Process, 0, len(entries))
|
|
for _, entry := range entries {
|
|
pid, err := strconv.Atoi(entry.Name())
|
|
if err != nil || !entry.IsDir() {
|
|
continue
|
|
}
|
|
base := filepath.Join(procRoot, entry.Name())
|
|
processes = append(processes, model.Process{
|
|
PID: pid,
|
|
Comm: strings.TrimSpace(readText(filepath.Join(base, "comm"))),
|
|
Cmdline: strings.TrimSpace(strings.ReplaceAll(readText(filepath.Join(base, "cmdline")), "\x00", " ")),
|
|
Exe: readLink(filepath.Join(base, "exe")),
|
|
})
|
|
}
|
|
sort.Slice(processes, func(i, j int) bool { return processes[i].PID < processes[j].PID })
|
|
return model.State{Processes: processes}, nil
|
|
}
|
|
|
|
func readText(path string) string {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(raw)
|
|
}
|
|
|
|
func readLink(path string) string {
|
|
target, err := os.Readlink(path)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return target
|
|
}
|