Files
bee/audit/internal/collector/pcie.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
}