188 lines
4.6 KiB
Go
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
|
|
} |