Report hardware and software overviews from IOKit and sysctl in system_profiler

system_profiler now answers SPHardwareDataType and SPSoftwareDataType (and
lists them in -listDataTypes). Model, serial number, platform UUID and
provisioning UDID come from IOKit's IOPlatformExpertDevice via ioreg; CPU,
cache and memory from sysctl; the OS version from sw_vers and uname. A
field the guest does not provide is left out.

#61 instead read a separate darling-smbios.conf written by a new
generator (gen-darling-smbios.py), with hardcoded fallbacks (an Intel Core
i9, macOS 14.7, SIP enabled). iokitd reads darling-platform.plist, so
system_profiler and IOKit could report different serials and models on the
same prefix, and the generator overwrote identities imported with
configure-darling-platform.py. Reading the guest's own APIs keeps a single
source of truth: configure-darling-platform.py remains the only way to set
or provision an identity, and it is still never run by install or launch.

tests/test_system_profiler.py runs the script under sh with stub guest
commands and runs in CI.

Split from #61.

Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
committed 2026-10-18 17:17:05 +02:00
1 parent c943a19606
commit beb64a4271
4 files changed
+193 -3

No files matched your search

+83 -3
View File
@@ -1,6 +1,8 @@
#!/bin/sh
# system_profiler for the Darling prefix: reports the host's real display and
# network hardware for the data types applications query at startup.
# network hardware for the data types applications query at startup. The hardware
# and software overviews come from the guest's own IOKit, sysctl and sw_vers, so they
# match what other APIs report (including a configure-darling-platform.py override).
# Installed as /usr/sbin/system_profiler by tools/install-darling-system-profiler.py.
host=/Volumes/SystemRoot
xml=0
@@ -9,12 +11,12 @@ while [ $# -gt 0 ]; do
case "$1" in
-xml) xml=1;;
-detailLevel|-timeout) shift;;
-listDataTypes) printf 'Available Datatypes:\nSPAirPortDataType\nSPDisplaysDataType\nSPEthernetDataType\n'; exit 0;;
-listDataTypes) printf 'Available Datatypes:\nSPAirPortDataType\nSPDisplaysDataType\nSPEthernetDataType\nSPHardwareDataType\nSPSoftwareDataType\n'; exit 0;;
SP*DataType) types="$types $1";;
esac
shift
done
[ -n "$types" ] || types=" SPDisplaysDataType SPEthernetDataType SPAirPortDataType"
[ -n "$types" ] || types=" SPHardwareDataType SPSoftwareDataType SPDisplaysDataType SPEthernetDataType SPAirPortDataType"
escape() { sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'; }
@@ -46,6 +48,80 @@ displays() { # one line per GPU: vendor|device|vram MB or empty|name
done
}
load_platform() { # IOPlatformExpertDevice string properties, "key|value" per line
# String values print as "value", NUL-terminated data as <"value">; other data is skipped.
platform_properties=$(ioreg -rd1 -c IOPlatformExpertDevice 2>/dev/null |
sed -n 's/^[ |]*"\([^"]*\)" = <\{0,1\}"\(.*\)">\{0,1\}$/\1|\2/p')
}
platform() {
printf '%s\n' "$platform_properties" | awk -v key="$1" 'index($0, key "|") == 1 { print substr($0, length(key) + 2); exit }'
}
machine_name() { # model identifier -> marketing family, as in About This Mac
case "$1" in
MacBookPro*) echo 'MacBook Pro';; MacBookAir*) echo 'MacBook Air';; MacBook*) echo 'MacBook';;
iMacPro*) echo 'iMac Pro';; iMac*) echo 'iMac';; Macmini*) echo 'Mac mini';; MacPro*) echo 'Mac Pro';;
*) echo 'Mac';;
esac
}
hardware() { # label|xml key|xml type|value for each known field
load_platform
model=$(platform model)
cores=$(sysctl -n hw.physicalcpu 2>/dev/null)
memory=$(sysctl -n hw.memsize 2>/dev/null | awk '$1 > 0 { printf "%d GB", $1 / 1073741824 + 0.5 }')
speed=$(sysctl -n hw.cpufrequency 2>/dev/null | awk '$1 > 0 { printf "%.1f GHz", $1 / 1e9 }')
l2=$(sysctl -n hw.l2cachesize 2>/dev/null | awk '$1 > 0 { printf "%d KB", $1 / 1024 }')
l3=$(sysctl -n hw.l3cachesize 2>/dev/null | awk '$1 > 0 { printf "%d MB", $1 / 1048576 }')
echo "Model Name|machine_name|string|${model:+$(machine_name "$model")}"
echo "Model Identifier|machine_model|string|$model"
echo "Processor Name|cpu_type|string|$(sysctl -n machdep.cpu.brand_string 2>/dev/null)"
echo "Processor Speed|current_processor_speed|string|$speed"
echo "Number of Processors|packages|integer|$(sysctl -n hw.packages 2>/dev/null)"
echo "Total Number of Cores|number_processors|integer|$cores"
echo "L2 Cache (per Core)|l2_cache_core|string|$l2"
echo "L3 Cache|l3_cache|string|$l3"
echo "Memory|physical_memory|string|$memory"
echo "Serial Number (system)|serial_number|string|$(platform IOPlatformSerialNumber)"
echo "Hardware UUID|platform_UUID|string|$(platform IOPlatformUUID)"
echo "Provisioning UDID|provisioning_UDID|string|$(platform provisioning_UDID)"
}
software() {
name=$(sw_vers -productName 2>/dev/null); version=$(sw_vers -productVersion 2>/dev/null)
build=$(sw_vers -buildVersion 2>/dev/null)
echo "System Version|os_version|string|${version:+${name:-macOS} $version${build:+ ($build)}}"
echo "Kernel Version|kernel_version|string|$(uname -sr 2>/dev/null)"
echo "Boot Volume|boot_volume|string|Macintosh HD"
echo "Boot Mode|boot_mode|string|normal_boot"
echo "Computer Name|local_host_name|string|$(hostname -s 2>/dev/null)"
echo "User Name|user_name|string|${USER:-$(id -un 2>/dev/null)}"
}
overview_xml() { # $1 = _name; fields on stdin; empty values are omitted
echo ' <dict>'
echo " <key>_name</key><string>$1</string>"
while IFS='|' read -r label key kind value; do
[ -n "$value" ] || continue
if [ "$kind" = integer ]; then
case "$value" in *[!0-9]*) continue;; esac
echo " <key>$key</key><integer>$value</integer>"
else
echo " <key>$key</key><string>$(printf '%s' "$value" | escape)</string>"
fi
done
echo ' </dict>'
}
overview_text() { # $1 = section title, $2 = overview title; fields on stdin
printf '%s:\n\n %s:\n\n' "$1" "$2"
while IFS='|' read -r label key kind value; do
[ "$key" != boot_mode ] || value=Normal
[ -z "$value" ] || printf ' %s: %s\n' "$label" "$value"
done
echo
}
interfaces() { # $1 = wired|wireless; physical host interfaces only
for path in "$host"/sys/class/net/*; do
[ -e "$path/device" ] || continue
@@ -65,6 +141,8 @@ if [ "$xml" = 1 ]; then
echo ' <key>_items</key>'
echo ' <array>'
case "$type" in
SPHardwareDataType) hardware | overview_xml hardware_overview;;
SPSoftwareDataType) software | overview_xml os_overview;;
SPDisplaysDataType)
displays | while IFS='|' read -r vendor device vram name; do
name=$(printf '%s' "$name" | escape)
@@ -108,6 +186,8 @@ fi
for type in $types; do
case "$type" in
SPHardwareDataType) hardware | overview_text Hardware 'Hardware Overview';;
SPSoftwareDataType) software | overview_text Software 'System Software Overview';;
SPDisplaysDataType)
printf 'Graphics/Displays:\n\n'
displays | while IFS='|' read -r vendor device vram name; do