go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/smbios/osx.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package smbios 5 6 import ( 7 "bytes" 8 "io" 9 "strings" 10 11 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 12 "go.mondoo.com/cnquery/providers/os/connection/shared" 13 plist "howett.net/plist" 14 ) 15 16 // We use the ioreg implementation to get native access. Its preferred 17 // over system_profiler since its much faster 18 // 19 // hardware info 20 // ioreg -rw0 -d2 -c IOPlatformExpertDevice -a 21 // 22 // acpi info: 23 // ioreg -rw0 -d1 -c AppleACPIPlatformExpert 24 // 25 // get the rom version: 26 // ioreg -r -p IODeviceTree -n rom@0 27 // 28 // helpful mac commands: 29 // https://github.com/erikberglund/Scripts/blob/master/snippets/macos_hardware.md 30 // 31 // results can be compared with dmidecode 32 // http://cavaliercoder.com/blog/dmidecode-for-apple-osx.html 33 type OSXSmbiosManager struct { 34 provider shared.Connection 35 platform *inventory.Platform 36 } 37 38 func (s *OSXSmbiosManager) Name() string { 39 return "macOS Smbios Manager" 40 } 41 42 func (s *OSXSmbiosManager) Info() (*SmBiosInfo, error) { 43 smInfo := SmBiosInfo{} 44 45 cmd, err := s.provider.RunCommand("ioreg -rw0 -d2 -c IOPlatformExpertDevice -a") 46 if err != nil { 47 return nil, err 48 } 49 50 hw, err := ParseMacosIOPlatformExpertDevice(cmd.Stdout) 51 if err != nil { 52 return nil, err 53 } 54 55 smInfo.SysInfo.Vendor = plistData(hw.Manufacturer) 56 smInfo.SysInfo.Model = plistData(hw.Model) 57 smInfo.SysInfo.Version = plistData(hw.Version) 58 smInfo.SysInfo.SerialNumber = hw.IOPlatformSerialNumber 59 smInfo.SysInfo.UUID = hw.IOPlatformUUID 60 61 smInfo.BaseBoardInfo.Vendor = plistData(hw.Manufacturer) 62 smInfo.BaseBoardInfo.Model = plistData(hw.BoardID) 63 smInfo.BaseBoardInfo.Version = "" 64 smInfo.BaseBoardInfo.SerialNumber = hw.IOPlatformSerialNumber 65 66 smInfo.ChassisInfo.Vendor = plistData(hw.Manufacturer) 67 smInfo.ChassisInfo.Version = plistData(hw.BoardID) 68 smInfo.ChassisInfo.SerialNumber = hw.IOPlatformSerialNumber 69 smInfo.ChassisInfo.Type = "Laptop" 70 71 cmd, err = s.provider.RunCommand("ioreg -r -p IODeviceTree -n rom@0 -a") 72 if err != nil { 73 return nil, err 74 } 75 76 // TODO: this does not work on m1 macs yet, we need to find a way to gather that information 77 if s.platform.Arch == "x86_64" { 78 bios, err := ParseMacosBios(cmd.Stdout) 79 if err != nil { 80 return nil, err 81 } 82 smInfo.BIOS.ReleaseDate = plistData(bios.ReleaseDate) 83 smInfo.BIOS.Vendor = plistData(bios.Vendor) 84 smInfo.BIOS.Version = plistData(bios.Version) 85 } 86 87 return &smInfo, nil 88 } 89 90 func plistData(data []byte) string { 91 return string(bytes.Trim(data, "\x00")) 92 } 93 94 type IOPlatformExpertDevice struct { 95 IORegistryEntryName string `plist:"IORegistryEntryName"` 96 BoardID []byte `plist:"board-id"` 97 Manufacturer []byte `plist:"manufacturer"` 98 Model []byte `plist:"model"` 99 SerialNumber []byte `plist:"serial-number"` 100 IOPlatformUUID string `plist:"IOPlatformUUID"` 101 IOPlatformSerialNumber string `plist:"IOPlatformSerialNumber"` 102 ProductName []byte `plist:"product-name"` 103 Version []byte `plist:"version"` 104 } 105 106 func ParseMacosIOPlatformExpertDevice(input io.Reader) (*IOPlatformExpertDevice, error) { 107 var r io.ReadSeeker 108 r, ok := input.(io.ReadSeeker) 109 110 // if the read seeker is not implemented lets cache stdout in-memory 111 if !ok { 112 entries, err := io.ReadAll(input) 113 if err != nil { 114 return nil, err 115 } 116 r = strings.NewReader(string(entries)) 117 } 118 119 var data []IOPlatformExpertDevice 120 decoder := plist.NewDecoder(r) 121 err := decoder.Decode(&data) 122 if err != nil { 123 return nil, err 124 } 125 126 return &data[0], nil 127 } 128 129 type IODeviceTree struct { 130 Vendor []byte `plist:"vendor"` 131 ReleaseDate []byte `plist:"release-date"` 132 Version []byte `plist:"version"` 133 } 134 135 func ParseMacosBios(input io.Reader) (*IODeviceTree, error) { 136 var r io.ReadSeeker 137 r, ok := input.(io.ReadSeeker) 138 139 // if the read seeker is not implemented lets cache stdout in-memory 140 if !ok { 141 entries, err := io.ReadAll(input) 142 if err != nil { 143 return nil, err 144 } 145 r = strings.NewReader(string(entries)) 146 } 147 148 var data []IODeviceTree 149 decoder := plist.NewDecoder(r) 150 err := decoder.Decode(&data) 151 if err != nil { 152 return nil, err 153 } 154 155 return &data[0], nil 156 }