//go:build linux package metrics import ( "bufio" "fmt" "os" "runtime" "strings" "time" ) // GetStaticInfo reads host-level static system information. // Reads from /proc and /etc (which reflect the host in Docker containers). func GetStaticInfo() StaticSystemInfo { info := StaticSystemInfo{} // Hostname — try host mount first, fall back to os.Hostname() if data, err := os.ReadFile("/host/etc/hostname"); err == nil { info.Hostname = strings.TrimSpace(string(data)) } else { info.Hostname, _ = os.Hostname() } // OS — try host mount first, fall back to container's info.OS = readOSRelease("/host/etc/os-release") if info.OS == "" { info.OS = readOSRelease("/etc/os-release") } // Kernel version if data, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil { info.Kernel = strings.TrimSpace(string(data)) } // Architecture if data, err := os.ReadFile("/proc/sys/kernel/arch"); err == nil { info.Architecture = strings.TrimSpace(string(data)) } if info.Architecture == "" { // Fallback: use uname -m equivalent from /proc/cpuinfo or runtime info.Architecture = runtime.GOARCH // Try to get the actual host arch if data, err := os.ReadFile("/proc/version"); err == nil { v := string(data) if strings.Contains(v, "x86_64") { info.Architecture = "x86_64" } else if strings.Contains(v, "aarch64") { info.Architecture = "aarch64" } } } // CPU model and cores info.CPUModel, info.CPUCores = readCPUInfo() if info.CPUCores == 0 { info.CPUCores = runtime.NumCPU() } // Uptime if data, err := os.ReadFile("/proc/uptime"); err == nil { var uptimeSec float64 if _, err := fmt.Sscanf(string(data), "%f", &uptimeSec); err == nil { info.UptimeSeconds = int64(uptimeSec) info.BootTime = time.Now().Add(-time.Duration(info.UptimeSeconds) * time.Second) } } return info } // readOSRelease reads PRETTY_NAME from an os-release file. func readOSRelease(path string) string { f, err := os.Open(path) if err != nil { return "" } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "PRETTY_NAME=") { val := strings.TrimPrefix(line, "PRETTY_NAME=") val = strings.Trim(val, `"`) return val } } return "" } // readCPUInfo reads the CPU model name and number of cores from /proc/cpuinfo. func readCPUInfo() (model string, cores int) { f, err := os.Open("/proc/cpuinfo") if err != nil { return "", 0 } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "model name") { if idx := strings.Index(line, ":"); idx >= 0 { model = strings.TrimSpace(line[idx+1:]) } } if strings.HasPrefix(line, "processor") { cores++ } } return model, cores }