Moved go files to src/ folder

This commit is contained in:
EnumDev 2025-03-21 14:56:58 +02:00
parent 47fd83f8cd
commit a76295c455
14 changed files with 490 additions and 459 deletions

View File

@ -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)

View File

@ -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.

View File

View File

70
src/hardware.go Normal file
View 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
}

View File

@ -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 {

57
src/memory.go Normal file
View 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
View 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()
}

View File

153
src/system.go Normal file
View 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
View 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
View 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
View File

@ -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
}