mirror of
https://codeberg.org/gigirassy/energonfetch.git
synced 2026-08-30 15:27:40 +00:00
Add main.go
This commit is contained in:
@@ -0,0 +1,448 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user