Feat[linux]: sysinfo
This commit is contained in:
parent
c7eafdbc52
commit
2e58f576ff
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module git.tcp.direct/kayos/common
|
|||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
|
||||
inet.af/netaddr v0.0.0-20220811202034-502d2d690317
|
||||
|
|
2
go.sum
2
go.sum
|
@ -1,3 +1,5 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//go:build linux
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
some interesting information on RAM, sysinfo, and /proc/meminfo
|
||||
|
||||
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
|
||||
- https://github.com/mmalecki/procps/blob/master/proc/sysinfo.c
|
||||
|
||||
in the second link we see that even procps is parsing /proc/meminfo to get the RAM information
|
||||
i'd really like to avoid this, and I may end up estimating the available memory myself...
|
||||
the whole idea of this package is to focus on system calls and not on parsing files
|
||||
|
||||
for now I'll just add a note to the RAMInfo struct definition.
|
||||
|
||||
tldr; sysinfo is a bit incomplete due to it's lack of available memory calculation
|
||||
*/
|
||||
|
||||
// from https://man7.org/linux/man-pages/man2/sysinfo.2.html
|
||||
/*
|
||||
struct sysinfo {
|
||||
long uptime; // Seconds since boot
|
||||
unsigned long loads[3]; // 1, 5, and 15 minute load averages
|
||||
unsigned long totalram; // Total usable main memory size
|
||||
unsigned long freeram; // Available memory size
|
||||
unsigned long sharedram; // Amount of shared memory
|
||||
unsigned long bufferram; // Memory used by buffers
|
||||
unsigned long totalswap; // Total swap space size
|
||||
unsigned long freeswap; // Swap space still available
|
||||
unsigned short procs; // Number of current processes
|
||||
unsigned long totalhigh; // Total high memory size
|
||||
unsigned long freehigh; // Available high memory size
|
||||
unsigned int mem_unit; // Memory unit size in bytes
|
||||
char _f[20-2*sizeof(long)-sizeof(int)]; // Padding to 64 bytes
|
||||
};
|
||||
*/
|
||||
|
||||
type SystemInfo struct {
|
||||
Uptime time.Duration
|
||||
Loads [3]uint64
|
||||
|
||||
RAM RAMInfo
|
||||
|
||||
// Totalswap is the total amount of swap space available.
|
||||
Totalswap uint64
|
||||
// Freeswap is the amount of swap space still available.
|
||||
Freeswap uint64
|
||||
// Procs is the number of current processes.
|
||||
Procs uint16
|
||||
}
|
||||
|
||||
// RAMInfo is a struct that contains information about the running system's memory.
|
||||
// Please see important notes in the struct definition.
|
||||
type RAMInfo struct {
|
||||
Total int64
|
||||
// Free is the amount of memory that is not being used.
|
||||
// This does not take into account memory that is being used for caching.
|
||||
// For more information, see the comments at the top of this file.
|
||||
Free int64
|
||||
Used int64
|
||||
Shared int64
|
||||
Buffers int64
|
||||
Cached int64
|
||||
SwapTotal int64
|
||||
SwapFree int64
|
||||
|
||||
// unit is the memory unit size multiple in bytes.
|
||||
// It is used to calculate the above values when the struct is initialized.q
|
||||
unit uint32
|
||||
}
|
||||
|
||||
// Sysinfo returns a SystemInfo struct containing information about the running system.
|
||||
// For memory, please see important notes in the RAMInfo struct definition and the top of this file.
|
||||
func Sysinfo() (systemInfo *SystemInfo, err error) {
|
||||
sysinf := syscall.Sysinfo_t{}
|
||||
err = syscall.Sysinfo(&sysinf)
|
||||
unit := uint64(sysinf.Unit)
|
||||
return &SystemInfo{
|
||||
Uptime: time.Duration(sysinf.Uptime) * time.Second,
|
||||
Loads: [3]uint64{sysinf.Loads[0], sysinf.Loads[1], sysinf.Loads[2]},
|
||||
RAM: RAMInfo{
|
||||
Total: int64(sysinf.Totalram * unit),
|
||||
Free: int64(sysinf.Freeram * unit),
|
||||
Used: int64((sysinf.Totalram - sysinf.Freeram) * unit),
|
||||
Shared: int64(sysinf.Sharedram * unit),
|
||||
Buffers: int64(sysinf.Bufferram * unit),
|
||||
Cached: int64((sysinf.Totalram - sysinf.Freeram - sysinf.Bufferram) * unit),
|
||||
SwapTotal: int64(sysinf.Totalswap * unit),
|
||||
SwapFree: int64(sysinf.Freeswap * unit),
|
||||
unit: sysinf.Unit,
|
||||
},
|
||||
Totalswap: sysinf.Totalswap,
|
||||
Freeswap: sysinf.Freeswap,
|
||||
Procs: sysinf.Procs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Uptime() (utime time.Duration, err error) {
|
||||
var sysinf *SystemInfo
|
||||
sysinf, err = Sysinfo()
|
||||
return sysinf.Uptime, nil
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
//go:build linux
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func TestUptime(t *testing.T) {
|
||||
f, err := os.Open("/proc/uptime")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open /proc/uptime with error: %v", err)
|
||||
}
|
||||
buf, err := io.ReadAll(f)
|
||||
|
||||
// calling Uptime() here to try and get the same time as the file
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read /proc/uptime with error: %v", err)
|
||||
}
|
||||
t.Logf("read %d bytes from /proc/uptime: %s", len(buf), buf)
|
||||
|
||||
controlSeconds, err := strconv.ParseInt(string(buf[:strings.IndexByte(string(buf), '.')]), 10, 64)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse /proc/uptime with error: %e", err)
|
||||
}
|
||||
if controlSeconds < 1 {
|
||||
t.Fatalf("failed to parse /proc/uptime")
|
||||
}
|
||||
uptimeCtrl := time.Duration(controlSeconds) * time.Second
|
||||
t.Logf("control uptime: %v", uptimeCtrl)
|
||||
|
||||
// ---
|
||||
|
||||
uptime, err := Uptime()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get uptime with error: %e", err)
|
||||
}
|
||||
|
||||
if uptime < 1 {
|
||||
t.Fatalf("failed to get uptime (zero value)")
|
||||
}
|
||||
|
||||
t.Logf("uptime: %v", uptime)
|
||||
|
||||
matching := uptime == uptimeCtrl
|
||||
|
||||
// if somehow the uptime is less than the control, which was called first
|
||||
// then this has failed terribly.
|
||||
// If it's greater, then it's possible we are within an acceptable tolerance.
|
||||
|
||||
if !matching && uptime < uptimeCtrl {
|
||||
t.Fatalf("uptime does not match control uptime (uptime < uptimeCtrl)!!")
|
||||
}
|
||||
|
||||
if !matching {
|
||||
t.Logf("no match, allowing for a 1 second tolerance...")
|
||||
matching = uptime == uptimeCtrl+time.Second
|
||||
if matching {
|
||||
t.Logf("success! (uptime == uptimeCtrl+time.Second)")
|
||||
}
|
||||
}
|
||||
|
||||
if !matching {
|
||||
t.Fatalf("uptime does not match control uptime: %v != %v", uptime, uptimeCtrl)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSysinfo(t *testing.T) {
|
||||
si, err := Sysinfo()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get sysinfo with error: %e", err)
|
||||
}
|
||||
if si == nil {
|
||||
t.Fatalf("failed to get sysinfo (nil)")
|
||||
}
|
||||
|
||||
t.Logf("sysinfo: %s", spew.Sdump(si))
|
||||
}
|
|
@ -49,7 +49,7 @@ const (
|
|||
// GetUname uses system calls to retrieve the same values as the uname linux command
|
||||
func GetUname(unameFlags string) (un string, err error) {
|
||||
ub := &syscall.Utsname{}
|
||||
_ = syscall.Uname(ub)
|
||||
err = syscall.Uname(ub)
|
||||
var targets []*[65]int8
|
||||
for _, n := range unameFlags {
|
||||
var flag = [1]string{string(n)}
|
||||
|
@ -76,7 +76,7 @@ func GetUname(unameFlags string) (un string, err error) {
|
|||
return "", errors.New("no valid uname targets in string")
|
||||
}
|
||||
|
||||
var uns []string
|
||||
var uns = make([]string, len(targets))
|
||||
for _, target := range targets {
|
||||
var sub []string
|
||||
for _, r := range target {
|
||||
|
|
Loading…
Reference in New Issue