diff --git a/agent/system.go b/agent/system.go index ecf1f884..26f204d9 100644 --- a/agent/system.go +++ b/agent/system.go @@ -7,12 +7,12 @@ import ( "log/slog" "os" "runtime" - "strconv" "strings" "time" "github.com/henrygd/beszel" "github.com/henrygd/beszel/agent/battery" + "github.com/henrygd/beszel/agent/zfs" "github.com/henrygd/beszel/internal/entities/container" "github.com/henrygd/beszel/internal/entities/system" @@ -107,7 +107,7 @@ func (a *Agent) refreshSystemDetails() { } // zfs - if _, err := getARCSize(); err != nil { + if _, err := zfs.ARCSize(); err != nil { slog.Debug("Not monitoring ZFS ARC", "err", err) } else { a.zfs = true @@ -178,7 +178,7 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats { // } // subtract ZFS ARC size from used memory and add as its own category if a.zfs { - if arcSize, _ := getARCSize(); arcSize > 0 && arcSize < v.Used { + if arcSize, _ := zfs.ARCSize(); arcSize > 0 && arcSize < v.Used { v.Used = v.Used - arcSize v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0 systemStats.MemZfsArc = bytesToGigabytes(arcSize) @@ -250,32 +250,6 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats { return systemStats } -// Returns the size of the ZFS ARC memory cache in bytes -func getARCSize() (uint64, error) { - file, err := os.Open("/proc/spl/kstat/zfs/arcstats") - if err != nil { - return 0, err - } - defer file.Close() - - // Scan the lines - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "size") { - // Example line: size 4 15032385536 - fields := strings.Fields(line) - if len(fields) < 3 { - return 0, err - } - // Return the size as uint64 - return strconv.ParseUint(fields[2], 10, 64) - } - } - - return 0, fmt.Errorf("failed to parse size field") -} - // getOsPrettyName attempts to get the pretty OS name from /etc/os-release on Linux systems func getOsPrettyName() (string, error) { file, err := os.Open("/etc/os-release") diff --git a/agent/zfs/zfs_freebsd.go b/agent/zfs/zfs_freebsd.go new file mode 100644 index 00000000..869776a3 --- /dev/null +++ b/agent/zfs/zfs_freebsd.go @@ -0,0 +1,11 @@ +//go:build freebsd + +package zfs + +import ( + "golang.org/x/sys/unix" +) + +func ARCSize() (uint64, error) { + return unix.SysctlUint64("kstat.zfs.misc.arcstats.size") +} diff --git a/agent/zfs/zfs_linux.go b/agent/zfs/zfs_linux.go new file mode 100644 index 00000000..9f94380d --- /dev/null +++ b/agent/zfs/zfs_linux.go @@ -0,0 +1,34 @@ +//go:build linux + +// Package zfs provides functions to read ZFS statistics. +package zfs + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func ARCSize() (uint64, error) { + file, err := os.Open("/proc/spl/kstat/zfs/arcstats") + if err != nil { + return 0, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "size") { + fields := strings.Fields(line) + if len(fields) < 3 { + return 0, fmt.Errorf("unexpected arcstats size format: %s", line) + } + return strconv.ParseUint(fields[2], 10, 64) + } + } + + return 0, fmt.Errorf("size field not found in arcstats") +} diff --git a/agent/zfs/zfs_unsupported.go b/agent/zfs/zfs_unsupported.go new file mode 100644 index 00000000..95aa8deb --- /dev/null +++ b/agent/zfs/zfs_unsupported.go @@ -0,0 +1,9 @@ +//go:build !linux && !freebsd + +package zfs + +import "errors" + +func ARCSize() (uint64, error) { + return 0, errors.ErrUnsupported +}