61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
//go:build !windows
|
|
|
|
package hoststats
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// memStats reads host RAM usage from /proc/meminfo. used is derived from
|
|
// MemAvailable (kernel's own "usable without swapping" estimate, accounts
|
|
// for reclaimable cache/buffers) rather than MemTotal-MemFree, which would
|
|
// count page cache as "used" and make a healthy box look starved.
|
|
func memStats() (used, total int64, err error) {
|
|
f, err := os.Open("/proc/meminfo")
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
defer f.Close()
|
|
|
|
var totalKB, availKB int64
|
|
haveTotal, haveAvail := false, false
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
switch {
|
|
case strings.HasPrefix(line, "MemTotal:"):
|
|
totalKB, err = parseMeminfoKB(line)
|
|
haveTotal = err == nil
|
|
case strings.HasPrefix(line, "MemAvailable:"):
|
|
availKB, err = parseMeminfoKB(line)
|
|
haveAvail = err == nil
|
|
}
|
|
if haveTotal && haveAvail {
|
|
break
|
|
}
|
|
}
|
|
if scanErr := scanner.Err(); scanErr != nil {
|
|
return 0, 0, scanErr
|
|
}
|
|
if !haveTotal || !haveAvail {
|
|
return 0, 0, fmt.Errorf("hoststats: MemTotal/MemAvailable not found in /proc/meminfo")
|
|
}
|
|
total = totalKB * 1024
|
|
used = total - availKB*1024
|
|
if used < 0 {
|
|
used = 0
|
|
}
|
|
return used, total, nil
|
|
}
|
|
|
|
func parseMeminfoKB(line string) (int64, error) {
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
return 0, fmt.Errorf("hoststats: malformed /proc/meminfo line %q", line)
|
|
}
|
|
return strconv.ParseInt(fields[1], 10, 64)
|
|
}
|