102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
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
|
|
}
|