add: memory, storage, pcie collectors (1.4-1.6) — tested on real hardware
This commit is contained in:
101
audit/internal/collector/pcie.go
Normal file
101
audit/internal/collector/pcie.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bee/audit/internal/schema"
|
||||
"log/slog"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func collectPCIe() []schema.HardwarePCIeDevice {
|
||||
out, err := exec.Command("lspci", "-vmm", "-D").Output()
|
||||
if err != nil {
|
||||
slog.Warn("pcie: lspci failed", "err", err)
|
||||
return nil
|
||||
}
|
||||
devs := parseLspci(string(out))
|
||||
slog.Info("pcie: collected", "count", len(devs))
|
||||
return devs
|
||||
}
|
||||
|
||||
func parseLspci(output string) []schema.HardwarePCIeDevice {
|
||||
// lspci -vmm -D outputs blank-line separated records, each field is "Key:\tValue"
|
||||
var devs []schema.HardwarePCIeDevice
|
||||
for _, block := range strings.Split(output, "\n\n") {
|
||||
block = strings.TrimSpace(block)
|
||||
if block == "" {
|
||||
continue
|
||||
}
|
||||
fields := map[string]string{}
|
||||
for _, line := range strings.Split(block, "\n") {
|
||||
idx := strings.Index(line, ":\t")
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(line[:idx])
|
||||
val := strings.TrimSpace(line[idx+2:])
|
||||
fields[key] = val
|
||||
}
|
||||
dev := parseLspciDevice(fields)
|
||||
devs = append(devs, dev)
|
||||
}
|
||||
return devs
|
||||
}
|
||||
|
||||
func parseLspciDevice(fields map[string]string) schema.HardwarePCIeDevice {
|
||||
dev := schema.HardwarePCIeDevice{}
|
||||
present := true
|
||||
dev.Present = &present
|
||||
status := "OK"
|
||||
dev.Status = &status
|
||||
|
||||
// Slot is the BDF: "0000:00:02.0"
|
||||
if bdf := fields["Slot"]; bdf != "" {
|
||||
dev.BDF = &bdf
|
||||
// parse vendor_id and device_id from sysfs
|
||||
vendorID, deviceID := readPCIIDs(bdf)
|
||||
if vendorID != 0 {
|
||||
dev.VendorID = &vendorID
|
||||
}
|
||||
if deviceID != 0 {
|
||||
dev.DeviceID = &deviceID
|
||||
}
|
||||
}
|
||||
|
||||
if v := fields["Class"]; v != "" {
|
||||
dev.DeviceClass = &v
|
||||
}
|
||||
if v := fields["Vendor"]; v != "" {
|
||||
dev.Manufacturer = &v
|
||||
}
|
||||
if v := fields["Device"]; v != "" {
|
||||
dev.Model = &v
|
||||
}
|
||||
|
||||
// SVendor/SDevice available but not in schema — skip
|
||||
|
||||
return dev
|
||||
}
|
||||
|
||||
// readPCIIDs reads vendor and device IDs from sysfs for a given BDF.
|
||||
func readPCIIDs(bdf string) (vendorID, deviceID int) {
|
||||
base := "/sys/bus/pci/devices/" + bdf
|
||||
if v, err := readHexFile(base + "/vendor"); err == nil {
|
||||
vendorID = v
|
||||
}
|
||||
if v, err := readHexFile(base + "/device"); err == nil {
|
||||
deviceID = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readHexFile(path string) (int, error) {
|
||||
out, err := exec.Command("cat", path).Output()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
s := strings.TrimSpace(strings.TrimPrefix(string(out), "0x"))
|
||||
n, err := strconv.ParseInt(s, 16, 64)
|
||||
return int(n), err
|
||||
}
|
||||
Reference in New Issue
Block a user