diff --git a/cmd/rpi5-health/main.go b/cmd/rpi5-health/main.go index 897de1e..b9f891b 100644 --- a/cmd/rpi5-health/main.go +++ b/cmd/rpi5-health/main.go @@ -6,6 +6,7 @@ import ( "rpi5-health/internal/http_internal" "rpi5-health/internal/memory" "rpi5-health/internal/processor" + "rpi5-health/internal/uptime" ) func main() { @@ -22,11 +23,13 @@ func main() { _, kernel, usage, idle := processor.GetCPUInformation().Usage() - const HOUR = 60 * 60 - log.Printf("Total time spent idling: %.2f%%\n", idle) log.Printf("Total time spent in kernel mode: %.2f%%\n", kernel) log.Printf("Total time spent in user mode: %.2f%%\n", usage) + uptime, _ := uptime.GetUptimeInformation() + + log.Printf("Total uptime: %s", uptime.FormatSeconds()) + cli.Listen() } diff --git a/internal/uptime/linux_uptime.go b/internal/uptime/linux_uptime.go new file mode 100644 index 0000000..215a7ee --- /dev/null +++ b/internal/uptime/linux_uptime.go @@ -0,0 +1,50 @@ +//go:build linux + +package uptime + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func getLinuxUptime() (UptimeInformation, error) { + uptimeFile, err := os.Open("/proc/uptime") + if err != nil { + return UptimeInformation{}, fmt.Errorf("Could not find /proc/uptime. Are you sure you are on linux?") + } + defer uptimeFile.Close() + + fileReader := bufio.NewScanner(uptimeFile) + + var busyTime float64 + + fileReader.Scan() + if fileReader.Err() != nil { + return UptimeInformation{}, fmt.Errorf("Could not read /proc/uptime.") + } + + data := fileReader.Text() + + /* + * fields[0] Time spent busy/suspended + * fields[1] Time spent idling, it's combined from all CPU cores + */ + fields := strings.Split(data, " ") + + busyTime, _ = strconv.ParseFloat(fields[0], 64) + + return UptimeInformation{ + uptime: busyTime, + }, nil +} + +func GetUptimeInformation() (UptimeInformation, error) { + info, err := getLinuxUptime() + if err != nil { + return UptimeInformation{}, fmt.Errorf("Could not read uptime info. Error: %v", err) + } + return info, nil +} diff --git a/internal/uptime/uptime.go b/internal/uptime/uptime.go new file mode 100644 index 0000000..8f06aab --- /dev/null +++ b/internal/uptime/uptime.go @@ -0,0 +1,41 @@ +package uptime + +import ( + "fmt" + "math" +) + +type UptimeInformation struct { + uptime float64 +} + +func (uptimeInfo UptimeInformation) GetUptime() (seconds float64) { + return uptimeInfo.uptime +} + +func (uptimeInfo UptimeInformation) FormatSeconds() string { + total := int64(math.Round(uptimeInfo.uptime)) + + if total < 60 { + return fmt.Sprintf("%d", total) + } + s := total % 60 + m := (total / 60) % 60 + + if total < 3600 { + // e.g., 5:03 + return fmt.Sprintf("%d:%02d", m, s) + } + + h := (total / 3600) % 24 + + if total < 86400 { + // e.g., 2:05:03 + return fmt.Sprintf("%d:%02d:%02d", h, m, s) + } + + d := total / 86400 + + // e.g., 1:02:05:03 + return fmt.Sprintf("%d:%02d:%02d:%02d", d, h, m, s) +} diff --git a/internal/uptime/win_uptime.go b/internal/uptime/win_uptime.go new file mode 100644 index 0000000..b696cc8 --- /dev/null +++ b/internal/uptime/win_uptime.go @@ -0,0 +1,23 @@ +//go:build windows + +package uptime + +import ( + "rpi5-health/internal/windows_system" + + "golang.org/x/sys/windows" +) + +type ULONGLONG uint64 + +func getWinUptime(uptime *windows.LazyProc) (UptimeInformation, error) { + uptimeMetric, _, _ := uptime.Call() + + uptimeSeconds := float64(uptimeMetric) / 1000.0 + + return UptimeInformation{uptime: uptimeSeconds}, nil +} + +func GetUptimeInformation() (UptimeInformation, error) { + return getWinUptime(windows_system.GetWindowsTools().Uptime) +} diff --git a/internal/windows_system/win.go b/internal/windows_system/win.go index 590fa4b..a4e73a5 100644 --- a/internal/windows_system/win.go +++ b/internal/windows_system/win.go @@ -21,16 +21,22 @@ type WindowsSystemCheckTools struct { * Windows processor information */ SystemTimes *windows.LazyProc + /* + * Windows uptime metrics + */ + Uptime *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") }) systemTimes := sync.OnceValue(func() *windows.LazyProc { return kernel32().NewProc("GetSystemTimes") }) + uptime := sync.OnceValue(func() *windows.LazyProc { return kernel32().NewProc("GetTickCount64") }) return WindowsSystemCheckTools{ Kernel32: kernel32(), ProcGlobalMemoryStatusEx: procGlobalMemoryStatusEx(), SystemTimes: systemTimes(), + Uptime: uptime(), } }