From a76295c455a67b3895191949568cf3debbc86f4a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 21 Mar 2025 14:56:58 +0200 Subject: [PATCH] Moved go files to src/ folder --- Makefile | 2 +- README.md | 2 +- go.mod => src/go.mod | 0 go.sum => src/go.sum | 0 src/hardware.go | 70 +++++ main.go => src/main.go | 16 +- src/memory.go | 57 ++++ src/network.go | 15 + partitions.go => src/partitions.go | 0 pms.go => src/pms.go | 0 src/system.go | 153 ++++++++++ src/user.go | 127 ++++++++ src/utils.go | 58 ++++ utils.go | 449 ----------------------------- 14 files changed, 490 insertions(+), 459 deletions(-) rename go.mod => src/go.mod (100%) rename go.sum => src/go.sum (100%) create mode 100644 src/hardware.go rename main.go => src/main.go (96%) create mode 100644 src/memory.go create mode 100644 src/network.go rename partitions.go => src/partitions.go (100%) rename pms.go => src/pms.go (100%) create mode 100644 src/system.go create mode 100644 src/user.go create mode 100644 src/utils.go delete mode 100644 utils.go diff --git a/Makefile b/Makefile index a73cae5..285c09b 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ endif build: mkdir -p build - $(GO) build -ldflags "-w -X 'main.systemConfigDir=$(SYSCONFDIR)'" -o build/stormfetch stormfetch + cd src; $(GO) build -ldflags "-w -X 'main.systemConfigDir=$(SYSCONFDIR)'" -o ../build/stormfetch stormfetch install: build/stormfetch config/ mkdir -p $(DESTDIR)$(BINDIR) diff --git a/README.md b/README.md index 8f4bf9c..aa60d7d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## A simple linux fetch program written in go and bash ### Developers: -- [EnumDev](https://gitlab.com/EnumDev) +- [EnumDev](https://enumerated.dev) ### Project Information Stormfetch is a program that can read your system's information and display it in the terminal along with the ASCII art of the Linux distribution you are running. diff --git a/go.mod b/src/go.mod similarity index 100% rename from go.mod rename to src/go.mod diff --git a/go.sum b/src/go.sum similarity index 100% rename from go.sum rename to src/go.sum diff --git a/src/hardware.go b/src/hardware.go new file mode 100644 index 0000000..f4ecb55 --- /dev/null +++ b/src/hardware.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "github.com/go-gl/glfw/v3.3/glfw" + "github.com/jackmordaunt/ghw" + "os" + "os/exec" + "strings" +) + +func GetCPUModel() string { + cpu, err := ghw.CPU() + if err != nil { + return "" + } + if len(cpu.Processors) == 0 { + return "" + } + return cpu.Processors[0].Model +} + +func GetCPUThreads() int { + cpu, err := ghw.CPU() + if err != nil { + return 0 + } + return int(cpu.TotalThreads) +} + +func GetGPUModels() []string { + var ret []string + cmd := exec.Command("/bin/bash", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:' | sed 's/^Device://' | awk '{$1=$1};1'") + bytes, err := cmd.Output() + if err != nil { + return nil + } + for _, name := range strings.Split(string(bytes), "\n") { + name = strings.TrimSpace(name) + if name == "" { + continue + } + ret = append(ret, name) + } + return ret +} + +func GetMotherboardModel() string { + bytes, err := os.ReadFile("/sys/devices/virtual/dmi/id/board_name") + if err != nil { + return "" + } + return strings.TrimSpace(string(bytes)) +} + +func GetMonitorResolution() []string { + var monitors []string + if GetDisplayProtocol() != "" { + err := glfw.Init() + if err != nil { + panic(err) + } + for _, monitor := range glfw.GetMonitors() { + mode := monitor.GetVideoMode() + monitors = append(monitors, fmt.Sprintf("%dx%d %dHz", mode.Width, mode.Height, mode.RefreshRate)) + } + defer glfw.Terminate() + } + return monitors +} diff --git a/main.go b/src/main.go similarity index 96% rename from main.go rename to src/main.go index 325454c..939c7d0 100644 --- a/main.go +++ b/src/main.go @@ -129,11 +129,11 @@ func SetupFetchEnv(showTimeTaken bool) []string { } } setVariable("PACKAGES", func() string { return GetInstalledPackages() }) - setVariable("DISTRO_LONG_NAME", func() string { return getDistroInfo().LongName }) - setVariable("DISTRO_SHORT_NAME", func() string { return getDistroInfo().ShortName }) - setVariable("CPU_MODEL", func() string { return getCPUName() }) - setVariable("MOTHERBOARD", func() string { return getMotherboardModel() }) - setVariable("CPU_THREADS", func() string { return strconv.Itoa(getCPUThreads()) }) + setVariable("DISTRO_LONG_NAME", func() string { return GetDistroInfo().LongName }) + setVariable("DISTRO_SHORT_NAME", func() string { return GetDistroInfo().ShortName }) + setVariable("CPU_MODEL", func() string { return GetCPUModel() }) + setVariable("MOTHERBOARD", func() string { return GetMotherboardModel() }) + setVariable("CPU_THREADS", func() string { return strconv.Itoa(GetCPUThreads()) }) start := time.Now().UnixMilli() memory := GetMemoryInfo() end := time.Now().UnixMilli() @@ -174,7 +174,7 @@ func SetupFetchEnv(showTimeTaken bool) []string { setVariable("INIT_SYSTEM", func() string { return GetInitSystem() }) setVariable("LOCAL_IPV4", func() string { return GetLocalIP() }) start = time.Now().UnixMilli() - monitors := getMonitorResolution() + monitors := GetMonitorResolution() end = time.Now().UnixMilli() if showTimeTaken { fmt.Println(fmt.Sprintf("Setting '%s' took %d milliseconds", "MONITOR_*", end-start)) @@ -186,7 +186,7 @@ func SetupFetchEnv(showTimeTaken bool) []string { } } start = time.Now().UnixMilli() - gpus := getGPUNames() + gpus := GetGPUModels() end = time.Now().UnixMilli() if showTimeTaken { fmt.Println(fmt.Sprintf("Setting '%s' took %d milliseconds", "GPU_*", end-start)) @@ -224,7 +224,7 @@ func runStormfetch() { } } setColorMap() - ascii := getDistroAsciiArt() + ascii := GetDistroAsciiArt() if strings.HasPrefix(ascii, "#/") { firstLine := strings.Split(ascii, "\n")[0] if !config.ForceConfigAnsii { diff --git a/src/memory.go b/src/memory.go new file mode 100644 index 0000000..5884c7d --- /dev/null +++ b/src/memory.go @@ -0,0 +1,57 @@ +package main + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +type Memory struct { + MemTotal int + MemFree int + MemAvailable int +} + +func GetMemoryInfo() *Memory { + toInt := func(raw string) int { + if raw == "" { + return 0 + } + res, err := strconv.Atoi(raw) + if err != nil { + panic(err) + } + return res + } + + parseLine := func(raw string) (key string, value int) { + text := strings.ReplaceAll(raw[:len(raw)-2], " ", "") + keyValue := strings.Split(text, ":") + return keyValue[0], toInt(keyValue[1]) + } + + if _, err := os.Stat("/proc/meminfo"); err != nil { + return nil + } + file, err := os.Open("/proc/meminfo") + if err != nil { + panic(err) + } + defer file.Close() + bufio.NewScanner(file) + scanner := bufio.NewScanner(file) + res := Memory{} + for scanner.Scan() { + key, value := parseLine(scanner.Text()) + switch key { + case "MemTotal": + res.MemTotal = value / 1024 + case "MemFree": + res.MemFree = value / 1024 + case "MemAvailable": + res.MemAvailable = value / 1024 + } + } + return &res +} diff --git a/src/network.go b/src/network.go new file mode 100644 index 0000000..1693b68 --- /dev/null +++ b/src/network.go @@ -0,0 +1,15 @@ +package main + +import "net" + +func GetLocalIP() string { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return "" + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP.String() +} diff --git a/partitions.go b/src/partitions.go similarity index 100% rename from partitions.go rename to src/partitions.go diff --git a/pms.go b/src/pms.go similarity index 100% rename from pms.go rename to src/pms.go diff --git a/src/system.go b/src/system.go new file mode 100644 index 0000000..a29327c --- /dev/null +++ b/src/system.go @@ -0,0 +1,153 @@ +package main + +import ( + "github.com/mitchellh/go-ps" + "os" + "os/exec" + "path" + "strings" +) + +type DistroInfo struct { + ID string + LongName string + ShortName string +} + +func GetDistroInfo() DistroInfo { + info := DistroInfo{ + ID: "unknown", + LongName: "Unknown", + ShortName: "Unknown", + } + if strings.TrimSpace(config.DistroName) != "" { + info.LongName = strings.TrimSpace(config.DistroName) + info.ShortName = strings.TrimSpace(config.DistroName) + } + var releaseMap = make(map[string]string) + if _, err := os.Stat("/etc/os-release"); err == nil { + releaseMap, err = ReadKeyValueFile("/etc/os-release") + if err != nil { + return info + } + } + if id, ok := releaseMap["ID"]; ok { + info.ID = id + } + if longName, ok := releaseMap["PRETTY_NAME"]; ok && info.LongName == "Unknown" { + info.LongName = longName + } + if shortName, ok := releaseMap["NAME"]; ok && info.ShortName == "Unknown" { + info.ShortName = shortName + } + return info +} + +func GetDistroAsciiArt() string { + defaultAscii := + ` .--. + |o_o | + |:_/ | + // \ \ + (| | ) +/'\_ _/'\ +\___)=(___/ ` + var id string + if config.Ascii == "auto" { + id = GetDistroInfo().ID + } else { + id = config.Ascii + } + userConfDir, err := os.UserConfigDir() + if err != nil { + if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil { + bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id)) + if err != nil { + return defaultAscii + } + return string(bytes) + } else { + return defaultAscii + } + } + if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", id)); err == nil { + bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", id)) + if err != nil { + return defaultAscii + } + return string(bytes) + } else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil { + bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id)) + if err != nil { + return defaultAscii + } + return strings.TrimRight(string(bytes), "\n\t ") + } else { + return defaultAscii + } +} + +func GetInitSystem() string { + runCommand := func(command string) string { + cmd := exec.Command("/bin/bash", "-c", command) + workdir, err := os.Getwd() + if err != nil { + return "" + } + cmd.Dir = workdir + cmd.Env = os.Environ() + out, err := cmd.Output() + if err != nil { + return "" + } + return strings.TrimSpace(string(out)) + } + + process, err := ps.FindProcess(1) + if err != nil { + return "" + } + + // Special cases + // OpenRC check + if _, err := os.Stat("/usr/sbin/openrc"); err == nil { + return "OpenRC " + runCommand("openrc --version | awk '{print $3}'") + } + + // Default PID 1 process name checking + switch process.Executable() { + case "systemd": + return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'") + case "runit": + return "Runit" + case "dinit": + return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'") + case "enit": + return "Enit " + runCommand("enit --version | awk '{print $3}'") + default: + return process.Executable() + } +} + +func GetLibc() string { + checkLibcOutput, err := exec.Command("ldd", "/usr/bin/ls").Output() + if err != nil { + return "Unknown" + } + + if strings.Contains(string(checkLibcOutput), "ld-musl") { + // Using Musl Libc + output, _ := exec.Command("ldd").CombinedOutput() + return "Musl " + strings.TrimPrefix(strings.Split(strings.TrimSpace(string(output)), "\n")[1], "Version ") + } else { + // Using Glibc + cmd := exec.Command("ldd", "--version") + output, err := cmd.Output() + if err != nil { + return "Glibc" + } + outputSplit := strings.Split(strings.Split(strings.TrimSpace(string(output)), "\n")[0], " ") + ver := outputSplit[len(outputSplit)-1] + return "Glibc " + ver + } +} diff --git a/src/user.go b/src/user.go new file mode 100644 index 0000000..049c4de --- /dev/null +++ b/src/user.go @@ -0,0 +1,127 @@ +package main + +import ( + "github.com/mitchellh/go-ps" + "log" + "os" + "os/exec" + "path/filepath" + "slices" + "strconv" + "strings" +) + +func GetShell() string { + runCommand := func(command string) string { + cmd := exec.Command("/bin/bash", "-c", command) + workdir, err := os.Getwd() + if err != nil { + return "" + } + cmd.Dir = workdir + cmd.Env = os.Environ() + out, err := cmd.Output() + if err != nil { + return "" + } + return strings.TrimSpace(string(out)) + } + file, err := os.ReadFile("/etc/passwd") + if err != nil { + return "" + } + str := string(file) + shell := "" + + for _, line := range strings.Split(str, "\n") { + if strings.TrimSpace(line) == "" { + continue + } + userInfo := strings.Split(line, ":") + if userInfo[2] == strconv.Itoa(os.Getuid()) { + shell = userInfo[6] + } + } + shellName := filepath.Base(shell) + switch shellName { + case "dash": + return "Dash" + case "bash": + return "Bash " + runCommand("echo $BASH_VERSION") + case "zsh": + return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'") + case "fish": + return "Fish " + runCommand("$SHELL --version | awk '{print $3}'") + case "nu": + return "Nushell " + runCommand("$SHELL --version") + default: + return "Unknown" + } +} + +func GetDEWM() string { + processes, err := ps.Processes() + if err != nil { + log.Fatalf("Error: could not get processes: %s", err) + } + var executables []string + for _, process := range processes { + executables = append(executables, process.Executable()) + } + + processExists := func(process string) bool { + return slices.Contains(executables, process) + } + runCommand := func(command string) string { + cmd := exec.Command("/bin/bash", "-c", command) + workdir, err := os.Getwd() + if err != nil { + return "" + } + cmd.Dir = workdir + cmd.Env = os.Environ() + out, err := cmd.Output() + if err != nil { + return "" + } + return strings.TrimSpace(string(out)) + } + if processExists("plasmashell") { + return "KDE Plasma " + runCommand("plasmashell --version | awk '{print $2}'") + } else if processExists("gnome-session") { + return "Gnome " + runCommand("gnome-shell --version | awk '{print $3}'") + } else if processExists("xfce4-session") { + return "XFCE " + runCommand("xfce4-session --version | head -n1 | awk '{print $2}'") + } else if processExists("cinnamon") { + return "Cinnamon " + runCommand("cinnamon --version | awk '{print $3}'") + } else if processExists("mate-panel") { + return "MATE " + runCommand("mate-about --version | awk '{print $4}'") + } else if processExists("lxsession") { + return "LXDE" + } else if processExists("i3") || processExists("i3-with-shmlog") { + return "i3 " + runCommand("i3 --version | awk '{print $3}'") + } else if processExists("sway") { + if runCommand("sway --version | awk '{print $1}'") == "swayfx" { + return "SwayFX " + runCommand("sway --version | awk '{print $3}'") + } else { + return "Sway " + runCommand("sway --version | awk '{print $3}'") + } + } else if processExists("bspwm") { + return "Bspwm " + runCommand("bspwm -v") + } else if processExists("Hyprland") { + return "Hyprland " + runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'") + } else if processExists("icewm-session") { + return "IceWM " + runCommand("icewm --version | awk '{print $2}'") + } + return "" +} + +func GetDisplayProtocol() string { + protocol := os.Getenv("XDG_SESSION_TYPE") + if protocol == "x11" { + return "X11" + } else if protocol == "wayland" { + return "Wayland" + } + return "" +} diff --git a/src/utils.go b/src/utils.go new file mode 100644 index 0000000..48f50d1 --- /dev/null +++ b/src/utils.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "math" + "os" + "regexp" + "strings" +) + +func FormatBytes(bytes uint64) string { + var suffixes [6]string + suffixes[0] = "B" + suffixes[1] = "KiB" + suffixes[2] = "MiB" + suffixes[3] = "GiB" + suffixes[4] = "TiB" + suffixes[5] = "PiB" + + bf := float64(bytes) + for _, unit := range suffixes { + if math.Abs(bf) < 1024.0 { + return fmt.Sprintf("%3.1f %s", bf, unit) + } + bf /= 1024.0 + } + return fmt.Sprintf("%.1fYiB", bf) +} + +func StripAnsii(str string) string { + const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + var re = regexp.MustCompile(ansi) + return re.ReplaceAllString(str, "") +} + +func ReadKeyValueFile(filepath string) (map[string]string, error) { + ret := make(map[string]string) + if _, err := os.Stat(filepath); err != nil { + return nil, err + } + bytes, err := os.ReadFile(filepath) + if err != nil { + return nil, err + } + str := string(bytes) + lines := strings.Split(str, "\n") + for _, line := range lines { + if len(strings.Split(line, "=")) >= 2 { + key := strings.SplitN(line, "=", 2)[0] + value := strings.SplitN(line, "=", 2)[1] + if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") { + value = value[1 : len(value)-1] + } + ret[key] = value + } + } + return ret, nil +} diff --git a/utils.go b/utils.go deleted file mode 100644 index 09c2a04..0000000 --- a/utils.go +++ /dev/null @@ -1,449 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "github.com/go-gl/glfw/v3.3/glfw" - "github.com/jackmordaunt/ghw" - "github.com/mitchellh/go-ps" - "log" - "math" - "net" - "os" - "os/exec" - "path" - "path/filepath" - "regexp" - "slices" - "strconv" - "strings" -) - -type DistroInfo struct { - ID string - LongName string - ShortName string -} - -func getDistroInfo() DistroInfo { - info := DistroInfo{ - ID: "unknown", - LongName: "Unknown", - ShortName: "Unknown", - } - if strings.TrimSpace(config.DistroName) != "" { - info.LongName = strings.TrimSpace(config.DistroName) - info.ShortName = strings.TrimSpace(config.DistroName) - } - var releaseMap = make(map[string]string) - if _, err := os.Stat("/etc/os-release"); err == nil { - releaseMap, err = ReadKeyValueFile("/etc/os-release") - if err != nil { - return info - } - } - if id, ok := releaseMap["ID"]; ok { - info.ID = id - } - if longName, ok := releaseMap["PRETTY_NAME"]; ok && info.LongName == "Unknown" { - info.LongName = longName - } - if shortName, ok := releaseMap["NAME"]; ok && info.ShortName == "Unknown" { - info.ShortName = shortName - } - return info -} - -func getDistroAsciiArt() string { - defaultAscii := - ` .--. - |o_o | - |:_/ | - // \ \ - (| | ) -/'\_ _/'\ -\___)=(___/ ` - var id string - if config.Ascii == "auto" { - id = getDistroInfo().ID - } else { - id = config.Ascii - } - userConfDir, err := os.UserConfigDir() - if err != nil { - if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil { - bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id)) - if err != nil { - return defaultAscii - } - return string(bytes) - } else { - return defaultAscii - } - } - if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", id)); err == nil { - bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", id)) - if err != nil { - return defaultAscii - } - return string(bytes) - } else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil { - bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id)) - if err != nil { - return defaultAscii - } - return strings.TrimRight(string(bytes), "\n\t ") - } else { - return defaultAscii - } -} - -func getMotherboardModel() string { - bytes, err := os.ReadFile("/sys/devices/virtual/dmi/id/board_name") - if err != nil { - return "" - } - return strings.TrimSpace(string(bytes)) -} - -func getCPUName() string { - cpu, err := ghw.CPU() - if err != nil { - return "" - } - if len(cpu.Processors) == 0 { - return "" - } - return cpu.Processors[0].Model -} - -func getCPUThreads() int { - cpu, err := ghw.CPU() - if err != nil { - return 0 - } - return int(cpu.TotalThreads) -} - -func getGPUNames() []string { - var ret []string - cmd := exec.Command("/bin/bash", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:' | sed 's/^Device://' | awk '{$1=$1};1'") - bytes, err := cmd.Output() - if err != nil { - return nil - } - for _, name := range strings.Split(string(bytes), "\n") { - name = strings.TrimSpace(name) - if name == "" { - continue - } - ret = append(ret, name) - } - return ret -} - -type Memory struct { - MemTotal int - MemFree int - MemAvailable int -} - -func GetMemoryInfo() *Memory { - toInt := func(raw string) int { - if raw == "" { - return 0 - } - res, err := strconv.Atoi(raw) - if err != nil { - panic(err) - } - return res - } - - parseLine := func(raw string) (key string, value int) { - text := strings.ReplaceAll(raw[:len(raw)-2], " ", "") - keyValue := strings.Split(text, ":") - return keyValue[0], toInt(keyValue[1]) - } - - if _, err := os.Stat("/proc/meminfo"); err != nil { - return nil - } - file, err := os.Open("/proc/meminfo") - if err != nil { - panic(err) - } - defer file.Close() - bufio.NewScanner(file) - scanner := bufio.NewScanner(file) - res := Memory{} - for scanner.Scan() { - key, value := parseLine(scanner.Text()) - switch key { - case "MemTotal": - res.MemTotal = value / 1024 - case "MemFree": - res.MemFree = value / 1024 - case "MemAvailable": - res.MemAvailable = value / 1024 - } - } - return &res -} - -func GetShell() string { - runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) - workdir, err := os.Getwd() - if err != nil { - return "" - } - cmd.Dir = workdir - cmd.Env = os.Environ() - out, err := cmd.Output() - if err != nil { - return "" - } - return strings.TrimSpace(string(out)) - } - file, err := os.ReadFile("/etc/passwd") - if err != nil { - return "" - } - str := string(file) - shell := "" - - for _, line := range strings.Split(str, "\n") { - if strings.TrimSpace(line) == "" { - continue - } - userInfo := strings.Split(line, ":") - if userInfo[2] == strconv.Itoa(os.Getuid()) { - shell = userInfo[6] - } - } - shellName := filepath.Base(shell) - switch shellName { - case "dash": - return "Dash" - case "bash": - return "Bash " + runCommand("echo $BASH_VERSION") - case "zsh": - return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'") - case "fish": - return "Fish " + runCommand("$SHELL --version | awk '{print $3}'") - case "nu": - return "Nushell " + runCommand("$SHELL --version") - default: - return "Unknown" - } -} - -func GetDEWM() string { - processes, err := ps.Processes() - if err != nil { - log.Fatalf("Error: could not get processes: %s", err) - } - var executables []string - for _, process := range processes { - executables = append(executables, process.Executable()) - } - - processExists := func(process string) bool { - return slices.Contains(executables, process) - } - runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) - workdir, err := os.Getwd() - if err != nil { - return "" - } - cmd.Dir = workdir - cmd.Env = os.Environ() - out, err := cmd.Output() - if err != nil { - return "" - } - return strings.TrimSpace(string(out)) - } - if processExists("plasmashell") { - return "KDE Plasma " + runCommand("plasmashell --version | awk '{print $2}'") - } else if processExists("gnome-session") { - return "Gnome " + runCommand("gnome-shell --version | awk '{print $3}'") - } else if processExists("xfce4-session") { - return "XFCE " + runCommand("xfce4-session --version | head -n1 | awk '{print $2}'") - } else if processExists("cinnamon") { - return "Cinnamon " + runCommand("cinnamon --version | awk '{print $3}'") - } else if processExists("mate-panel") { - return "MATE " + runCommand("mate-about --version | awk '{print $4}'") - } else if processExists("lxsession") { - return "LXDE" - } else if processExists("i3") || processExists("i3-with-shmlog") { - return "i3 " + runCommand("i3 --version | awk '{print $3}'") - } else if processExists("sway") { - if runCommand("sway --version | awk '{print $1}'") == "swayfx" { - return "SwayFX " + runCommand("sway --version | awk '{print $3}'") - } else { - return "Sway " + runCommand("sway --version | awk '{print $3}'") - } - } else if processExists("bspwm") { - return "Bspwm " + runCommand("bspwm -v") - } else if processExists("Hyprland") { - return "Hyprland " + runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'") - } else if processExists("icewm-session") { - return "IceWM " + runCommand("icewm --version | awk '{print $2}'") - } - return "" -} - -func GetDisplayProtocol() string { - protocol := os.Getenv("XDG_SESSION_TYPE") - if protocol == "x11" { - return "X11" - } else if protocol == "wayland" { - return "Wayland" - } - return "" -} - -func getMonitorResolution() []string { - var monitors []string - if GetDisplayProtocol() != "" { - err := glfw.Init() - if err != nil { - panic(err) - } - for _, monitor := range glfw.GetMonitors() { - mode := monitor.GetVideoMode() - monitors = append(monitors, fmt.Sprintf("%dx%d %dHz", mode.Width, mode.Height, mode.RefreshRate)) - } - defer glfw.Terminate() - } - return monitors -} - -func GetInitSystem() string { - runCommand := func(command string) string { - cmd := exec.Command("/bin/bash", "-c", command) - workdir, err := os.Getwd() - if err != nil { - return "" - } - cmd.Dir = workdir - cmd.Env = os.Environ() - out, err := cmd.Output() - if err != nil { - return "" - } - return strings.TrimSpace(string(out)) - } - - process, err := ps.FindProcess(1) - if err != nil { - return "" - } - - // Special cases - // OpenRC check - if _, err := os.Stat("/usr/sbin/openrc"); err == nil { - return "OpenRC " + runCommand("openrc --version | awk '{print $3}'") - } - - // Default PID 1 process name checking - switch process.Executable() { - case "systemd": - return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'") - case "runit": - return "Runit" - case "dinit": - return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'") - case "enit": - return "Enit " + runCommand("enit --version | awk '{print $3}'") - default: - return process.Executable() - } -} - -func GetLibc() string { - checkLibcOutput, err := exec.Command("ldd", "/usr/bin/ls").Output() - if err != nil { - return "Unknown" - } - - if strings.Contains(string(checkLibcOutput), "ld-musl") { - // Using Musl Libc - output, _ := exec.Command("ldd").CombinedOutput() - return "Musl " + strings.TrimPrefix(strings.Split(strings.TrimSpace(string(output)), "\n")[1], "Version ") - } else { - // Using Glibc - cmd := exec.Command("ldd", "--version") - output, err := cmd.Output() - if err != nil { - return "Glibc" - } - outputSplit := strings.Split(strings.Split(strings.TrimSpace(string(output)), "\n")[0], " ") - ver := outputSplit[len(outputSplit)-1] - return "Glibc " + ver - } -} - -func GetLocalIP() string { - conn, err := net.Dial("udp", "8.8.8.8:80") - if err != nil { - return "" - } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP.String() -} - -func FormatBytes(bytes uint64) string { - var suffixes [6]string - suffixes[0] = "B" - suffixes[1] = "KiB" - suffixes[2] = "MiB" - suffixes[3] = "GiB" - suffixes[4] = "TiB" - suffixes[5] = "PiB" - - bf := float64(bytes) - for _, unit := range suffixes { - if math.Abs(bf) < 1024.0 { - return fmt.Sprintf("%3.1f %s", bf, unit) - } - bf /= 1024.0 - } - return fmt.Sprintf("%.1fYiB", bf) -} - -func StripAnsii(str string) string { - const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" - var re = regexp.MustCompile(ansi) - return re.ReplaceAllString(str, "") -} - -func ReadKeyValueFile(filepath string) (map[string]string, error) { - ret := make(map[string]string) - if _, err := os.Stat(filepath); err != nil { - return nil, err - } - bytes, err := os.ReadFile(filepath) - if err != nil { - return nil, err - } - str := string(bytes) - lines := strings.Split(str, "\n") - for _, line := range lines { - if len(strings.Split(line, "=")) >= 2 { - key := strings.SplitN(line, "=", 2)[0] - value := strings.SplitN(line, "=", 2)[1] - if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") { - value = value[1 : len(value)-1] - } - ret[key] = value - } - } - return ret, nil -}