package collector import ( "bee/audit/internal/schema" "log/slog" "strings" ) // collectMemory runs dmidecode -t 17 and returns all memory slots. func collectMemory() []schema.HardwareMemory { out, err := runDmidecode("17") if err != nil { slog.Warn("memory: dmidecode type 17 failed", "err", err) return nil } dimms := parseMemory(out) slog.Info("memory: collected", "count", len(dimms)) return dimms } func parseMemory(output string) []schema.HardwareMemory { sections := splitDMISections(output, "Memory Device") dimms := make([]schema.HardwareMemory, 0, len(sections)) for _, fields := range sections { dimm := parseMemorySection(fields) dimms = append(dimms, dimm) } return dimms } func parseMemorySection(fields map[string]string) schema.HardwareMemory { dimm := schema.HardwareMemory{} if v := cleanDMIValue(fields["Locator"]); v != "" { dimm.Slot = &v } if v := cleanDMIValue(fields["Bank Locator"]); v != "" { dimm.Location = &v } // presence: "No Module Installed" or size == 0 present := true rawSize := fields["Size"] if strings.Contains(strings.ToLower(rawSize), "no module") || rawSize == "0" || rawSize == "" { present = false } dimm.Present = &present if !present { status := "EMPTY" dimm.Status = &status return dimm } status := "OK" dimm.Status = &status if mb := parseMemorySizeMB(rawSize); mb > 0 { dimm.SizeMB = &mb } if v := cleanDMIValue(fields["Type"]); v != "" && v != "Unknown" { dimm.Type = &v } if mhz := parseInt(strings.TrimSuffix(fields["Speed"], " MT/s")); mhz > 0 { dimm.MaxSpeedMHz = &mhz } if mhz := parseInt(strings.TrimSuffix(fields["Configured Memory Speed"], " MT/s")); mhz > 0 { dimm.CurrentSpeedMHz = &mhz } if v := cleanDMIValue(fields["Manufacturer"]); v != "" { dimm.Manufacturer = &v } if v := cleanDMIValue(fields["Serial Number"]); v != "" { dimm.SerialNumber = &v } if v := cleanDMIValue(fields["Part Number"]); v != "" { p := strings.TrimSpace(v) dimm.PartNumber = &p } return dimm } // parseMemorySizeMB parses DMI size strings: "8 GB", "2048 MB", "No Module Installed". func parseMemorySizeMB(s string) int { s = strings.TrimSpace(s) if strings.Contains(strings.ToLower(s), "no module") { return 0 } if strings.HasSuffix(s, " GB") { n := parseInt(strings.TrimSuffix(s, " GB")) return n * 1024 } if strings.HasSuffix(s, " MB") { return parseInt(strings.TrimSuffix(s, " MB")) } return parseInt(s) }