From 71e4f1294e793c2c551b88b8b4bd10280efbd28d Mon Sep 17 00:00:00 2001 From: Dottere Date: Mon, 5 Oct 2026 14:17:03 +0200 Subject: [PATCH] skeleton for testing purposes --- cmd/rpi5-health/main.go | 23 ++++++++++++++ go.mod | 5 +++ go.sum | 2 ++ internal/cli/cli.go | 36 ++++++++++++++++++++++ internal/errors/UnsupportedOS.go | 13 ++++++++ internal/http_internal/server.go | 30 ++++++++++++++++++ internal/system_check/linux_ram.go | 49 ++++++++++++++++++++++++++++++ internal/system_check/ram.go | 32 +++++++++++++++++++ internal/system_check/win.go | 28 +++++++++++++++++ internal/system_check/win_ram.go | 44 +++++++++++++++++++++++++++ web/index.html | 11 +++++++ 11 files changed, 273 insertions(+) create mode 100644 cmd/rpi5-health/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/cli/cli.go create mode 100644 internal/errors/UnsupportedOS.go create mode 100644 internal/http_internal/server.go create mode 100644 internal/system_check/linux_ram.go create mode 100644 internal/system_check/ram.go create mode 100644 internal/system_check/win.go create mode 100644 internal/system_check/win_ram.go create mode 100644 web/index.html diff --git a/cmd/rpi5-health/main.go b/cmd/rpi5-health/main.go new file mode 100644 index 0000000..9e4ca29 --- /dev/null +++ b/cmd/rpi5-health/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + "rpi5-health/internal/cli" + "rpi5-health/internal/http_internal" + "rpi5-health/internal/system_check" +) + +func main() { + srv := http_internal.NewAppServer() + go srv.Start() + + memInfo, _ := system_check.GetRamInformation() + + const GB = 1024 * 1024 * 1024 + + log.Printf("Total memory: %dGB\n", memInfo.SystemMemory/GB) + log.Printf("Available memory: %dGB\n", memInfo.AvailableMemory/GB) + log.Printf("Used memory: %dGB\n", memInfo.GetUsedMemory()/GB) + + cli.Listen() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..59baad0 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module rpi5-health + +go 1.26.4 + +require golang.org/x/sys v0.48.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e3bf0e4 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= diff --git a/internal/cli/cli.go b/internal/cli/cli.go new file mode 100644 index 0000000..16725ac --- /dev/null +++ b/internal/cli/cli.go @@ -0,0 +1,36 @@ +package cli + +import ( + "bufio" + "fmt" + "log" + "os" + "strings" +) + +func Listen() { + reader := bufio.NewScanner(os.Stdin) + + fmt.Print("> ") + + for { + + if !reader.Scan() { + log.Println("Program terminated (SIGINT)") + return + } + + if err := reader.Err(); err != nil { + log.Printf("Console input error: %v\n", err) + return + } + + text := strings.TrimSpace(reader.Text()) + + switch text { + case "exit": + return + } + } + +} diff --git a/internal/errors/UnsupportedOS.go b/internal/errors/UnsupportedOS.go new file mode 100644 index 0000000..9e73338 --- /dev/null +++ b/internal/errors/UnsupportedOS.go @@ -0,0 +1,13 @@ +package errors + +func New(text string) error { + return &errorString{text} +} + +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} diff --git a/internal/http_internal/server.go b/internal/http_internal/server.go new file mode 100644 index 0000000..ab66df3 --- /dev/null +++ b/internal/http_internal/server.go @@ -0,0 +1,30 @@ +package http_internal + +import "net/http" + +type AppServer struct { + srv *http.Server + mux *http.ServeMux +} + +func NewAppServer() *AppServer { + mux := http.NewServeMux() + + fs := http.FileServer(http.Dir("web/")) + + mux.Handle("/", fs) + + srv := &http.Server{ + Addr: "localhost:1234", + Handler: mux, + } + + return &AppServer{ + srv: srv, + mux: mux, + } +} + +func (srv *AppServer) Start() { + srv.srv.ListenAndServe() +} diff --git a/internal/system_check/linux_ram.go b/internal/system_check/linux_ram.go new file mode 100644 index 0000000..bf9c9c6 --- /dev/null +++ b/internal/system_check/linux_ram.go @@ -0,0 +1,49 @@ +package system_check + +import ( + "bufio" + "fmt" + "os" + "regexp" + "strconv" + "strings" +) + +var numberRegex *regexp.Regexp = regexp.MustCompile(`\d+`) + +func getLinuxRamInfo() (MemoryInformation, error) { + memoryFile, err := os.Open("/proc/meminfo") + if err != nil { + return MemoryInformation{}, fmt.Errorf("Could not find /proc/meminfo. Are you sure you are on linux?") + } + fileReader := bufio.NewScanner(memoryFile) + + var TotalMemory uint64 + var AvailMemory uint64 + + for fileReader.Scan() { + + if fileReader.Err() != nil { + return MemoryInformation{}, fmt.Errorf("There was an error while reading /proc/meninfo. Error: %v", fileReader.Err()) + } + + line := fileReader.Text() + + if strings.Contains(line, "MemTotal") { + TotalMemory, err = strconv.ParseUint(numberRegex.FindString(line), 10, 64) + if err != nil { + return MemoryInformation{}, fmt.Errorf("Could not parse total memory from /proc/meminfo") + } + } else if strings.Contains(line, "MemAvailable") { + AvailMemory, err = strconv.ParseUint(numberRegex.FindString(line), 10, 64) + if err != nil { + return MemoryInformation{}, fmt.Errorf("Could not parse available memory from /proc/meminfo") + } + } + } + + return MemoryInformation{ + SystemMemory: TotalMemory, + AvailableMemory: AvailMemory, + }, nil +} diff --git a/internal/system_check/ram.go b/internal/system_check/ram.go new file mode 100644 index 0000000..7918f37 --- /dev/null +++ b/internal/system_check/ram.go @@ -0,0 +1,32 @@ +package system_check + +import ( + "fmt" + "runtime" +) + +type MemoryInformation struct { + /* + * The total amount of memory the system has available + */ + SystemMemory uint64 + /* + * How much memory is currently unused + */ + AvailableMemory uint64 +} + +func (memInfo MemoryInformation) GetUsedMemory() uint64 { + return memInfo.SystemMemory - memInfo.AvailableMemory +} + +func GetRamInformation() (MemoryInformation, error) { + switch runtime.GOOS { + case "windows": + return getWinRamInfo(getWindowsTools().ProcGlobalMemoryStatusEx), nil + case "linux": + return getLinuxRamInfo() + default: + return MemoryInformation{}, fmt.Errorf("Unsupported OS: \"%s\"", runtime.GOOS) + } +} diff --git a/internal/system_check/win.go b/internal/system_check/win.go new file mode 100644 index 0000000..daf01d0 --- /dev/null +++ b/internal/system_check/win.go @@ -0,0 +1,28 @@ +package system_check + +import ( + "sync" + + "golang.org/x/sys/windows" +) + +type WindowsSystemCheckTools struct { + /* + * Win32 API + */ + Kernel32 *windows.LazyDLL + /* + * Windows memory information + */ + ProcGlobalMemoryStatusEx *windows.LazyProc +} + +func getWindowsTools() WindowsSystemCheckTools { + kernel32 := sync.OnceValue(func() *windows.LazyDLL { return windows.NewLazySystemDLL("kernel32.dll") }) + procGlobalMemoryStatusEx := sync.OnceValue(func() *windows.LazyProc { return kernel32().NewProc("GlobalMemoryStatusEx") }) + + return WindowsSystemCheckTools{ + Kernel32: kernel32(), + ProcGlobalMemoryStatusEx: procGlobalMemoryStatusEx(), + } +} diff --git a/internal/system_check/win_ram.go b/internal/system_check/win_ram.go new file mode 100644 index 0000000..dbf8a22 --- /dev/null +++ b/internal/system_check/win_ram.go @@ -0,0 +1,44 @@ +package system_check + +import ( + "log" + "unsafe" + + "golang.org/x/sys/windows" +) + +type memoryStatusEx struct { + /* + * Struct for storing memory status information. + * + * It gets populated via syscall. + */ + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +func getWinRamInfo(procGlobalMemoryStatusEx *windows.LazyProc) MemoryInformation { + var mem memoryStatusEx + + // * Windows requires the dwLength field to be set to the struct's size + // * before execution so it knows which OS struct version is being used. + mem.dwLength = uint32(unsafe.Sizeof(mem)) + + ret, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&mem))) // exec syscall + + if ret == 0 { // winapi syscalls return 0 on failure + log.Fatalf("GlobalMemoryStatusEx failed: %v", err) + } + + return MemoryInformation{ + SystemMemory: mem.ullTotalPhys, + AvailableMemory: mem.ullAvailPhys, + } +} diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..fe810b2 --- /dev/null +++ b/web/index.html @@ -0,0 +1,11 @@ + + + + + + Document + + +

Hello world!

+ +