Files
AetherForge/agent/stats/cpu_darwin.go
AetherForge 1551bd5dad feat: Emberwake, Crucible phases, Linux agent, musical dashboard, e2e
Emberwake spread/waterhole UI, campaign DB, spread handler, spread-kit web publisher, and SPREAD_TECHNIQUES doc. Crucible Phase A-C: expanded ops, port-forward matrix, remote dir browser, crucible help/tests.

Linux agent hardening: credential vault, persistence audit, firewall/defender deploy, SMB spread status, CPU stats, screenshots/crypt/file-ops split. Docker compose and agent/server images with e2e validation script and docs.

Musical dashboard: ambient music player, hover SFX, SoundContext/AmbientMusicContext, steampunk polish. Public builds API, dropper handler updates, SessionGate and fleet UX. README and PROBLEMS.md refresh.
2026-06-04 21:53:31 -07:00

63 lines
1.1 KiB
Go

//go:build darwin
package stats
import (
"os/exec"
"strconv"
"strings"
)
func (r *Reporter) SystemCPUPercent() float64 {
idle, total, ok := readDarwinCPUSample()
if !ok {
return 0
}
r.mu.Lock()
defer r.mu.Unlock()
if !r.hasSample {
r.lastIdle = idle
r.lastTotal = total
r.hasSample = true
return 0
}
idleDelta := float64(idle - r.lastIdle)
totalDelta := float64(total - r.lastTotal)
r.lastIdle = idle
r.lastTotal = total
return cpuBusyPercentFromDeltas(idleDelta, totalDelta)
}
func readDarwinCPUSample() (idle, total uint64, ok bool) {
out, err := exec.Command("sysctl", "-n", "kern.cp_time").Output()
if err != nil {
return 0, 0, false
}
return parseDarwinCPTimes(string(out))
}
func parseDarwinCPTimes(raw string) (idle, total uint64, ok bool) {
parts := strings.Fields(strings.TrimSpace(raw))
if len(parts) < 4 {
return 0, 0, false
}
var values []uint64
for _, p := range parts {
v, err := strconv.ParseUint(p, 10, 64)
if err != nil {
return 0, 0, false
}
values = append(values, v)
}
for _, v := range values {
total += v
}
// user, nice, sys, idle[, intr]
idle = values[3]
return idle, total, true
}