Skip to content

Commit

Permalink
feat: add smbios info to report
Browse files Browse the repository at this point in the history
Signed-off-by: Brian McGee <[email protected]>
  • Loading branch information
brianmcgee committed Jul 31, 2024
1 parent c3229b7 commit 7f1b1c4
Show file tree
Hide file tree
Showing 28 changed files with 1,367 additions and 21 deletions.
21 changes: 5 additions & 16 deletions pkg/facter/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,17 @@ package facter

import (
"fmt"
"slices"

"github.com/numtide/nixos-facter/pkg/build"
"github.com/numtide/nixos-facter/pkg/hwinfo"
"github.com/numtide/nixos-facter/pkg/virt"
)

type Report struct {
Virtualisation virt.Type `json:"virtualisation"`
Hardware []*hwinfo.HardwareItem `json:"hardware"`
Smbios []hwinfo.Smbios `json:"smbios,omitempty"`
System string `json:"system"`
}

func (r *Report) AddHardwareItem(item *hwinfo.HardwareItem) {
r.Hardware = append(r.Hardware, item)

// canonically sort by device index
slices.SortFunc(r.Hardware, func(a, b *hwinfo.HardwareItem) int {
return int(a.Index) - int(b.Index)
})
Virtualisation virt.Type `json:"virtualisation"`
}

func GenerateReport() (*Report, error) {
Expand All @@ -32,14 +23,12 @@ func GenerateReport() (*Report, error) {
}
report.System = build.System

if err := hwinfo.Scan(func(item *hwinfo.HardwareItem) error {
report.AddHardwareItem(item)
return nil
}); err != nil {
var err error
report.Smbios, report.Hardware, err = hwinfo.Scan()
if err != nil {
return nil, fmt.Errorf("failed to scan hardware: %w", err)
}

var err error
if report.Virtualisation, err = virt.Detect(); err != nil {
return nil, fmt.Errorf("failed to detect virtualisation: %w", err)
}
Expand Down
29 changes: 24 additions & 5 deletions pkg/hwinfo/hwinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,46 @@ package hwinfo
#cgo pkg-config: hwinfo
#include <hd.h>
#include <stdlib.h>
// CGO cannot access union type fields, so we do this as a workaround
hd_smbios_t* hd_smbios_next(hd_smbios_t *sm) { return sm->next; }
*/
import "C"

import (
"slices"
"unsafe"
)

func Scan(f func(item *HardwareItem) error) error {
func Scan() ([]Smbios, []*HardwareItem, error) {
data := (*C.hd_data_t)(unsafe.Pointer(C.calloc(1, C.size_t(unsafe.Sizeof(C.hd_data_t{})))))

C.hd_set_probe_feature(data, C.enum_probe_feature(ProbeFeatureAll))
C.hd_scan(data)
defer C.hd_free_hd_data(data)

var smbiosItems []Smbios
for sm := data.smbios; sm != nil; sm = C.hd_smbios_next(sm) {
item, err := NewSmbios(sm)
if err != nil {
return nil, nil, err
}
smbiosItems = append(smbiosItems, item)
}

var hardwareItems []*HardwareItem
for hd := data.hd; hd != nil; hd = hd.next {
if item, err := NewHardwareItem(hd); err != nil {
return err
} else if err = f(item); err != nil {
return err
return nil, nil, err
} else {
hardwareItems = append(hardwareItems, item)
}
}

return nil
// canonically sort by device index
slices.SortFunc(hardwareItems, func(a, b *HardwareItem) int {
return int(a.Index) - int(b.Index)
})

return smbiosItems, hardwareItems, nil
}
161 changes: 161 additions & 0 deletions pkg/hwinfo/smbios.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package hwinfo

/*
#cgo pkg-config: hwinfo
#include <hd.h>
// CGO cannot access union type fields, so we do this as a workaround
hd_smbios_type_t hd_smbios_get_type(hd_smbios_t *sm) { return sm->any.type; }
smbios_biosinfo_t hd_smbios_get_biosinfo(hd_smbios_t *sm) { return sm->biosinfo; }
smbios_sysinfo_t hd_smbios_get_sysinfo(hd_smbios_t *sm) { return sm->sysinfo; }
smbios_boardinfo_t hd_smbios_get_boardinfo(hd_smbios_t *sm) { return sm->boardinfo; }
smbios_chassis_t hd_smbios_get_chassis(hd_smbios_t *sm) { return sm->chassis; }
smbios_processor_t hd_smbios_get_processor(hd_smbios_t *sm) { return sm->processor; }
smbios_cache_t hd_smbios_get_cache(hd_smbios_t *sm) { return sm->cache; }
smbios_connect_t hd_smbios_get_connect(hd_smbios_t *sm) { return sm->connect; }
smbios_slot_t hd_smbios_get_slot(hd_smbios_t *sm) { return sm->slot; }
smbios_onboard_t hd_smbios_get_onboard(hd_smbios_t *sm) { return sm->onboard; }
smbios_oem_t hd_smbios_get_oem(hd_smbios_t *sm) { return sm->oem; }
smbios_config_t hd_smbios_get_config(hd_smbios_t *sm) { return sm->config; }
smbios_lang_t hd_smbios_get_lang(hd_smbios_t *sm) { return sm->lang; }
smbios_group_t hd_smbios_get_group(hd_smbios_t *sm) { return sm->group; }
smbios_memarray_t hd_smbios_get_memarray(hd_smbios_t *sm) { return sm->memarray; }
smbios_memarraymap_t hd_smbios_get_memarraymap(hd_smbios_t *sm) { return sm->memarraymap; }
smbios_memdevice_t hd_smbios_get_memdevice(hd_smbios_t *sm) { return sm->memdevice; }
smbios_memerror_t hd_smbios_get_memerror(hd_smbios_t *sm) { return sm->memerror; }
smbios_mem64error_t hd_smbios_get_mem64error(hd_smbios_t *sm) { return sm->mem64error; }
smbios_memdevicemap_t hd_smbios_get_memdevicemap(hd_smbios_t *sm) { return sm->memdevicemap; }
smbios_mouse_t hd_smbios_get_mouse(hd_smbios_t *sm) { return sm->mouse; }
smbios_secure_t hd_smbios_get_secure(hd_smbios_t *sm) { return sm->secure; }
smbios_power_t hd_smbios_get_power(hd_smbios_t *sm) { return sm->power; }
smbios_any_t hd_smbios_get_any(hd_smbios_t *sm) { return sm->any; }
*/
import "C"

//go:generate enumer -type=SmbiosType -json -transform=snake -trimprefix SmbiosType -output=./smbios_enum_type.go
type SmbiosType uint

// For a full list of types see https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf.
// hwinfo doesn't provide structs for all of these, but we've ensured we at least have their ids so they format
// well in the json output when used with the Any type.
const (
SmbiosTypeBios SmbiosType = iota
SmbiosTypeSystem
SmbiosTypeBoard
SmbiosTypeChassis

SmbiosTypeProcessor
SmbiosTypeMemoryController
SmbiosTypeMemoryModule
SmbiosTypeCache

SmbiosTypePortConnector
SmbiosTypeSlot
SmbiosTypeOnboard
SmbiosTypeOEMStrings

SmbiosTypeConfig
SmbiosTypeLanguage
SmbiosTypeGroupAssociations
SmbiosTypeEventLog

SmbiosTypeMemoryArray
SmbiosTypeMemoryDevice
SmbiosTypeMemoryError
SmbiosTypeMemoryArrayMappedAddress

SmbiosTypeMemoryDeviceMappedAddress
SmbiosTypePointingDevice
SmbiosTypeBattery
SmbiosTypeSystemReset

SmbiosTypeHardwareSecurity
SmbiosTypePowerControls
SmbiosTypeVoltage
SmbiosTypeCoolingDevice

SmbiosTypeTemperature
SmbiosTypeCurrent
SmbiosTypeOutOfBandRemoteAccess
SmbiosTypeBootIntegrityServices

SmbiosTypeSystemBoot
SmbiosTypeMemory64Error
SmbiosTypeManagementDevice
SmbiosTypeManDeviceComponent
SmbiosTypeManDeviceThreshold
SmbiosTypeMemoryChannel
SmbiosTypeIPMIDevice

SmbiosTypeSystemPowerSupply
SmbiosTypeAdditionalInfo
SmbiosTypeOnboardExtended
SmbiosTypeManagementControllerHostInterface
SmbiosTypeTPM
SmbiosTypeProcessorAdditional
SmbiosFirmwareInventory

SmbiosTypeInactive SmbiosType = 126
SmbiosTypeEndOfTable SmbiosType = 127
)

type Smbios interface {
SmbiosType() SmbiosType
}

func NewSmbios(smbios *C.hd_smbios_t) (Smbios, error) {
if smbios == nil {
return nil, nil
}

smbiosType := SmbiosType(C.hd_smbios_get_type(smbios))

switch smbiosType {
case SmbiosTypeBios:
return NewSmbiosBiosInfo(C.hd_smbios_get_biosinfo(smbios))
case SmbiosTypeSystem:
return NewSmbiosSysInfo(C.hd_smbios_get_sysinfo(smbios))
case SmbiosTypeBoard:
return NewSmbiosBoardInfo(C.hd_smbios_get_boardinfo(smbios))
case SmbiosTypeChassis:
return NewSmbiosChassis(C.hd_smbios_get_chassis(smbios))
case SmbiosTypeProcessor:
return NewSmbiosProcessor(C.hd_smbios_get_processor(smbios))
case SmbiosTypeCache:
return NewSmbiosCache(C.hd_smbios_get_cache(smbios))
case SmbiosTypePortConnector:
return NewSmbiosConnect(C.hd_smbios_get_connect(smbios))
case SmbiosTypeSlot:
return NewSmbiosSlot(C.hd_smbios_get_slot(smbios))
case SmbiosTypeOnboard:
return NewSmbiosOnboard(C.hd_smbios_get_onboard(smbios))
case SmbiosTypeOEMStrings:
return NewSmbiosOEM(C.hd_smbios_get_oem(smbios))
case SmbiosTypeConfig:
return NewSmbiosConfig(C.hd_smbios_get_config(smbios))
case SmbiosTypeLanguage:
return NewSmbiosLang(C.hd_smbios_get_lang(smbios))
case SmbiosTypeGroupAssociations:
return NewSmbiosGroup(C.hd_smbios_get_group(smbios))
case SmbiosTypeMemoryArray:
return NewSmbiosMemArray(C.hd_smbios_get_memarray(smbios))
case SmbiosTypeMemoryDevice:
return NewSmbiosMemDevice(C.hd_smbios_get_memdevice(smbios))
case SmbiosTypeMemoryError:
return NewSmbiosMemError(C.hd_smbios_get_memerror(smbios))
case SmbiosTypeMemory64Error:
return NewSmbiosMem64Error(C.hd_smbios_get_mem64error(smbios))
case SmbiosTypeMemoryArrayMappedAddress:
return NewSmbiosMemArrayMap(C.hd_smbios_get_memarraymap(smbios))
case SmbiosTypeMemoryDeviceMappedAddress:
return NewSmbiosMemDeviceMap(C.hd_smbios_get_memdevicemap(smbios))
case SmbiosTypePointingDevice:
return NewSmbiosMouse(C.hd_smbios_get_mouse(smbios))
case SmbiosTypeHardwareSecurity:
return NewSmbiosSecure(C.hd_smbios_get_secure(smbios))
case SmbiosTypePowerControls:
return NewSmbiosPower(C.hd_smbios_get_power(smbios))
default:
return NewSmbiosAny(smbiosType, C.hd_smbios_get_any(smbios))
}
}
33 changes: 33 additions & 0 deletions pkg/hwinfo/smbios_any.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hwinfo

/*
#cgo pkg-config: hwinfo
#include <hd.h>
*/
import "C"

import (
"fmt"
"unsafe"
)

// SmbiosAny captures generic smbios data.
type SmbiosAny struct {
Type SmbiosType `json:"type"`
Handle int `json:"handle"`
Data string `json:"data"`
Strings []string `json:"strings,omitempty"`
}

func (s SmbiosAny) SmbiosType() SmbiosType {
return s.Type
}

func NewSmbiosAny(smbiosType SmbiosType, info C.smbios_any_t) (Smbios, error) {
return SmbiosAny{
Type: smbiosType,
Handle: int(info.handle),
Data: fmt.Sprintf("0x%x", ReadByteArray(unsafe.Pointer(info.data), int(info.data_len))),
Strings: ReadStringList(info.strings),
}, nil
}
40 changes: 40 additions & 0 deletions pkg/hwinfo/smbios_bios.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package hwinfo

/*
#cgo pkg-config: hwinfo
#include <hd.h>
*/
import "C"

import (
"fmt"
)

// SmbiosBios captures BIOS related information.
type SmbiosBios struct {
Type SmbiosType `json:"type"`
Handle int `json:"handle"`
Vendor string `json:"vendor"`
Version string `json:"version"`
Date string `json:"date"`
Features []string `json:"features"`
StartAddress string `json:"start_address"`
RomSize uint `json:"rom_size"`
}

func (s SmbiosBios) SmbiosType() SmbiosType {
return s.Type
}

func NewSmbiosBiosInfo(info C.smbios_biosinfo_t) (Smbios, error) {
return SmbiosBios{
Type: SmbiosTypeBios,
Handle: int(info.handle),
Vendor: C.GoString(info.vendor),
Version: C.GoString(info.version),
Date: C.GoString(info.date),
Features: ReadStringList(info.feature.str),
StartAddress: fmt.Sprintf("0x%x", uint(info.start)),
RomSize: uint(info.rom_size),
}, nil
}
45 changes: 45 additions & 0 deletions pkg/hwinfo/smbios_board.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package hwinfo

/*
#cgo pkg-config: hwinfo
#include <hd.h>
*/
import "C"
import "unsafe"

// SmbiosBoard captures motherboard related information.
type SmbiosBoard struct {
Type SmbiosType `json:"type"`
Handle int `json:"handle"`
Manufacturer string `json:"manufacturer"`
Product string `json:"product"`
Version string `json:"version"`
Serial string `json:"-"` // omit from json output
AssetTag string `json:"asset_tag,omitempty"`
BoardType *Id `json:"board_type"`
Features []string `json:"features"`
Location string `json:"location"` // location in chassis
Chassis int `json:"chassis"` // handle of chassis
Objects []int `json:"objects,omitempty"` // array of object handles
}

func (s SmbiosBoard) SmbiosType() SmbiosType {
return s.Type
}

func NewSmbiosBoardInfo(info C.smbios_boardinfo_t) (Smbios, error) {
return SmbiosBoard{
Type: SmbiosTypeBoard,
Handle: int(info.handle),
Manufacturer: C.GoString(info.manuf),
Product: C.GoString(info.product),
Version: C.GoString(info.version),
Serial: C.GoString(info.serial),
AssetTag: C.GoString(info.asset),
BoardType: NewId(info.board_type),
Features: ReadStringList(info.feature.str),
Location: C.GoString(info.location),
Chassis: int(info.chassis),
Objects: ReadIntArray(unsafe.Pointer(info.objects), int(info.objects_len)),
}, nil
}
Loading

0 comments on commit 7f1b1c4

Please sign in to comment.