22 Commits
Author SHA1 Message Date
EnumDev 8cafe93b33 Update README.md 2026-02-15 20:12:48 +02:00
EnumDev adcf06c149 Reduce gif quality for faster playback 2026-02-15 19:55:49 +02:00
EnumDev 5ef39f89cc Update preview gif 2026-02-15 19:52:35 +02:00
EnumDev 738d2c2086 Disale local IP module if no ip is available 2026-02-15 19:42:18 +02:00
EnumDevandGitHub 637bd2c3e3 Merge pull request #1 from EnumeratedDev/experimental
Merge experimental changes
2026-02-15 17:39:04 +00:00
EnumDev 08035010c9 Add used and total GPU vram variables 2025-12-29 19:30:28 +02:00
EnumDev 983b347d88 Add post installation steps to README.md 2025-12-24 17:59:07 +02:00
EnumDev db96cd7e5e Detect GPU VRAM 2025-12-24 17:52:00 +02:00
EnumDev 96a2dee0e1 Update go modules 2025-12-24 17:40:56 +02:00
EnumDev 85d96fceec Improve GPU name fetching 2025-12-24 17:28:42 +02:00
EnumDev d83440491b Merge branch 'master' into experimental 2025-12-23 16:33:58 +02:00
EnumDev 16b27c4002 Update README.md 2025-12-23 16:32:50 +02:00
EnumDev 701585bd5e Remove duplicate whitespaces in motherboard names 2025-12-23 11:42:15 +02:00
EnumDev b60c9df495 Fallback to product name if subsystem can't be detected 2025-12-23 11:27:12 +02:00
EnumDev 428ee7ceee Improve 'cpus' module 2025-12-23 11:21:40 +02:00
EnumDev fbc9977bf4 Improve 'gpus' module 2025-12-23 11:09:07 +02:00
EnumDev 2f71da0673 Rework package counting 2025-12-22 21:18:06 +02:00
EnumDev d4e324d79f Do not attempt to show memory if not found 2025-12-22 20:02:02 +02:00
EnumDev 7f7c7d646d Do not attempt to show init system if not found 2025-12-22 20:01:14 +02:00
EnumDev cecce857e1 Fetch module information outside os.Expand() function 2025-12-22 17:38:35 +02:00
EnumDev fabc1adbf7 Take default color length into account when calculating module text
length
2025-12-22 14:28:51 +02:00
EnumDev 3b7cb5e4e2 Fix wrong module name in default config 2025-12-22 14:14:03 +02:00
12 changed files with 376 additions and 57 deletions
+6 -3
View File
@@ -4,7 +4,7 @@
### 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 still in beta, so distro compatibility is limited. If you would like to contribute ASCII art or add other compatibility features feel free to create a pull request or notify me through GitLab Issues.
At the moment ascii art for different distributions is limited. If you would like to contribute ascii art feel free to make a pull request.
### How it looks
![Stormfetch gif](media/stormfetch.gif)
@@ -19,7 +19,10 @@ Stormfetch is still in beta, so distro compatibility is limited. If you would li
```
make SYSCONFDIR=/etc
```
- Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location
- Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different root directory
```
make install PREFIX=/usr SYSCONFDIR=/etc
make install
make install-config SYSCONFDIR=/etc
```
### Post installation
- (Optional) Download `curl` from your package manager to fetch the amdgpu.ids database for AMD GPUs. It may be safely uninstalled after running stormfetch once with curl installed
+2 -1
View File
@@ -1,11 +1,12 @@
distro_ascii: auto
disable_amdgpu_ids_warning: false
modules:
- name: distribution
- name: hostname
- name: kernel
- name: packages
- name: shell
- name: init
- name: init_system
- name: motherboard
- name: cpus
- name: gpus
Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 12 MiB

+5 -4
View File
@@ -9,10 +9,11 @@ import (
)
type StormfetchConfig struct {
Ascii string `yaml:"distro_ascii"`
Modules []stormfetchModuleConfig `yaml:"modules"`
AnsiiColors []int `yaml:"ansii_colors"`
ForceConfigAnsii bool `yaml:"force_config_ansii"`
Ascii string `yaml:"distro_ascii"`
DisableAmdgpuIdsWarning bool `yaml:"disable_amdgpu_ids_warning"`
Modules []stormfetchModuleConfig `yaml:"modules"`
AnsiiColors []int `yaml:"ansii_colors"`
ForceConfigAnsii bool `yaml:"force_config_ansii"`
}
var config = StormfetchConfig{
+5 -5
View File
@@ -1,19 +1,19 @@
module stormfetch
go 1.22
go 1.24.0
require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a
github.com/jackmordaunt/ghw v1.0.4
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728
github.com/jackmordaunt/ghw v1.0.5
github.com/mitchellh/go-ps v1.0.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/jackmordaunt/pcidb v1.0.1 // indirect
github.com/jackmordaunt/wmi v1.2.4 // indirect
github.com/kr/pretty v0.1.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/sys v0.39.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
+9
View File
@@ -1,9 +1,15 @@
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/jackmordaunt/ghw v1.0.4 h1:as+COFuPuXaNQC3WqzoHS/E2JYWZU7gN8ompNTUxNxs=
github.com/jackmordaunt/ghw v1.0.4/go.mod h1:4dReYvJ36CoAzIxlEx8du25Qi/YqKYvEGE9QJoRXiK8=
github.com/jackmordaunt/ghw v1.0.5 h1:3rTXwu0D9RkungV7/WlhC8HVuDlPaq0aKY8u0I51jLY=
github.com/jackmordaunt/ghw v1.0.5/go.mod h1:VpFlLXnJErgoRttR3WOxun4v5EE8/xfB4cK26G5q2U0=
github.com/jackmordaunt/pcidb v1.0.1 h1:uLLZa6kD5P39r2cwMyJJkxmuHfH9Wq19gYQEbYcB0Z4=
github.com/jackmordaunt/pcidb v1.0.1/go.mod h1:OMmhrZOZVu2hYXhBDZXddypxwKR/dp4DbIgzCkQDxdQ=
github.com/jackmordaunt/wmi v1.2.4 h1:/XyuMiKby0qXNQp1j0uU0JqTWLM0QGOVok1Hf5Yagtg=
@@ -16,8 +22,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+116 -15
View File
@@ -1,9 +1,10 @@
package main
import (
"fmt"
"os"
"os/exec"
"slices"
"strconv"
"strings"
"github.com/go-gl/glfw/v3.3/glfw"
@@ -11,12 +12,22 @@ import (
)
type CPU struct {
Vendor string
Model string
Cores int
Threads int
}
type GPU struct {
PCIAddress string
Vendor string
Name string
Product string
Subsystem string
Driver string
VramTotal string
VramUsed string
}
type Monitor struct {
Width int
Height int
@@ -36,9 +47,23 @@ func GetCPUs(hiddenCPUs []int) []CPU {
continue
}
// Remove unnecessary information from the CPU model
model := cpu.Model
stringsToRemove := []string{
" CPU", " FPU", " APU", " Processor",
" Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core",
" 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core",
}
for _, str := range stringsToRemove {
model = strings.ReplaceAll(model, str, "")
}
model = strings.Split(model, "w/ Radeon ")[0]
model = strings.Split(model, "with Radeon ")[0]
model = strings.Split(model, "@")[0]
model = strings.TrimSpace(model)
ret = append(ret, CPU{
Vendor: cpu.Vendor,
Model: cpu.Model,
Model: model,
Cores: int(cpu.NumCores),
Threads: int(cpu.NumThreads),
})
@@ -47,22 +72,94 @@ func GetCPUs(hiddenCPUs []int) []CPU {
return ret
}
func GetGPUModels(hiddenGPUS []int) (ret []string) {
cmd := exec.Command("sh", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:'")
bytes, err := cmd.Output()
func GetGPUModels(hiddenGPUs []int) []GPU {
ret := make([]GPU, 0)
// Set stderr to nil to avoid warnings
stderr := os.Stderr
os.Stderr = nil
gpus, err := ghw.GPU()
if err != nil {
return nil
return ret
}
for i, gpu := range strings.Split(string(bytes), "\n") {
if slices.Contains(hiddenGPUS, i+1) {
// Restore stderr
os.Stderr = stderr
for i, gpu := range gpus.GraphicsCards {
if slices.Contains(hiddenGPUs, i+1) {
continue
}
if gpu == "" {
continue
// Set alternative names for vendors
var vendor string
switch gpu.DeviceInfo.Vendor.ID {
case "1002":
vendor = "AMD"
case "10de":
vendor = "Nvidia"
case "8086":
vendor = "Intel"
default:
vendor = gpu.DeviceInfo.Vendor.Name
}
gpu = strings.TrimPrefix(strings.TrimSpace(gpu), "Device:\t")
ret = append(ret, gpu)
// Set GPU name
name := ""
// Use GPU name from amdgpu.ids database
if vendor == "AMD" {
fetchedName, err := fetchAmdGpuName(gpu.DeviceInfo.Product.ID, gpu.DeviceInfo.Revision)
if err == nil && !config.DisableAmdgpuIdsWarning {
name = fetchedName
} else {
fmt.Println("Warning: could not fetch GPU name from amdgpu.ids database! Error: " + err.Error())
fmt.Println(" You can disable this warning in the configuration file")
}
}
if name == "" {
if gpu.DeviceInfo.Subsystem.Name == "" || gpu.DeviceInfo.Subsystem.Name == "unknown" {
// Set GPU name to product name
name = gpu.DeviceInfo.Product.Name
} else {
// Set GPU name to subsystem name
name = gpu.DeviceInfo.Subsystem.Name
}
// Use GPU name in brackets
leftBracket := strings.IndexByte(name, '[')
rightBracket := strings.IndexByte(name, ']')
if leftBracket != -1 && rightBracket != -1 {
name = name[leftBracket+1 : rightBracket]
}
}
// Get VRAM
vramTotal := "Unknown"
bytes, err := os.ReadFile("/sys/class/drm/card" + strconv.Itoa(gpu.Index) + "/device/mem_info_vram_total")
if err == nil {
vramUint, _ := strconv.ParseUint(strings.TrimSpace(string(bytes)), 10, 64)
vramTotal = FormatBytes(vramUint)
}
vramUsed := "Unknown"
bytes, err = os.ReadFile("/sys/class/drm/card" + strconv.Itoa(gpu.Index) + "/device/mem_info_vram_used")
if err == nil {
vramUint, _ := strconv.ParseUint(strings.TrimSpace(string(bytes)), 10, 64)
vramUsed = FormatBytes(vramUint)
}
ret = append(ret, GPU{
PCIAddress: gpu.Address,
Vendor: vendor,
Name: name,
Product: gpu.DeviceInfo.Product.Name,
Subsystem: gpu.DeviceInfo.Subsystem.Name,
Driver: gpu.DeviceInfo.Driver,
VramTotal: vramTotal,
VramUsed: vramUsed,
})
}
return ret
@@ -73,7 +170,11 @@ func GetMotherboardModel() string {
if err != nil {
return ""
}
return strings.TrimSpace(string(bytes))
// Remove duplicate whitespaces
ret := strings.Join(strings.Fields(string(bytes)), " ")
return ret
}
func GetMonitors() []Monitor {
+1 -1
View File
@@ -107,7 +107,7 @@ func run() {
}
// Continue if text length is 0
if len(textNoColor) == 0 {
if len(textNoColor)-len(colorMap[0]) == 0 {
continue
}
+45 -11
View File
@@ -100,10 +100,12 @@ func initializeModuleMap() {
// Packages module
packagesModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "packages", Format: "%3Packages: %4$PACKAGES"}, Execute: func(sm StormfetchModule) string {
packages := GetInstalledPackages()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "PACKAGES":
return GetInstalledPackages()
return packages
default:
return ""
}
@@ -113,10 +115,12 @@ func initializeModuleMap() {
// Shell module
shellModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "shell", Format: "%3Shell: %4$SHELL"}, Execute: func(sm StormfetchModule) string {
shell := GetShell()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "SHELL":
return GetShell()
return shell
default:
return ""
}
@@ -126,10 +130,16 @@ func initializeModuleMap() {
// Init system module
initSystemModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "init_system", Format: "%3Init: %4$INIT"}, Execute: func(sm StormfetchModule) string {
initSystem := GetInitSystem()
if initSystem == "" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "INIT":
return GetInitSystem()
return initSystem
default:
return ""
}
@@ -139,10 +149,12 @@ func initializeModuleMap() {
// Libc module
libcModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "libc", Format: "%3Libc: %4$LIBC"}, Execute: func(sm StormfetchModule) string {
libc := GetLibc()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LIBC":
return GetLibc()
return libc
default:
return ""
}
@@ -188,8 +200,6 @@ func initializeModuleMap() {
switch s {
case "CPU_NUM":
return strconv.Itoa(i + 1)
case "CPU_VENDOR":
return cpu.Vendor
case "CPU_MODEL":
return cpu.Model
case "CPU_CORES":
@@ -209,7 +219,7 @@ func initializeModuleMap() {
RegisterModule(cpusModule)
// GPUs module
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_MODEL"}, Execute: func(sm StormfetchModule) string {
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_VENDOR $GPU_NAME"}, Execute: func(sm StormfetchModule) string {
hiddenGPUsInterface, _ := sm.GetData("hidden_gpus", make([]any, 0))
// Convert interface slices to string slices
@@ -226,8 +236,20 @@ func initializeModuleMap() {
switch s {
case "GPU_NUM":
return strconv.Itoa(i + 1)
case "GPU_MODEL":
return gpu
case "GPU_VENDOR":
return gpu.Vendor
case "GPU_NAME":
return gpu.Name
case "GPU_PRODUCT":
return gpu.Product
case "GPU_SUBSYSTEM":
return gpu.Subsystem
case "GPU_DRIVER":
return gpu.Driver
case "GPU_VRAM_TOTAL":
return gpu.VramTotal
case "GPU_VRAM_USED":
return gpu.VramUsed
default:
return ""
}
@@ -244,6 +266,10 @@ func initializeModuleMap() {
memoryModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "memory", Format: "%3Memory: %4$MEM_USED MiB / $MEM_TOTAL MiB"}, Execute: func(sm StormfetchModule) string {
memoryInfo := GetMemoryInfo()
if memoryInfo == nil {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "MEM_TOTAL":
@@ -330,10 +356,17 @@ func initializeModuleMap() {
// Local IP module
localIpModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "local_ip", Format: "%3Local IP: %4$LOCAL_IP"}, Execute: func(sm StormfetchModule) string {
localIP := GetLocalIP()
// Return empty string if local IP is unavailable
if localIP == "Unknown" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LOCAL_IP":
return GetLocalIP()
return localIP
default:
return ""
}
@@ -349,6 +382,7 @@ func initializeModuleMap() {
}
dewm := GetDEWM()
displayProtocol := GetDisplayProtocol()
// Return empty string if can't detect DE/WM
if dewm.Name == "Unknown" {
@@ -364,7 +398,7 @@ func initializeModuleMap() {
case "DEWM_VERSION":
return dewm.Version
case "DISPLAY_PROTOCOL":
return GetDisplayProtocol()
return displayProtocol
default:
return ""
}
+141 -17
View File
@@ -2,25 +2,29 @@ package main
import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"strings"
)
type PackageManager struct {
Name string
ExecutableName string
PackageListCommand string
Name string
ExecutableName string
GetPackages func(...any) int
FunctionInput []any
}
var PackageManagers = []PackageManager{
{Name: "dpkg", ExecutableName: "dpkg", PackageListCommand: "dpkg-query -f '${Package}\\n' -W"},
{Name: "pacman", ExecutableName: "pacman", PackageListCommand: "pacman -Q"},
{Name: "rpm", ExecutableName: "rpm", PackageListCommand: "rpm -qa"},
{Name: "xbps", ExecutableName: "xbps-query", PackageListCommand: "xbps-query -l"},
{Name: "bpm", ExecutableName: "bpm", PackageListCommand: "ls /var/lib/bpm/installed/"},
{Name: "portage", ExecutableName: "emerge", PackageListCommand: "find /var/db/pkg/*/ -mindepth 1 -maxdepth 1"},
{Name: "flatpak", ExecutableName: "flatpak", PackageListCommand: "flatpak list"},
{Name: "snap", ExecutableName: "snap", PackageListCommand: "snap list | tail +2"},
{Name: "dpkg", ExecutableName: "dpkg", GetPackages: pmFileLines, FunctionInput: []any{"/var/lib/dpkg/status", "Status: install ok installed"}},
{Name: "pacman", ExecutableName: "pacman", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/pacman/local/", true}},
{Name: "rpm", ExecutableName: "rpm", GetPackages: pmShellCommandLines, FunctionInput: []any{"rpm -qa"}},
{Name: "xbps", ExecutableName: "xbps-query", GetPackages: pmFileLines, FunctionInput: []any{"/var/db/xbps/pkgdb-0.38.plist", "<string>installed</string>"}},
{Name: "bpm", ExecutableName: "bpm", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/bpm/installed/"}},
{Name: "portage", ExecutableName: "emerge", GetPackages: pmPortage},
{Name: "flatpak", ExecutableName: "flatpak", GetPackages: pmFlatpak},
{Name: "snap", ExecutableName: "snap", GetPackages: pmSnap},
}
func (pm *PackageManager) CountPackages() int {
@@ -29,12 +33,7 @@ func (pm *PackageManager) CountPackages() int {
return 0
}
output, err := exec.Command("/bin/sh", "-c", pm.PackageListCommand).Output()
if err != nil {
return 0
}
return strings.Count(string(output), "\n")
return pm.GetPackages(pm.FunctionInput...)
}
func GetInstalledPackages() (ret string) {
@@ -51,3 +50,128 @@ func GetInstalledPackages() (ret string) {
return ret
}
func pmDirectoryElements(args ...any) int {
directory := args[0].(string)
dirsOnly := false
if len(args) >= 2 {
dirsOnly = args[1].(bool)
}
total := 0
dirEntries, _ := os.ReadDir(directory)
for _, entry := range dirEntries {
if !entry.IsDir() && dirsOnly {
continue
}
total++
}
return total
}
func pmFileLines(args ...any) int {
filepath := args[0].(string)
mustContain := ""
if len(args) >= 2 {
mustContain = args[1].(string)
}
total := 0
content, err := os.ReadFile(filepath)
if err == nil {
for _, line := range strings.Split(strings.TrimSpace(string(content)), "\n") {
if mustContain != "" && !strings.Contains(line, mustContain) {
continue
}
total++
}
}
return total
}
func pmShellCommandLines(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
return len(strings.Split(output, "\n"))
}
func pmShellCommandOutput(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
packageCount, _ := strconv.Atoi(output)
return packageCount
}
func pmPortage(args ...any) int {
portageDir := "/var/db/pkg"
total := 0
dirEntries, _ := os.ReadDir(portageDir)
for _, repo := range dirEntries {
if !repo.IsDir() {
continue
}
packages, _ := os.ReadDir(path.Join(portageDir, repo.Name()))
total += len(packages)
}
return total
}
func pmFlatpak(args ...any) int {
arch := GetArch()
flatpakDir := "/var/lib/flatpak"
total := 0
// Count applications
apps, err := os.ReadDir(path.Join(flatpakDir, "app"))
if err == nil {
for _, app := range apps {
if strings.HasSuffix(app.Name(), ".Locale") || strings.HasSuffix(app.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "app", app.Name(), arch))
total += len(dirEntries)
}
}
// Count runtimes
runtimes, err := os.ReadDir(path.Join(flatpakDir, "runtime"))
if err == nil {
for _, runtime := range runtimes {
if strings.HasSuffix(runtime.Name(), ".Locale") || strings.HasSuffix(runtime.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "runtime", runtime.Name(), arch))
total += len(dirEntries)
}
}
return total
}
func pmSnap(args ...any) int {
total := pmDirectoryElements("/snap", true)
if total > 0 {
return total - 1
}
total = pmDirectoryElements("/var/lib/snapd/snap", true)
if total > 0 {
return total - 1
}
return 0
}
+5
View File
@@ -138,6 +138,11 @@ func GetInitSystem() string {
return ""
}
// Return if init system can't be found
if process == nil {
return ""
}
// Special cases
// OpenRC check
if _, err := os.Stat("/usr/sbin/openrc"); err == nil {
+41
View File
@@ -5,6 +5,7 @@ import (
"math"
"os"
"os/exec"
"path"
"regexp"
"strings"
)
@@ -58,6 +59,46 @@ func ReadKeyValueFile(filepath string) (map[string]string, error) {
return ret, nil
}
func fetchAmdGpuName(productId, revision string) (string, error) {
productId = strings.ToUpper(productId)
revision = strings.ToUpper(revision[2:])
// Get cache directory
cachedir, err := os.UserCacheDir()
if err != nil {
return "", err
}
// Ensure amdgpu.ids file exists and download it if it doesn't
if _, err := os.Stat(path.Join(cachedir, "amdgpu.ids")); err != nil {
cmd := exec.Command("curl", "-o", path.Join(cachedir, "amdgpu.ids"), "https://gitlab.freedesktop.org/mesa/libdrm/-/raw/main/data/amdgpu.ids")
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("Could not fetch amdgpu.ids using curl")
}
}
// Read amdgpu.ids file
amdgpuIds, err := os.ReadFile(path.Join(cachedir, "amdgpu.ids"))
if err != nil {
return "", err
}
// Parse read data and find GPU
for _, line := range strings.Split(string(amdgpuIds), "\n") {
if len(line) < 2 || line[0] == '#' {
continue
}
fields := strings.Split(line, ",\t")
if fields[0] == productId && fields[1] == revision {
return strings.TrimPrefix(fields[2], "AMD "), nil
}
}
return "", nil
}
func runCommand(command string, shell string) string {
cmd := exec.Command(shell, "-c", command)
workdir, err := os.Getwd()