namespace RackPeek.Domain.Discovery;
///
/// macOS host probe. Disks are deliberately left out: everything that reports them
/// (diskutil, system_profiler) needs a plist parser for a field that is optional
/// anyway, and omitting is better than guessing — null means "don't touch" on merge.
///
public sealed class MacSystemProbe : ISystemProbe {
public bool IsSupported => OperatingSystem.IsMacOS();
public async Task ReadAsync(CancellationToken cancellationToken = default) {
// The four probes spawn independent processes, so they run side by side.
Task osName = ReadOsNameAsync(cancellationToken);
Task memoryBytes = ReadMemoryBytesAsync(cancellationToken);
Task platformUuid = ReadPlatformUuidAsync(cancellationToken);
Task hypervisorPresent = ReadHypervisorPresentAsync(cancellationToken);
return new RawSystemSnapshot {
Hostname = SystemProbeCommon.Hostname(),
Cores = SystemProbeCommon.Cores(),
Nics = SystemProbeCommon.Nics(),
FallbackMemoryBytes = SystemProbeCommon.FallbackMemoryBytes(),
OsName = await osName,
MemoryBytes = await memoryBytes,
PlatformUuid = await platformUuid,
HypervisorPresent = await hypervisorPresent
};
}
private static async Task ReadOsNameAsync(CancellationToken cancellationToken) {
var product = await SystemProbeCommon.TryRunAsync("sw_vers", "-productName", cancellationToken);
var version = await SystemProbeCommon.TryRunAsync("sw_vers", "-productVersion", cancellationToken);
if (string.IsNullOrWhiteSpace(product))
return null;
return string.IsNullOrWhiteSpace(version) ? product : $"{product} {version}";
}
private static async Task ReadMemoryBytesAsync(CancellationToken cancellationToken) {
var value = await SystemProbeCommon.TryRunAsync("sysctl", "-n hw.memsize", cancellationToken);
return long.TryParse(value, out var bytes) ? bytes : null;
}
private static async Task ReadHypervisorPresentAsync(CancellationToken cancellationToken) {
var value = await SystemProbeCommon.TryRunAsync("sysctl", "-n kern.hv_vmm_present", cancellationToken);
return value?.Trim() == "1";
}
private static async Task ReadPlatformUuidAsync(CancellationToken cancellationToken) {
var output = await SystemProbeCommon.TryRunAsync(
"ioreg", "-rd1 -c IOPlatformExpertDevice", cancellationToken);
return ParsePlatformUuid(output);
}
/// Pulls IOPlatformUUID out of an ioreg dump. Public so it can be tested off a macOS host.
public static string? ParsePlatformUuid(string? ioregOutput) {
if (string.IsNullOrWhiteSpace(ioregOutput))
return null;
foreach (var line in ioregOutput.Split('\n')) {
if (!line.Contains("IOPlatformUUID", StringComparison.Ordinal))
continue;
var parts = line.Split('=', 2);
if (parts.Length == 2)
return parts[1].Trim().Trim('"').Trim();
}
return null;
}
}