master ohmu/fastfetch-openbsd / src / detection / physicaldisk / physicaldisk_apple.c
ohmu  ·  2026-10-15
  1#include "physicaldisk.h"
  2#include "common/apple/cf_helpers.h"
  3#include "common/debug.h"
  4
  5#include <IOKit/IOKitLib.h>
  6#include <IOKit/IOBSD.h>
  7#include <IOKit/storage/IOMedia.h>
  8#include <IOKit/storage/IOBlockStorageDriver.h>
  9#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 10#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
 11#ifdef MAC_OS_X_VERSION_10_15
 12    #include <IOKit/storage/nvme/NVMeSMARTLibExternal.h>
 13#endif
 14
 15#ifdef MAC_OS_X_VERSION_10_15
 16static inline void wrapIoDestroyPlugInInterface(IOCFPlugInInterface*** pluginInf) {
 17    assert(pluginInf);
 18    if (*pluginInf) {
 19        IODestroyPlugInInterface(*pluginInf);
 20    }
 21}
 22#endif
 23
 24static const char* detectSsdTemp(io_service_t entryPhysical, double* temp) {
 25#ifdef MAC_OS_X_VERSION_10_15
 26    [[gnu::cleanup(wrapIoDestroyPlugInInterface)]] IOCFPlugInInterface** pluginInf = nullptr;
 27    int32_t score;
 28    if (IOCreatePlugInInterfaceForService(entryPhysical, kIONVMeSMARTUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInf, &score) != kIOReturnSuccess) {
 29        FF_DEBUG("IOCreatePlugInInterfaceForService(kIONVMeSMARTUserClientTypeID) failed");
 30        return "IOCreatePlugInInterfaceForService() failed";
 31    }
 32
 33    IONVMeSMARTInterface** smartInf = nullptr;
 34    if ((*pluginInf)->QueryInterface(pluginInf, CFUUIDGetUUIDBytes(kIONVMeSMARTInterfaceID), (LPVOID) &smartInf) != kIOReturnSuccess) {
 35        FF_DEBUG("QueryInterface(kIONVMeSMARTInterfaceID) failed");
 36        return "QueryInterface() failed";
 37    }
 38
 39    NVMeSMARTData smartData;
 40    const char* error = nullptr;
 41    if ((*smartInf)->SMARTReadData(smartInf, &smartData) == kIOReturnSuccess) {
 42        *temp = smartData.TEMPERATURE - 273;
 43    } else {
 44        error = "SMARTReadData() failed";
 45    }
 46
 47    (*pluginInf)->Release(smartInf);
 48    return error;
 49#else
 50    FF_DEBUG("SSD temperature detection needs macOS 10.15 or newer");
 51    return "No support for old MacOS version";
 52#endif
 53}
 54
 55const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) {
 56    FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = 0;
 57    if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOBlockStorageDriverClass), &iterator) != KERN_SUCCESS) {
 58        FF_DEBUG("IOServiceGetMatchingServices(kIOBlockStorageDriverClass) failed");
 59        return "IOServiceGetMatchingServices() failed";
 60    }
 61
 62    io_registry_entry_t registryEntry;
 63    while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
 64        FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDriver = registryEntry;
 65
 66        FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryMedia = IO_OBJECT_NULL;
 67        IORegistryEntryGetChildEntry(entryDriver, kIOServicePlane, &entryMedia);
 68
 69        FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryPhysical = 0;
 70        if (IORegistryEntryGetParentEntry(entryDriver, kIOServicePlane, &entryPhysical) != KERN_SUCCESS) {
 71            continue;
 72        }
 73
 74        FF_STRBUF_AUTO_DESTROY deviceName = ffStrbufCreate();
 75
 76        if (entryMedia != IO_OBJECT_NULL) {
 77            io_name_t name;
 78            if (IORegistryEntryGetName(entryMedia, name) == KERN_SUCCESS) {
 79                ffStrbufSetS(&deviceName, name);
 80            }
 81        }
 82
 83        if (deviceName.length == 0) {
 84            FF_CFTYPE_AUTO_RELEASE CFDictionaryRef deviceCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyDeviceCharacteristicsKey), nullptr, kNilOptions);
 85            if (deviceCharacteristics) {
 86                if (ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyVendorNameKey), &deviceName) == nullptr && deviceName.length > 0) {
 87                    ffStrbufAppendC(&deviceName, ' ');
 88                }
 89
 90                FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
 91                if (ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductNameKey), &name) == nullptr) {
 92                    ffStrbufAppend(&deviceName, &name);
 93                }
 94            }
 95        }
 96
 97        if (options->namePrefix.length && ffStrbufStartsWith(&deviceName, &options->namePrefix) != 0) {
 98            continue;
 99        }
100
101        FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE;
102
103        FF_STRBUF_AUTO_DESTROY interconnect = ffStrbufCreate();
104        FF_CFTYPE_AUTO_RELEASE CFDictionaryRef protocolCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyProtocolCharacteristicsKey), nullptr, kNilOptions);
105        if (protocolCharacteristics) {
106            if (ffCfDictGetString(protocolCharacteristics, CFSTR(kIOPropertyPhysicalInterconnectTypeKey), &interconnect) == nullptr) {
107                if (ffStrbufEqualS(&interconnect, kIOPropertyPhysicalInterconnectTypeVirtual)) {
108                    if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) {
109                        continue;
110                    }
111
112                    type |= FF_PHYSICALDISK_TYPE_VIRTUAL;
113                    FF_STRBUF_AUTO_DESTROY location = ffStrbufCreate();
114                    if (ffCfDictGetString(protocolCharacteristics, CFSTR(kIOPropertyPhysicalInterconnectLocationKey), &location) == nullptr) {
115                        ffStrbufAppendS(&interconnect, " - ");
116                        ffStrbufAppend(&interconnect, &location);
117                    }
118                }
119            }
120        }
121
122        uint64_t size = 0;
123        if (entryMedia != IO_OBJECT_NULL) {
124            FF_CFTYPE_AUTO_RELEASE CFNumberRef mediaSize = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaSizeKey), nullptr, kNilOptions);
125            if (mediaSize) {
126                ffCfNumGetInt64(mediaSize, (int64_t*) &size);
127            }
128        }
129
130        if (size == 0) {
131            if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) {
132                continue;
133            }
134
135            type |= FF_PHYSICALDISK_TYPE_UNUSED;
136        }
137
138        FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result);
139        ffStrbufInit(&device->serial);
140        ffStrbufInit(&device->revision);
141        ffStrbufInitMove(&device->name, &deviceName);
142        ffStrbufInit(&device->devPath);
143        ffStrbufInitMove(&device->interconnect, &interconnect);
144        device->type = type;
145        device->size = size;
146        device->temperature = FF_PHYSICALDISK_TEMP_UNSET;
147
148        if (entryMedia != IO_OBJECT_NULL) {
149            FF_CFTYPE_AUTO_RELEASE CFBooleanRef removable = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaRemovableKey), nullptr, kNilOptions);
150            if (removable) {
151                device->type |= CFBooleanGetValue(removable) ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED;
152            }
153
154            FF_CFTYPE_AUTO_RELEASE CFBooleanRef writable = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaWritableKey), nullptr, kNilOptions);
155            if (writable) {
156                device->type |= CFBooleanGetValue(writable) ? FF_PHYSICALDISK_TYPE_READWRITE : FF_PHYSICALDISK_TYPE_READONLY;
157            }
158
159            FF_CFTYPE_AUTO_RELEASE CFStringRef bsdName = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOBSDNameKey), nullptr, kNilOptions);
160            if (bsdName) {
161                ffCfStrGetString(bsdName, &device->devPath);
162                ffStrbufPrependS(&device->devPath, "/dev/");
163            }
164        }
165
166        FF_CFTYPE_AUTO_RELEASE CFDictionaryRef deviceCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyDeviceCharacteristicsKey), nullptr, kNilOptions);
167        if (deviceCharacteristics) {
168            ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductSerialNumberKey), &device->serial);
169            ffStrbufTrimSpace(&device->serial);
170            ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductRevisionLevelKey), &device->revision);
171            ffStrbufTrimRightSpace(&device->revision);
172
173            if (!(device->type & FF_PHYSICALDISK_TYPE_VIRTUAL)) {
174                CFStringRef mediumType = (CFStringRef) CFDictionaryGetValue(deviceCharacteristics, CFSTR(kIOPropertyMediumTypeKey));
175                if (mediumType) {
176                    if (CFStringCompare(mediumType, CFSTR(kIOPropertyMediumTypeSolidStateKey), 0) == 0) {
177                        device->type |= FF_PHYSICALDISK_TYPE_SSD;
178                    } else if (CFStringCompare(mediumType, CFSTR(kIOPropertyMediumTypeRotationalKey), 0) == 0) {
179                        device->type |= FF_PHYSICALDISK_TYPE_HDD;
180                    }
181                }
182            }
183        }
184
185#ifdef MAC_OS_X_VERSION_10_15
186        if (!(device->type & FF_PHYSICALDISK_TYPE_VIRTUAL) && options->temp) {
187            FF_CFTYPE_AUTO_RELEASE CFBooleanRef nvmeSMARTCapable = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyNVMeSMARTCapableKey), nullptr, kNilOptions);
188            if (nvmeSMARTCapable && CFBooleanGetValue(nvmeSMARTCapable)) {
189                detectSsdTemp(entryPhysical, &device->temperature);
190            }
191        }
192#endif
193    }
194
195    return nullptr;
196}