From d485d253083cd2bf6250c1c9a74fc0be76bd2b29 Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 13:13:40 -0500 Subject: [PATCH 1/6] os: replace sysinfo dep with modules/os - remove sysinfo dep - copy relevant pieces to modules/os - add preliminary MacOS support Notably, this also allows the project to run on Windows, which wasn't previously possible (I tried compiling mewfetch on Windows, but had issues with sysinfo failing to compile for some reason). --- CONTRIBUTING.md | 2 +- artwork.go | 8 ++- go.mod | 2 - go.sum | 4 -- modules/os.go | 132 +++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 130 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35f5675..d563323 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ distribution artwork should be: - line drawings, not filled - close to 8 lines long and square-ish -they are plain text files stored under `artwork/`. the file name should be the `OS.Vendor` value in the struct returned by [zcalusic/sysinfo](https://github.com/zcalusic/sysinfo). +they are plain text files stored under `artwork/`. the file name should be the `OS.Vendor` value in the struct returned by [modules/os](modules/os). ### platform compliance i am just one person: a linux user without much access to commercial/esoteric OSes or a variety of hardware; so i would like help with testing and tailoring to work on as many computers as possible. of course it's not going to work on *literally every machine*, but reasonably common ones are my general target. diff --git a/artwork.go b/artwork.go index 97b10d3..b5df576 100644 --- a/artwork.go +++ b/artwork.go @@ -6,9 +6,9 @@ import ( "regexp" "strings" + "git.cyberia.club/reese/mewfetch/modules" "git.cyberia.club/reese/slog" "github.com/mattn/go-runewidth" - "github.com/zcalusic/sysinfo" ) //go:embed artwork @@ -50,10 +50,8 @@ func draw() { artName = "custom" artwork = conf.Custom } else if conf.OS == "auto" { - var si sysinfo.SysInfo - si.GetSysInfo() - artName = si.OS.Vendor - artwork = getArt(artName) + os := modules.GetOSInfo() + artwork = getArt(os.Vendor) } else { artName = conf.OS artwork = getArt(artName) diff --git a/go.mod b/go.mod index dad2b76..9908137 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ toolchain go1.22.8 require ( github.com/fatih/color v1.18.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -23,6 +22,5 @@ require ( github.com/mackerelio/go-osstat v0.2.5 github.com/mattn/go-runewidth v0.0.16 github.com/spf13/pflag v1.0.5 - github.com/zcalusic/sysinfo v1.1.3 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 15348ef..074fc8a 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -33,8 +31,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/zcalusic/sysinfo v1.1.3 h1:u/AVENkuoikKuIZ4sUEJ6iibpmQP6YpGD8SSMCrqAF0= -github.com/zcalusic/sysinfo v1.1.3/go.mod h1:NX+qYnWGtJVPV0yWldff9uppNKU4h40hJIRPf/pGLv4= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= diff --git a/modules/os.go b/modules/os.go index 20a40ff..1f79db6 100644 --- a/modules/os.go +++ b/modules/os.go @@ -1,14 +1,34 @@ package modules import ( + "bufio" + "os" + "regexp" "runtime" - - "github.com/zcalusic/sysinfo" + "strings" ) -func OS(opts map[string]string) (rune, string) { - var si sysinfo.SysInfo - si.GetSysInfo() +var ( + rePrettyName = regexp.MustCompile(`^PRETTY_NAME=(.*)$`) + reID = regexp.MustCompile(`^ID=(.*)$`) + reVersionID = regexp.MustCompile(`^VERSION_ID=(.*)$`) + reUbuntu = regexp.MustCompile(`[\( ]([\d\.]+)`) + reAlma = regexp.MustCompile(`^AlmaLinux release ([\d\.]+)`) + reCentOS = regexp.MustCompile(`^CentOS( Linux)? release ([\d\.]+)`) + reRocky = regexp.MustCompile(`^Rocky Linux release ([\d\.]+)`) + reRedHat = regexp.MustCompile(`[\( ]([\d\.]+)`) +) + +type OSInfo struct { + Name string `json:"name,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Release string `json:"release,omitempty"` + Architecture string `json:"architecture,omitempty"` +} + +func OS(map[string]string) (rune, string) { + os := GetOSInfo() icon := map[string]rune{ "linux": '', @@ -18,5 +38,105 @@ func OS(opts map[string]string) (rune, string) { "openbsd": '', }[runtime.GOOS] - return icon, si.OS.Name + return icon, os.Name +} + +func GetOSInfo() *OSInfo { + OS := &OSInfo{} + switch runtime.GOOS { + case "linux": + getLinuxOSInfo(OS) + case "darwin": + getMacOSInfo(OS) + } + + return OS + // This seems to be the best and most portable way to detect OS architecture (NOT kernel!) +} + +func getLinuxOSInfo(OS *OSInfo) *OSInfo { + if _, err := os.Stat("/lib64/ld-linux-x86-64.so.2"); err == nil { + OS.Architecture = "amd64" + } else if _, err := os.Stat("/lib/ld-linux.so.2"); err == nil { + OS.Architecture = "i386" + } + + f, err := os.Open("/etc/os-release") + if err != nil { + return nil + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + if m := rePrettyName.FindStringSubmatch(s.Text()); m != nil { + OS.Name = strings.Trim(m[1], `"`) + } else if m := reID.FindStringSubmatch(s.Text()); m != nil { + OS.Vendor = strings.Trim(m[1], `"`) + } else if m := reVersionID.FindStringSubmatch(s.Text()); m != nil { + OS.Version = strings.Trim(m[1], `"`) + } + } + + switch OS.Vendor { + case "debian": + OS.Release = slurpFile("/etc/debian_version") + case "ubuntu": + if m := reUbuntu.FindStringSubmatch(OS.Name); m != nil { + OS.Release = m[1] + } + case "almalinux": + if release := slurpFile("/etc/almalinux-release"); release != "" { + if m := reAlma.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + + OS.Version = strings.Split(OS.Release, ".")[0] + case "centos": + if release := slurpFile("/etc/centos-release"); release != "" { + if m := reCentOS.FindStringSubmatch(release); m != nil { + OS.Release = m[2] + } + } + case "rocky": + if release := slurpFile("/etc/rocky-release"); release != "" { + if m := reRocky.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + + OS.Version = strings.Split(OS.Release, ".")[0] + + case "rhel": + if release := slurpFile("/etc/redhat-release"); release != "" { + if m := reRedHat.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + if OS.Release == "" { + if m := reRedHat.FindStringSubmatch(OS.Name); m != nil { + OS.Release = m[1] + } + } + } + + return OS +} + +func getMacOSInfo(OS *OSInfo) *OSInfo { + OS.Name = "macOS" + OS.Vendor = "apple" + return OS +} + +// Read one-liner text files, strip newline. +func slurpFile(path string) string { + data, err := os.ReadFile(path) + if err != nil { + return "" + } + + // Trim spaces & \u0000 \uffff + return strings.Trim(string(data), " \r\n\t\u0000\uffff") } -- 2.45.2 From 6f0bdb8513571b1a0d1b888c908e28b22c065b5e Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 13:32:33 -0500 Subject: [PATCH 2/6] artwork: add apple --- artwork/apple | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 artwork/apple diff --git a/artwork/apple b/artwork/apple new file mode 100644 index 0000000..9033afb --- /dev/null +++ b/artwork/apple @@ -0,0 +1,7 @@ + .:' + __ :'__ + .`` `-' ``. +: .` +: `._ + : ; + `.__.-.__.` -- 2.45.2 From 612a3c4d47be2107030bb8f5a5ca666a91ad456c Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 15:46:18 -0500 Subject: [PATCH 3/6] macos: correct memory reporting --- modules/memory.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/memory.go b/modules/memory.go index 1639a8c..1349d55 100644 --- a/modules/memory.go +++ b/modules/memory.go @@ -1,6 +1,7 @@ package modules import ( + "encoding/binary" "fmt" "os" "regexp" @@ -8,6 +9,7 @@ import ( "strconv" "git.cyberia.club/reese/slog" + "golang.org/x/sys/unix" ) func Memory(opts map[string]string) (rune, string) { @@ -19,7 +21,12 @@ func Memory(opts map[string]string) (rune, string) { memory, err = strconv.Atoi(regexp.MustCompile(`MemTotal:\s+(\d+)`).FindStringSubmatch(string(meminfo))[1]) memory *= 1000 + case "darwin": + totalb, err := unix.Sysctl("hw.memsize") + slog.Warn(err, "module: memory: unable to read hw.memsize") + memory = int(binary.LittleEndian.Uint64([]byte(totalb + "\x00"))) } + // TODO: more OS cases return '󰘚', fmt.Sprintf("%s", byteFormat(memory)) -- 2.45.2 From c7783bda15e52f0688753ecf3c3ad5bdace40bf8 Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 16:06:10 -0500 Subject: [PATCH 4/6] Use uname to detect arch, add MacOS cpu brand detection --- modules/cpu.go | 18 +++++++++++++----- modules/os.go | 16 +++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/modules/cpu.go b/modules/cpu.go index 2507ae2..4d00a66 100644 --- a/modules/cpu.go +++ b/modules/cpu.go @@ -6,16 +6,24 @@ import ( "runtime" "git.cyberia.club/reese/slog" + "golang.org/x/sys/unix" ) func CPU(opts map[string]string) (rune, string) { - var model_name string + var ( + cpuinfo []byte + err error + model_name string + ) + switch runtime.GOOS { case "linux": - meminfo, err := os.ReadFile("/proc/cpuinfo") - slog.Warn(err, "module: memory: unable to read /proc/meminfo") - - model_name = regexp.MustCompile(`model name\s*:\s*(\w.*)`).FindStringSubmatch(string(meminfo))[1] + cpuinfo, err = os.ReadFile("/proc/cpuinfo") + slog.Warn(err, "module: cpu: unable to read /proc/cpuinfo") + model_name = regexp.MustCompile(`model name\s*:\s*(\w.*)`).FindStringSubmatch(string(cpuinfo))[1] + case "darwin": + model_name, err = unix.Sysctl("machdep.cpu.brand_string") + slog.Warn(err, "module: cpu: unable to read machdep.cpu.brand_string") } // TODO: more OS cases diff --git a/modules/os.go b/modules/os.go index 1f79db6..27d480e 100644 --- a/modules/os.go +++ b/modules/os.go @@ -3,9 +3,12 @@ package modules import ( "bufio" "os" + "os/exec" "regexp" "runtime" "strings" + + "git.cyberia.club/reese/slog" ) var ( @@ -43,6 +46,11 @@ func OS(map[string]string) (rune, string) { func GetOSInfo() *OSInfo { OS := &OSInfo{} + cmd := exec.Command("uname", "-m") + stdout, err := cmd.CombinedOutput() + slog.Warn(err, "module: os: unable to run uname -m") + + OS.Architecture = string(stdout) switch runtime.GOOS { case "linux": getLinuxOSInfo(OS) @@ -51,18 +59,12 @@ func GetOSInfo() *OSInfo { } return OS - // This seems to be the best and most portable way to detect OS architecture (NOT kernel!) } func getLinuxOSInfo(OS *OSInfo) *OSInfo { - if _, err := os.Stat("/lib64/ld-linux-x86-64.so.2"); err == nil { - OS.Architecture = "amd64" - } else if _, err := os.Stat("/lib/ld-linux.so.2"); err == nil { - OS.Architecture = "i386" - } - f, err := os.Open("/etc/os-release") if err != nil { + slog.Warn(err, "unable to open /etc/os-release") return nil } defer f.Close() -- 2.45.2 From 8bfe85e9164cb7dac57298a55c93483f5bf1151d Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 18:45:13 -0500 Subject: [PATCH 5/6] Refactor modules into platform-specific files --- artwork.go | 3 +- modules/cpu.go | 24 +------- modules/cpu_darwin.go | 12 ++++ modules/cpu_linux.go | 21 +++++++ modules/memory.go | 24 +------- modules/memory_darwin.go | 19 ++++++ modules/memory_linux.go | 29 +++++++++ modules/os.go | 128 +++++---------------------------------- modules/os_darwin.go | 27 +++++++++ modules/os_linux.go | 102 +++++++++++++++++++++++++++++++ modules/util.go | 10 +++ 11 files changed, 241 insertions(+), 158 deletions(-) create mode 100644 modules/cpu_darwin.go create mode 100644 modules/cpu_linux.go create mode 100644 modules/memory_darwin.go create mode 100644 modules/memory_linux.go create mode 100644 modules/os_darwin.go create mode 100644 modules/os_linux.go diff --git a/artwork.go b/artwork.go index b5df576..d74dcc1 100644 --- a/artwork.go +++ b/artwork.go @@ -50,8 +50,7 @@ func draw() { artName = "custom" artwork = conf.Custom } else if conf.OS == "auto" { - os := modules.GetOSInfo() - artwork = getArt(os.Vendor) + artwork = getArt(modules.GetOSVendor()) } else { artName = conf.OS artwork = getArt(artName) diff --git a/modules/cpu.go b/modules/cpu.go index 4d00a66..8e37f74 100644 --- a/modules/cpu.go +++ b/modules/cpu.go @@ -1,31 +1,11 @@ package modules import ( - "os" - "regexp" - "runtime" - "git.cyberia.club/reese/slog" - "golang.org/x/sys/unix" ) func CPU(opts map[string]string) (rune, string) { - var ( - cpuinfo []byte - err error - model_name string - ) - - switch runtime.GOOS { - case "linux": - cpuinfo, err = os.ReadFile("/proc/cpuinfo") - slog.Warn(err, "module: cpu: unable to read /proc/cpuinfo") - model_name = regexp.MustCompile(`model name\s*:\s*(\w.*)`).FindStringSubmatch(string(cpuinfo))[1] - case "darwin": - model_name, err = unix.Sysctl("machdep.cpu.brand_string") - slog.Warn(err, "module: cpu: unable to read machdep.cpu.brand_string") - } - // TODO: more OS cases - + model_name, err := getCPUModelName() + slog.Warn(err, "could not get CPU model name") return '', model_name } diff --git a/modules/cpu_darwin.go b/modules/cpu_darwin.go new file mode 100644 index 0000000..797bf04 --- /dev/null +++ b/modules/cpu_darwin.go @@ -0,0 +1,12 @@ +//go:build darwin +// +build darwin + +package modules + +import ( + "golang.org/x/sys/unix" +) + +func getCPUModelName() (string, error) { + return unix.Sysctl("machdep.cpu.brand_string") +} diff --git a/modules/cpu_linux.go b/modules/cpu_linux.go new file mode 100644 index 0000000..a94ce5e --- /dev/null +++ b/modules/cpu_linux.go @@ -0,0 +1,21 @@ +//go:build linux +// +build linux + +package modules + +import ( + "os" + "regexp" +) + +func getCPUModelName() (string, error) { + cpuinfo, err := os.ReadFile("/proc/cpuinfo") + if err != nil { + return "", err + } + matches := regexp.MustCompile(`model name\s*:\s*(\w.*)`).FindStringSubmatch(string(cpuinfo)) + if len(matches) < 2 { + return "", nil + } + return matches[1], nil +} diff --git a/modules/memory.go b/modules/memory.go index 1349d55..bf7e49c 100644 --- a/modules/memory.go +++ b/modules/memory.go @@ -1,33 +1,15 @@ package modules import ( - "encoding/binary" "fmt" - "os" - "regexp" - "runtime" - "strconv" "git.cyberia.club/reese/slog" - "golang.org/x/sys/unix" ) func Memory(opts map[string]string) (rune, string) { - var memory int - switch runtime.GOOS { - case "linux": - meminfo, err := os.ReadFile("/proc/meminfo") - slog.Warn(err, "module: memory: unable to read /proc/meminfo") - - memory, err = strconv.Atoi(regexp.MustCompile(`MemTotal:\s+(\d+)`).FindStringSubmatch(string(meminfo))[1]) - memory *= 1000 - case "darwin": - totalb, err := unix.Sysctl("hw.memsize") - slog.Warn(err, "module: memory: unable to read hw.memsize") - memory = int(binary.LittleEndian.Uint64([]byte(totalb + "\x00"))) + memory, err := getMemory() + if err != nil { + slog.Die(err, "module: memory: unable to get memory") } - - // TODO: more OS cases - return '󰘚', fmt.Sprintf("%s", byteFormat(memory)) } diff --git a/modules/memory_darwin.go b/modules/memory_darwin.go new file mode 100644 index 0000000..838ddee --- /dev/null +++ b/modules/memory_darwin.go @@ -0,0 +1,19 @@ +//go:build darwin +// +build darwin + +package modules + +import ( + "encoding/binary" + + "golang.org/x/sys/unix" +) + +func getMemory() (int, error) { + totalb, err := unix.Sysctl("hw.memsize") + if err != nil { + return 0, err + } + mem := int(binary.LittleEndian.Uint64([]byte(totalb + "\x00"))) + return mem, nil +} diff --git a/modules/memory_linux.go b/modules/memory_linux.go new file mode 100644 index 0000000..4985dc1 --- /dev/null +++ b/modules/memory_linux.go @@ -0,0 +1,29 @@ +//go:build linux +// +build linux + +package modules + +import ( + "errors" + "os" + "regexp" + "strconv" +) + +func getMemory() (int, error) { + meminfo, err := os.ReadFile("/proc/meminfo") + if err != nil { + return 0, err + } + + matches := regexp.MustCompile(`MemTotal:\s+(\d+)`).FindStringSubmatch(string(meminfo)) + if len(matches) < 2 { + return 0, errors.New("could not find MemTotal in /proc/meminfo") + } + + val, err := strconv.Atoi(matches[1]) + if err != nil { + return 0, err + } + return val * 1000, nil +} diff --git a/modules/os.go b/modules/os.go index 27d480e..53fdc33 100644 --- a/modules/os.go +++ b/modules/os.go @@ -1,27 +1,11 @@ package modules import ( - "bufio" - "os" - "os/exec" - "regexp" "runtime" - "strings" "git.cyberia.club/reese/slog" ) -var ( - rePrettyName = regexp.MustCompile(`^PRETTY_NAME=(.*)$`) - reID = regexp.MustCompile(`^ID=(.*)$`) - reVersionID = regexp.MustCompile(`^VERSION_ID=(.*)$`) - reUbuntu = regexp.MustCompile(`[\( ]([\d\.]+)`) - reAlma = regexp.MustCompile(`^AlmaLinux release ([\d\.]+)`) - reCentOS = regexp.MustCompile(`^CentOS( Linux)? release ([\d\.]+)`) - reRocky = regexp.MustCompile(`^Rocky Linux release ([\d\.]+)`) - reRedHat = regexp.MustCompile(`[\( ]([\d\.]+)`) -) - type OSInfo struct { Name string `json:"name,omitempty"` Vendor string `json:"vendor,omitempty"` @@ -30,115 +14,33 @@ type OSInfo struct { Architecture string `json:"architecture,omitempty"` } -func OS(map[string]string) (rune, string) { - os := GetOSInfo() +func OS(opts map[string]string) (rune, string) { + osInfo, err := getOSInfo() + if err != nil { + slog.Warn(err, "module: os: unable to get OS info") + return '', "Unknown" + } icon := map[string]rune{ "linux": '', - "macos": '', + "darwin": '', "windows": '', "freebsd": '', "openbsd": '', }[runtime.GOOS] - return icon, os.Name -} - -func GetOSInfo() *OSInfo { - OS := &OSInfo{} - cmd := exec.Command("uname", "-m") - stdout, err := cmd.CombinedOutput() - slog.Warn(err, "module: os: unable to run uname -m") - - OS.Architecture = string(stdout) - switch runtime.GOOS { - case "linux": - getLinuxOSInfo(OS) - case "darwin": - getMacOSInfo(OS) + if icon == 0 { + icon = '' // default to Linux icon if unknown } - return OS + return icon, osInfo.Name } -func getLinuxOSInfo(OS *OSInfo) *OSInfo { - f, err := os.Open("/etc/os-release") +func GetOSVendor() string { + os, err := getOSInfo() if err != nil { - slog.Warn(err, "unable to open /etc/os-release") - return nil + slog.Warn(err, "module: os: unable to get OS info") + return "Unknown" } - defer f.Close() - - s := bufio.NewScanner(f) - for s.Scan() { - if m := rePrettyName.FindStringSubmatch(s.Text()); m != nil { - OS.Name = strings.Trim(m[1], `"`) - } else if m := reID.FindStringSubmatch(s.Text()); m != nil { - OS.Vendor = strings.Trim(m[1], `"`) - } else if m := reVersionID.FindStringSubmatch(s.Text()); m != nil { - OS.Version = strings.Trim(m[1], `"`) - } - } - - switch OS.Vendor { - case "debian": - OS.Release = slurpFile("/etc/debian_version") - case "ubuntu": - if m := reUbuntu.FindStringSubmatch(OS.Name); m != nil { - OS.Release = m[1] - } - case "almalinux": - if release := slurpFile("/etc/almalinux-release"); release != "" { - if m := reAlma.FindStringSubmatch(release); m != nil { - OS.Release = m[1] - } - } - - OS.Version = strings.Split(OS.Release, ".")[0] - case "centos": - if release := slurpFile("/etc/centos-release"); release != "" { - if m := reCentOS.FindStringSubmatch(release); m != nil { - OS.Release = m[2] - } - } - case "rocky": - if release := slurpFile("/etc/rocky-release"); release != "" { - if m := reRocky.FindStringSubmatch(release); m != nil { - OS.Release = m[1] - } - } - - OS.Version = strings.Split(OS.Release, ".")[0] - - case "rhel": - if release := slurpFile("/etc/redhat-release"); release != "" { - if m := reRedHat.FindStringSubmatch(release); m != nil { - OS.Release = m[1] - } - } - if OS.Release == "" { - if m := reRedHat.FindStringSubmatch(OS.Name); m != nil { - OS.Release = m[1] - } - } - } - - return OS -} - -func getMacOSInfo(OS *OSInfo) *OSInfo { - OS.Name = "macOS" - OS.Vendor = "apple" - return OS -} - -// Read one-liner text files, strip newline. -func slurpFile(path string) string { - data, err := os.ReadFile(path) - if err != nil { - return "" - } - - // Trim spaces & \u0000 \uffff - return strings.Trim(string(data), " \r\n\t\u0000\uffff") + return os.Vendor } diff --git a/modules/os_darwin.go b/modules/os_darwin.go new file mode 100644 index 0000000..bd4dabd --- /dev/null +++ b/modules/os_darwin.go @@ -0,0 +1,27 @@ +//go:build darwin +// +build darwin + +package modules + +import ( + "os/exec" + "strings" + + "git.cyberia.club/reese/slog" +) + +func getOSInfo() (*OSInfo, error) { + OS := &OSInfo{} + OS.Name = "macOS" + OS.Vendor = "apple" + + // Get architecture using `uname -m` + cmd := exec.Command("uname", "-m") + stdout, err := cmd.CombinedOutput() + if err != nil { + slog.Warn(err, "module: os: unable to run uname -m") + } + OS.Architecture = strings.TrimSpace(string(stdout)) + + return OS, nil +} diff --git a/modules/os_linux.go b/modules/os_linux.go new file mode 100644 index 0000000..c36e9bf --- /dev/null +++ b/modules/os_linux.go @@ -0,0 +1,102 @@ +//go:build linux +// +build linux + +package modules + +import ( + "bufio" + "os" + "os/exec" + "regexp" + "strings" + + "git.cyberia.club/reese/slog" +) + +var ( + rePrettyName = regexp.MustCompile(`^PRETTY_NAME=(.*)$`) + reID = regexp.MustCompile(`^ID=(.*)$`) + reVersionID = regexp.MustCompile(`^VERSION_ID=(.*)$`) + reUbuntu = regexp.MustCompile(`[\( ]([\d\.]+)`) + reAlma = regexp.MustCompile(`^AlmaLinux release ([\d\.]+)`) + reCentOS = regexp.MustCompile(`^CentOS( Linux)? release ([\d\.]+)`) + reRocky = regexp.MustCompile(`^Rocky Linux release ([\d\.]+)`) + reRedHat = regexp.MustCompile(`[\( ]([\d\.]+)`) +) + +func getOSInfo() (*OSInfo, error) { + OS := &OSInfo{} + + // Get architecture using `uname -m` + cmd := exec.Command("uname", "-m") + stdout, err := cmd.CombinedOutput() + if err != nil { + slog.Warn(err, "module: os: unable to run uname -m") + } + OS.Architecture = strings.TrimSpace(string(stdout)) + + f, err := os.Open("/etc/os-release") + if err != nil { + slog.Warn(err, "unable to open /etc/os-release") + return OS, nil + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + line := s.Text() + if m := rePrettyName.FindStringSubmatch(line); m != nil { + OS.Name = strings.Trim(m[1], `"`) + } else if m := reID.FindStringSubmatch(line); m != nil { + OS.Vendor = strings.Trim(m[1], `"`) + } else if m := reVersionID.FindStringSubmatch(line); m != nil { + OS.Version = strings.Trim(m[1], `"`) + } + } + + switch OS.Vendor { + case "debian": + OS.Release = slurpFile("/etc/debian_version") + case "ubuntu": + if m := reUbuntu.FindStringSubmatch(OS.Name); m != nil { + OS.Release = m[1] + } + case "almalinux": + if release := slurpFile("/etc/almalinux-release"); release != "" { + if m := reAlma.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + if OS.Release != "" { + OS.Version = strings.Split(OS.Release, ".")[0] + } + case "centos": + if release := slurpFile("/etc/centos-release"); release != "" { + if m := reCentOS.FindStringSubmatch(release); m != nil { + OS.Release = m[2] + } + } + case "rocky": + if release := slurpFile("/etc/rocky-release"); release != "" { + if m := reRocky.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + if OS.Release != "" { + OS.Version = strings.Split(OS.Release, ".")[0] + } + case "rhel": + if release := slurpFile("/etc/redhat-release"); release != "" { + if m := reRedHat.FindStringSubmatch(release); m != nil { + OS.Release = m[1] + } + } + if OS.Release == "" { + if m := reRedHat.FindStringSubmatch(OS.Name); m != nil { + OS.Release = m[1] + } + } + } + + return OS, nil +} diff --git a/modules/util.go b/modules/util.go index 0f2a8e7..6829c95 100644 --- a/modules/util.go +++ b/modules/util.go @@ -2,6 +2,7 @@ package modules import ( "fmt" + "os" "os/exec" "strings" "time" @@ -64,3 +65,12 @@ func durationStr(d time.Duration) string { } return out } + +// slurpFile reads a file and trims whitespace. +func slurpFile(path string) string { + data, err := os.ReadFile(path) + if err != nil { + return "" + } + return strings.Trim(string(data), " \r\n\t\u0000\uffff") +} -- 2.45.2 From 3f68e96d711ff7296cc212711a5f3196d945c423 Mon Sep 17 00:00:00 2001 From: Jes Olson Date: Thu, 19 Dec 2024 18:49:22 -0500 Subject: [PATCH 6/6] cpu/memory/os: better error handling --- modules/cpu.go | 5 ++++- modules/memory.go | 3 ++- modules/os_darwin.go | 28 +++++++++++++--------------- modules/os_linux.go | 7 ++----- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/modules/cpu.go b/modules/cpu.go index 8e37f74..e085d15 100644 --- a/modules/cpu.go +++ b/modules/cpu.go @@ -6,6 +6,9 @@ import ( func CPU(opts map[string]string) (rune, string) { model_name, err := getCPUModelName() - slog.Warn(err, "could not get CPU model name") + if err != nil { + slog.Warn(err, "could not get CPU model name") + return '', "Unknown" + } return '', model_name } diff --git a/modules/memory.go b/modules/memory.go index bf7e49c..6f90529 100644 --- a/modules/memory.go +++ b/modules/memory.go @@ -9,7 +9,8 @@ import ( func Memory(opts map[string]string) (rune, string) { memory, err := getMemory() if err != nil { - slog.Die(err, "module: memory: unable to get memory") + slog.Warn(err, "module: memory: unable to get memory") + return '󰘚', "Unknown" } return '󰘚', fmt.Sprintf("%s", byteFormat(memory)) } diff --git a/modules/os_darwin.go b/modules/os_darwin.go index bd4dabd..71bfe67 100644 --- a/modules/os_darwin.go +++ b/modules/os_darwin.go @@ -4,24 +4,22 @@ package modules import ( - "os/exec" - "strings" - - "git.cyberia.club/reese/slog" + "os/exec" + "strings" ) func getOSInfo() (*OSInfo, error) { - OS := &OSInfo{} - OS.Name = "macOS" - OS.Vendor = "apple" + OS := &OSInfo{} + OS.Name = "macOS" + OS.Vendor = "apple" - // Get architecture using `uname -m` - cmd := exec.Command("uname", "-m") - stdout, err := cmd.CombinedOutput() - if err != nil { - slog.Warn(err, "module: os: unable to run uname -m") - } - OS.Architecture = strings.TrimSpace(string(stdout)) + // Get architecture using `uname -m` + cmd := exec.Command("uname", "-m") + stdout, err := cmd.CombinedOutput() + if err != nil { + return nil, err + } + OS.Architecture = strings.TrimSpace(string(stdout)) - return OS, nil + return OS, nil } diff --git a/modules/os_linux.go b/modules/os_linux.go index c36e9bf..ced87ba 100644 --- a/modules/os_linux.go +++ b/modules/os_linux.go @@ -9,8 +9,6 @@ import ( "os/exec" "regexp" "strings" - - "git.cyberia.club/reese/slog" ) var ( @@ -31,14 +29,13 @@ func getOSInfo() (*OSInfo, error) { cmd := exec.Command("uname", "-m") stdout, err := cmd.CombinedOutput() if err != nil { - slog.Warn(err, "module: os: unable to run uname -m") + return OS, err } OS.Architecture = strings.TrimSpace(string(stdout)) f, err := os.Open("/etc/os-release") if err != nil { - slog.Warn(err, "unable to open /etc/os-release") - return OS, nil + return OS, err } defer f.Close() -- 2.45.2