From b1f0ce01fb6ece8628cf0499690a003cdac28f7f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 14 Feb 2026 17:29:08 +0200 Subject: Remove macOS stats gathering support, require Linux with /proc Remove non-functional macOS monitoring code that used native tools (sysctl, vm_stat, netstat, iostat). Now loadbars exits with an error when attempting local stats gathering on non-Linux systems. macOS can still be used as a client to monitor remote Linux servers via SSH. --- AGENTS.md | 18 +++---- README.md | 4 +- internal/collector/collector.go | 13 +++-- internal/collector/loadbars-remote-darwin.sh | 77 ---------------------------- internal/collector/script.go | 5 -- internal/version/version.go | 2 +- 6 files changed, 17 insertions(+), 102 deletions(-) delete mode 100644 internal/collector/loadbars-remote-darwin.sh diff --git a/AGENTS.md b/AGENTS.md index 63531fd..ab005e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,33 +66,27 @@ Network interface: chosen by `netint` config or first non-`lo`; press `n` to cyc - `go test ./...` or `mage test`. No UI tests; collector has parse tests for protocol (e.g. `ParseNetLine` for Linux-style `/proc/net/dev` output). -## macOS support +## macOS support (client only) -Loadbars supports macOS with automatic window activation and OS-specific monitoring scripts. +Loadbars supports macOS as a client to monitor remote Linux servers via SSH. ### Implementation details -- **Script embedding:** Both Linux and Darwin monitoring scripts are embedded in the binary -- **OS detection:** Automatically uses the correct script based on the host: - - `localhost` on macOS → Darwin script (sysctl, vm_stat, netstat, iostat) - - `localhost` on Linux → Linux script (/proc filesystem) - - All remote hosts → Linux script (assumes remote servers are Linux) - **Window activation:** macOS-specific code automatically brings SDL window to foreground - Uses build tags (`activate_darwin.go` and `activate.go`) - No external helper script needed ### Key files -- `internal/collector/script.go` - Embeds both scripts -- `internal/collector/collector.go` - Selects script based on host (checks if `/proc` exists for localhost) +- `internal/collector/script.go` - Embeds the Linux monitoring script +- `internal/collector/collector.go` - Runs Linux script locally or over SSH; exits with error if local system lacks /proc - `internal/display/activate_darwin.go` - macOS-specific activation using `open -a` - `internal/display/activate.go` - No-op for other platforms ### Known limitations -- Remote macOS hosts are not supported (assumes all remote hosts are Linux) -- macOS script doesn't provide per-core CPU statistics (iostat limitation) -- Swap usage on macOS always shows 0 (macOS uses compressed memory differently) +- Local monitoring on macOS is not supported (requires Linux with /proc filesystem) +- All remote hosts must be Linux (assumes all remote hosts are Linux) ## Useful references diff --git a/README.md b/README.md index fb3fe53..5fe02ea 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Loadbars is a tool that can be used to observe CPU loads of several remote serve This version of loadbars has been tested on: - Fedora Linux 43 and most modern Linux distributions (RHEL, CentOS, Ubuntu, Debian, etc.) -- macOS (Darwin) - localhost monitoring uses native macOS tools (sysctl, vm_stat, netstat, iostat) +- macOS (Darwin) - can connect to remote Linux servers via SSH (local monitoring not supported) -**Note:** Remote hosts are assumed to be Linux (using /proc filesystem). When running on macOS, localhost monitoring uses macOS-specific commands, while remote hosts use the Linux script. So remote hosts only work with Linux. +**Note:** Local monitoring requires Linux with /proc filesystem. Remote hosts must be Linux (using /proc filesystem). macOS can be used as a client to monitor remote Linux servers. ## Build and run diff --git a/internal/collector/collector.go b/internal/collector/collector.go index cab9df0..91f3d7b 100644 --- a/internal/collector/collector.go +++ b/internal/collector/collector.go @@ -25,13 +25,16 @@ type StatsStore interface { // The script is embedded in the binary; no external script file is required. func Run(ctx context.Context, host string, cfg *config.Config, store StatsStore) error { hostKey, user := splitHostUser(host) - - // Select script: Darwin for localhost on macOS, Linux for everything else (all remotes are Linux) + + // Select script: Only Linux supported for local monitoring scriptBytes := LinuxScript if isLocal(hostKey) { scriptBytes = getLocalScript() + if scriptBytes == nil { + return fmt.Errorf("%s: local stats gathering requires Linux with /proc filesystem", hostKey) + } } - + script := bytes.NewReader(scriptBytes) var scanner *bufio.Scanner if isLocal(hostKey) { @@ -135,6 +138,6 @@ func getLocalScript() []byte { if _, err := exec.Command("test", "-d", "/proc").CombinedOutput(); err == nil { return LinuxScript } - // Otherwise assume macOS - return DarwinScript + // /proc not found - unsupported OS for local stats gathering + return nil } diff --git a/internal/collector/loadbars-remote-darwin.sh b/internal/collector/loadbars-remote-darwin.sh deleted file mode 100644 index f82c802..0000000 --- a/internal/collector/loadbars-remote-darwin.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# loadbars-remote-darwin.sh - macOS version using sysctl, vm_stat, netstat, and iostat -# Emits loadbars protocol (M LOADAVG, M MEMSTATS, M NETSTATS, M CPUSTATS) -# Interval for CPU sampling (seconds) -INTERVAL=0.14 - -# Get number of CPUs -NCPU=$(sysctl -n hw.ncpu) - -while true; do - # Load average: from sysctl - echo "M LOADAVG" - sysctl -n vm.loadavg 2>/dev/null | awk '{print $2";"$3";"$4}' || echo "0;0;0" - - # Memory: convert vm_stat output to /proc/meminfo-like format - echo "M MEMSTATS" - vm_stat 2>/dev/null | awk ' - BEGIN { pagesize = 4096 } - /page size of ([0-9]+)/ { pagesize = $8 } - /Pages free:/ { free = $3 * pagesize / 1024 } - /Pages active:/ { active = $3 * pagesize / 1024 } - /Pages inactive:/ { inactive = $3 * pagesize / 1024 } - /Pages speculative:/ { speculative = $3 * pagesize / 1024 } - /Pages wired down:/ { wired = $4 * pagesize / 1024 } - /Pages occupied by compressor:/ { compressed = $5 * pagesize / 1024 } - END { - total = free + active + inactive + speculative + wired + compressed - printf "MemTotal: %d kB\n", total - printf "MemFree: %d kB\n", free - printf "MemAvailable: %d kB\n", free + inactive + speculative - printf "SwapTotal: 0 kB\n" - printf "SwapFree: 0 kB\n" - } - ' - - # Network: use netstat -ibn for interface stats - echo "M NETSTATS" - netstat -ibn 2>/dev/null | awk ' - NR > 1 && $1 !~ /^Name/ && $3 ~ /^/dev/null | tail -1 | awk -v ncpu="$NCPU" ' - { - # iostat columns: %user %nice %sys %idle (approximately) - # Multiply by ncpu to get total ticks (simulated) - user = int($3 * ncpu * 10) - sys = int($4 * ncpu * 10) - idle = int($5 * ncpu * 10) - - # Output in /proc/stat format - printf "cpu %d 0 %d %d 0 0 0 0 0 0\n", user, sys, idle - } - ' - sleep "$INTERVAL" 2>/dev/null || true - done -done diff --git a/internal/collector/script.go b/internal/collector/script.go index 3be2190..5dc9dce 100644 --- a/internal/collector/script.go +++ b/internal/collector/script.go @@ -6,8 +6,3 @@ import _ "embed" // //go:embed loadbars-remote.sh var LinuxScript []byte - -// DarwinScript contains the embedded loadbars-remote-darwin.sh script for macOS hosts -// -//go:embed loadbars-remote-darwin.sh -var DarwinScript []byte diff --git a/internal/version/version.go b/internal/version/version.go index 67d9c60..2b4ace2 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,4 +1,4 @@ package version // Version is the application version (set at build time or here for development). -const Version = "0.8.3" +const Version = "0.8.4" -- cgit v1.2.3