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  }