package main import ( "bytes" "fmt" "os" "runtime" "strconv" "strings" "syscall" ) var cube = []string{ " +------+", " / /|", " +------+ |", " | | +", " | |/", " +------+", } func main() { info := collectAll() values := make([]string, 0, len(info)) keys := make([]string, 0, len(info)) for _, kv := range info { values = append(values, strings.ToLower(kv.v)) keys = append(keys, strings.ToLower(kv.k)) } printAligned(values, keys, cube) } type pair struct{ k, v string } func collectAll() []pair { var out []pair distro := readPrettyOSRelease("/etc/os-release") if distro == "" { distro = runtime.GOOS } out = append(out, pair{"os", distro}) out = append(out, pair{"arch", runtime.GOARCH}) out = append(out, pair{"kernel", getUname()}) cpuModel, cpuHz := detectCPU() if cpuHz > 0 { if cpuHz >= 1e9 { ghz := float64(cpuHz) / 1e9 out = append(out, pair{"cpu", fmt.Sprintf("%s-%.2f ghz", cpuModel, ghz)}) } else { out = append(out, pair{"cpu", fmt.Sprintf("%s-%d mhz", cpuModel, cpuHz/1e6)}) } } else { out = append(out, pair{"cpu", cpuModel}) } memTotal, memAvail, swapTotal, swapFree := parseMemInfo() if memTotal > 0 { memUsed := memTotal - memAvail out = append(out, pair{"memory", fmt.Sprintf("%s / %s", hr(memUsed), hr(memTotal))}) } if swapTotal > 0 { swapUsed := swapTotal - swapFree out = append(out, pair{"swap", fmt.Sprintf("%s / %s", hr(swapUsed), hr(swapTotal))}) } out = append(out, pair{"desktop", detectDesktop()}) used, total, fstype := getStorageAndType("/") out = append(out, pair{"storage", fmt.Sprintf("%s / %s %s", hr(used), hr(total), fstype)}) return out } func readPrettyOSRelease(path string) string { if b, err := os.ReadFile(path); err == nil { for _, ln := range bytes.Split(b, []byte{'\n'}) { if bytes.HasPrefix(ln, []byte("PRETTY_NAME=")) { val := strings.Trim(string(bytes.TrimPrefix(ln, []byte("PRETTY_NAME="))), `"`) return strings.TrimSpace(val) } } name := parseKey(bytesToLower(b), "name=") version := parseKey(bytesToLower(b), "version=") if name != "" && version != "" { return strings.TrimSpace(name + " " + version) } if name != "" { return name } } return "" } func parseKey(b []byte, key string) string { for _, ln := range bytes.Split(b, []byte{'\n'}) { if bytes.HasPrefix(ln, []byte(key)) { val := bytes.TrimPrefix(ln, []byte(key)) val = bytes.Trim(val, `"' `) return string(val) } } return "" } func bytesToLower(b []byte) []byte { return bytes.ToLower(b) } func getUname() string { var u syscall.Utsname if err := syscall.Uname(&u); err == nil { return fromUts(u.Release[:]) } return "unknown" } func fromUts(a []int8) string { var b []byte for _, c := range a { if c == 0 { break } b = append(b, byte(c)) } return string(b) } func detectCPU() (model string, hz uint64) { kPaths := []string{ "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", } for _, p := range kPaths { if b, err := os.ReadFile(p); err == nil { if n, err := strconv.ParseUint(strings.TrimSpace(string(b)), 10, 64); err == nil && n > 0 { hz = n * 1000 model = readCPUModelFromProc() if model == "" { model = "unknown" } return model, hz } } } if b, err := os.ReadFile("/proc/cpuinfo"); err == nil { lower := bytes.ToLower(b) if m := findFirstField(lower, "model name"); m != "" { model = trimField(m) } else if m := findFirstField(lower, "hardware"); m != "" { model = trimField(m) } else if m := findFirstField(lower, "processor"); m != "" { model = trimField(m) } else if m := findFirstField(lower, "model"); m != "" { model = trimField(m) } else { model = "unknown" } if mhz := findFirstField(lower, "cpu mhz"); mhz != "" { if f, err := strconv.ParseFloat(strings.TrimSpace(mhz), 64); err == nil && f > 0 { hz = uint64(f * 1e6) } } return model, hz } return "unknown", 0 } func readCPUModelFromProc() string { if b, err := os.ReadFile("/proc/cpuinfo"); err == nil { lower := bytes.ToLower(b) if m := findFirstField(lower, "model name"); m != "" { return trimField(m) } if m := findFirstField(lower, "hardware"); m != "" { return trimField(m) } } return "" } func findFirstField(b []byte, key string) string { t := []byte(key + ":") if i := bytes.Index(b, t); i != -1 { if j := bytes.IndexByte(b[i:], '\n'); j != -1 { line := b[i : i+j] parts := bytes.SplitN(line, []byte{':'}, 2) if len(parts) == 2 { return string(parts[1]) } } else { parts := bytes.SplitN(b[i:], []byte{':'}, 2) if len(parts) == 2 { return string(parts[1]) } } } t2 := []byte(key + "\t:") if i := bytes.Index(b, t2); i != -1 { if j := bytes.IndexByte(b[i:], '\n'); j != -1 { line := b[i : i+j] parts := bytes.SplitN(line, []byte{':'}, 2) if len(parts) == 2 { return string(parts[1]) } } } t3 := []byte(key + "=") if i := bytes.Index(b, t3); i != -1 { if j := bytes.IndexByte(b[i:], '\n'); j != -1 { line := b[i : i+j] parts := bytes.SplitN(line, []byte{'='}, 2) if len(parts) == 2 { return string(parts[1]) } } } return "" } func trimField(s string) string { return strings.TrimSpace(strings.Trim(s, `"`)) } func parseMemInfo() (memTotal, memAvailable, swapTotal, swapFree uint64) { if b, err := os.ReadFile("/proc/meminfo"); err == nil { for _, ln := range bytes.Split(b, []byte{'\n'}) { if len(ln) == 0 { continue } l := bytes.ToLower(ln) fields := bytes.Fields(l) if len(fields) < 2 { continue } key := string(fields[0]) valStr := string(fields[1]) n, err := strconv.ParseUint(valStr, 10, 64) if err != nil { continue } switch key { case "memtotal:": memTotal = n * 1024 case "memavailable:": memAvailable = n * 1024 case "swaptotal:": swapTotal = n * 1024 case "swapfree:": swapFree = n * 1024 } } } return } func getStorageAndType(path string) (used, total uint64, fstype string) { var st syscall.Statfs_t if syscall.Statfs(path, &st) == nil { total = st.Blocks * uint64(st.Bsize) avail := st.Bavail * uint64(st.Bsize) used = total - avail } if b, err := os.ReadFile("/proc/mounts"); err == nil { lines := bytes.Split(b, []byte{'\n'}) bestLen := -1 for _, ln := range lines { if len(ln) == 0 { continue } parts := bytes.Fields(ln) if len(parts) < 3 { continue } mp := string(parts[1]) if strings.HasPrefix(path, mp) && len(mp) > bestLen { bestLen = len(mp) fstype = string(parts[2]) } } } if fstype == "" { fstype = "fs" } return used, total, strings.ToLower(fstype) } func detectDesktop() string { envs := []string{ "XDG_CURRENT_DESKTOP", "DESKTOP_SESSION", "XDG_SESSION_DESKTOP", "GDMSESSION", "SWAYSOCK", } for _, e := range envs { if v := os.Getenv(e); v != "" { return strings.ToLower(strings.Split(v, ":")[0]) } } if os.Getenv("WAYLAND_DISPLAY") != "" { return "wayland" } if os.Getenv("DISPLAY") != "" { return "x11" } return "tty" } func printAligned(values, keys []string, cube []string) { n := len(values) cubeH := len(cube) valW := 0 keyW := 0 for _, v := range values { if visibleLen(v) > valW { valW = visibleLen(v) } } for _, k := range keys { if visibleLen(k) > keyW { keyW = visibleLen(k) } } cubeCol := valW + 2 + 3 + keyW + 2 lines := n if cubeH > lines { lines = cubeH } cubeStart := 0 if n > cubeH { cubeStart = (n - cubeH) / 2 } for i := 0; i < lines; i++ { left := "" mid := "" if i < n { left = values[i] mid = "<- " + bold(keys[i]) } line := "" if left != "" || mid != "" { if left != "" { line += padRight(left, valW) } else { line += strings.Repeat(" ", valW) } line += " " if mid != "" { key := keys[i] paddedKey := padRight(key, keyW) mid = "<- " + bold(paddedKey) line += mid } } vis := visibleLen(line) if pad := cubeCol - vis; pad > 0 { line += strings.Repeat(" ", pad) } cIdx := i - cubeStart if cIdx >= 0 && cIdx < cubeH { line += cube[cIdx] } fmt.Println(line) } } func bold(s string) string { return "\033[1m" + s + "\033[0m" } func padRight(s string, w int) string { if visibleLen(s) >= w { return s } return s + strings.Repeat(" ", w-visibleLen(s)) } func visibleLen(s string) int { n := 0 esc := false for i := 0; i < len(s); i++ { c := s[i] if c == '\033' { esc = true continue } if esc { if c == 'm' { esc = false } continue } n++ } return n } func hr(b uint64) string { const ( KB = 1024 MB = KB * 1024 GB = MB * 1024 ) if b >= GB { f := float64(b) / float64(GB) return fmt.Sprintf("%.2f gb", f) } if b >= MB { return fmt.Sprintf("%.0f mb", float64(b)/float64(MB)) } return fmt.Sprintf("%d kb", b/KB) }