Moved go files to src/ folder
This commit is contained in:
parent
47fd83f8cd
commit
a76295c455
2
Makefile
2
Makefile
@ -15,7 +15,7 @@ endif
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p 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/
|
install: build/stormfetch config/
|
||||||
mkdir -p $(DESTDIR)$(BINDIR)
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## A simple linux fetch program written in go and bash
|
## A simple linux fetch program written in go and bash
|
||||||
|
|
||||||
### Developers:
|
### Developers:
|
||||||
- [EnumDev](https://gitlab.com/EnumDev)
|
- [EnumDev](https://enumerated.dev)
|
||||||
|
|
||||||
### Project Information
|
### 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.
|
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.
|
||||||
|
70
src/hardware.go
Normal file
70
src/hardware.go
Normal file
@ -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
|
||||||
|
}
|
@ -129,11 +129,11 @@ func SetupFetchEnv(showTimeTaken bool) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setVariable("PACKAGES", func() string { return GetInstalledPackages() })
|
setVariable("PACKAGES", func() string { return GetInstalledPackages() })
|
||||||
setVariable("DISTRO_LONG_NAME", func() string { return getDistroInfo().LongName })
|
setVariable("DISTRO_LONG_NAME", func() string { return GetDistroInfo().LongName })
|
||||||
setVariable("DISTRO_SHORT_NAME", func() string { return getDistroInfo().ShortName })
|
setVariable("DISTRO_SHORT_NAME", func() string { return GetDistroInfo().ShortName })
|
||||||
setVariable("CPU_MODEL", func() string { return getCPUName() })
|
setVariable("CPU_MODEL", func() string { return GetCPUModel() })
|
||||||
setVariable("MOTHERBOARD", func() string { return getMotherboardModel() })
|
setVariable("MOTHERBOARD", func() string { return GetMotherboardModel() })
|
||||||
setVariable("CPU_THREADS", func() string { return strconv.Itoa(getCPUThreads()) })
|
setVariable("CPU_THREADS", func() string { return strconv.Itoa(GetCPUThreads()) })
|
||||||
start := time.Now().UnixMilli()
|
start := time.Now().UnixMilli()
|
||||||
memory := GetMemoryInfo()
|
memory := GetMemoryInfo()
|
||||||
end := time.Now().UnixMilli()
|
end := time.Now().UnixMilli()
|
||||||
@ -174,7 +174,7 @@ func SetupFetchEnv(showTimeTaken bool) []string {
|
|||||||
setVariable("INIT_SYSTEM", func() string { return GetInitSystem() })
|
setVariable("INIT_SYSTEM", func() string { return GetInitSystem() })
|
||||||
setVariable("LOCAL_IPV4", func() string { return GetLocalIP() })
|
setVariable("LOCAL_IPV4", func() string { return GetLocalIP() })
|
||||||
start = time.Now().UnixMilli()
|
start = time.Now().UnixMilli()
|
||||||
monitors := getMonitorResolution()
|
monitors := GetMonitorResolution()
|
||||||
end = time.Now().UnixMilli()
|
end = time.Now().UnixMilli()
|
||||||
if showTimeTaken {
|
if showTimeTaken {
|
||||||
fmt.Println(fmt.Sprintf("Setting '%s' took %d milliseconds", "MONITOR_*", end-start))
|
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()
|
start = time.Now().UnixMilli()
|
||||||
gpus := getGPUNames()
|
gpus := GetGPUModels()
|
||||||
end = time.Now().UnixMilli()
|
end = time.Now().UnixMilli()
|
||||||
if showTimeTaken {
|
if showTimeTaken {
|
||||||
fmt.Println(fmt.Sprintf("Setting '%s' took %d milliseconds", "GPU_*", end-start))
|
fmt.Println(fmt.Sprintf("Setting '%s' took %d milliseconds", "GPU_*", end-start))
|
||||||
@ -224,7 +224,7 @@ func runStormfetch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setColorMap()
|
setColorMap()
|
||||||
ascii := getDistroAsciiArt()
|
ascii := GetDistroAsciiArt()
|
||||||
if strings.HasPrefix(ascii, "#/") {
|
if strings.HasPrefix(ascii, "#/") {
|
||||||
firstLine := strings.Split(ascii, "\n")[0]
|
firstLine := strings.Split(ascii, "\n")[0]
|
||||||
if !config.ForceConfigAnsii {
|
if !config.ForceConfigAnsii {
|
57
src/memory.go
Normal file
57
src/memory.go
Normal file
@ -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
|
||||||
|
}
|
15
src/network.go
Normal file
15
src/network.go
Normal file
@ -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()
|
||||||
|
}
|
153
src/system.go
Normal file
153
src/system.go
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
127
src/user.go
Normal file
127
src/user.go
Normal file
@ -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 ""
|
||||||
|
}
|
58
src/utils.go
Normal file
58
src/utils.go
Normal file
@ -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
|
||||||
|
}
|
449
utils.go
449
utils.go
@ -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
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user