Files

188 lines
4.6 KiB
Go

package inventory
import (
"os"
"os/exec"
"runtime"
"strings"
"github.com/fleetforge/forge-agent/internal/pkgmanager"
"github.com/fleetforge/forge-agent/internal/svcmgr"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
func execCmd(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...)
out, err := cmd.Output()
return string(out), err
}
type NetworkInterface struct {
Name string `json:"name"`
Addresses []string `json:"addresses"`
MAC string `json:"mac_address,omitempty"`
}
type SystemInfo struct {
Hostname string
Platform string
OSName string
OSVersion string
Architecture string
KernelVersion string
CPUModel string
CPUCores int
TotalMemory uint64
TotalDisk uint64
IPAddress string
NetworkInterfaces []NetworkInterface
Packages []pkgmanager.PackageInfo
Services []svcmgr.ServiceInfo
}
func CollectSystemInfo() (*SystemInfo, error) {
hostname, _ := os.Hostname()
info := &SystemInfo{
Hostname: hostname,
Platform: runtime.GOOS,
Architecture: runtime.GOARCH,
}
switch runtime.GOOS {
case "linux":
info.OSName = "Linux"
if data, err := os.ReadFile("/etc/os-release"); err == nil {
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "PRETTY_NAME=") {
info.OSName = strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"")
}
if strings.HasPrefix(line, "VERSION_ID=") {
info.OSVersion = strings.Trim(strings.TrimPrefix(line, "VERSION_ID="), "\"")
}
}
}
if data, err := os.ReadFile("/proc/version"); err == nil {
parts := strings.Fields(string(data))
if len(parts) >= 3 {
info.KernelVersion = parts[2]
}
}
case "darwin":
info.OSName = "macOS"
if out, err := execCmd("sw_vers", "-productVersion"); err == nil {
info.OSVersion = strings.TrimSpace(out)
}
if out, err := execCmd("uname", "-r"); err == nil {
info.KernelVersion = strings.TrimSpace(out)
}
case "windows":
info.OSName = "Windows"
if out, err := execCmd("cmd", "/c", "ver"); err == nil {
out = strings.TrimSpace(out)
if start := strings.Index(out, "[Version "); start != -1 {
ver := out[start+len("[Version "):]
if end := strings.Index(ver, "]"); end != -1 {
info.OSVersion = ver[:end]
}
}
if info.OSVersion == "" {
info.OSVersion = out
}
}
if out, err := execCmd("cmd", "/c", "ver"); err == nil {
_ = out
}
if out, err := execCmd("powershell", "-NoProfile", "-NonInteractive", "-Command",
"(Get-CimInstance Win32_OperatingSystem).Caption"); err == nil {
caption := strings.TrimSpace(out)
if caption != "" {
info.OSName = caption
}
}
if out, err := execCmd("powershell", "-NoProfile", "-NonInteractive", "-Command",
"$k = (Get-CimInstance Win32_OperatingSystem).Version; $b = (Get-CimInstance Win32_ComputerSystem).BuildNumber; $k"); err == nil {
if kb := strings.TrimSpace(out); kb != "" {
info.KernelVersion = kb
}
}
}
if cpuInfo, err := cpu.Info(); err == nil && len(cpuInfo) > 0 {
info.CPUModel = cpuInfo[0].ModelName
info.CPUCores = len(cpuInfo)
}
if v, err := mem.VirtualMemory(); err == nil {
info.TotalMemory = v.Total
}
if d, err := disk.Usage("/"); err == nil {
info.TotalDisk = d.Total
}
populateNetwork(info)
if info.OSVersion == "" {
info.OSVersion = "unknown"
}
if pkgs, err := pkgmanager.ListPackages(); err == nil {
info.Packages = pkgs
}
if svcs, err := svcmgr.ListServices(); err == nil {
info.Services = svcs
}
return info, nil
}
func populateNetwork(info *SystemInfo) {
ifs, err := net.Interfaces()
if err != nil {
return
}
var primaryIP string
for _, ifc := range ifs {
if !containsFlag(ifc.Flags, "up") || containsFlag(ifc.Flags, "loopback") {
continue
}
ni := NetworkInterface{
Name: ifc.Name,
Addresses: []string{},
MAC: ifc.HardwareAddr,
}
for _, addr := range ifc.Addrs {
if addr.Addr == "" {
continue
}
ip := strings.TrimSpace(addr.Addr)
ni.Addresses = append(ni.Addresses, ip)
if primaryIP == "" && !strings.Contains(ip, ":") {
primaryIP = strings.Split(ip, "/")[0]
}
}
if len(ni.Addresses) > 0 {
info.NetworkInterfaces = append(info.NetworkInterfaces, ni)
}
}
if primaryIP != "" {
info.IPAddress = primaryIP
}
}
func containsFlag(flags []string, flag string) bool {
for _, f := range flags {
if strings.EqualFold(f, flag) {
return true
}
}
return false
}