| #include <CoreFoundation/CoreFoundation.h> |
| #include <IOKit/IOKitLib.h> |
| #include <IOKit/storage/IOBlockStorageDriver.h> |
| #include <IOKit/storage/IOMedia.h> |
| #include <IOKit/IOBSD.h> |
| |
| // The iterator of all things disk. Allocated by StartIOCounterFetch, released |
| // by EndIOCounterFetch. |
| static io_iterator_t diskIter; |
| |
| // Begins fetching IO counters. |
| // |
| // Returns 1 if the fetch started successfully, false otherwise. |
| // |
| // If the fetch was started successfully, you must call EndIOCounterFetch once |
| // done to release resources. |
| int StartIOCounterFetch() |
| { |
| if (IOServiceGetMatchingServices(kIOMasterPortDefault, |
| IOServiceMatching(kIOMediaClass), |
| &diskIter) != kIOReturnSuccess) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| // Releases resources from fetching IO counters. |
| void EndIOCounterFetch() |
| { |
| IOObjectRelease(diskIter); |
| } |
| |
| // The current disk entry of interest. Allocated by FetchNextDisk(), released by |
| // ReadDiskInfo(). |
| static io_registry_entry_t diskEntry; |
| |
| // The parent of diskEntry. Same lifetimes. |
| static io_registry_entry_t parentEntry; |
| |
| // Fetches the next disk. Note that a disk entry is allocated, and will be held |
| // until it is processed and freed by ReadDiskInfo. |
| int FetchNextDisk() |
| { |
| while ((diskEntry = IOIteratorNext(diskIter)) != 0) { |
| // We are iterating IOMedia. We need to get the parent too (IOBSD). |
| if (IORegistryEntryGetParentEntry(diskEntry, kIOServicePlane, &parentEntry) != kIOReturnSuccess) { |
| // something is wrong... |
| IOObjectRelease(diskEntry); |
| continue; |
| } |
| |
| if (!IOObjectConformsTo(parentEntry, "IOBlockStorageDriver")) { |
| // no use to us, try the next disk |
| IOObjectRelease(diskEntry); |
| IOObjectRelease(parentEntry); |
| continue; |
| } |
| |
| // Got a disk OK. |
| return 1; |
| } |
| |
| // No more disks. |
| return 0; |
| } |
| |
| // Reads the current disk (from iteration) info into DiskInfo struct. |
| // Once done, all resources from the current iteration of reading are freed, |
| // ready for FetchNextDisk() to be called again. |
| int ReadDiskInfo(DiskInfo *info) |
| { |
| // Parent props. Allocated by us. |
| CFDictionaryRef parentProps = NULL; |
| |
| // Disk props. Allocated by us. |
| CFDictionaryRef diskProps = NULL; |
| |
| // Disk stats, fetched by us, but not allocated by us. |
| CFDictionaryRef stats = NULL; |
| |
| if (IORegistryEntryCreateCFProperties(diskEntry, (CFMutableDictionaryRef *)&parentProps, |
| kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) |
| { |
| // can't get parent props, give up |
| CFRelease(parentProps); |
| IOObjectRelease(diskEntry); |
| IOObjectRelease(parentEntry); |
| return -1; |
| } |
| |
| if (IORegistryEntryCreateCFProperties(parentEntry, (CFMutableDictionaryRef *)&diskProps, |
| kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) |
| { |
| // can't get disk props, give up |
| CFRelease(parentProps); |
| CFRelease(diskProps); |
| IOObjectRelease(diskEntry); |
| IOObjectRelease(parentEntry); |
| return -1; |
| } |
| |
| // Start fetching |
| CFStringRef cfDiskName = (CFStringRef)CFDictionaryGetValue(parentProps, CFSTR(kIOBSDNameKey)); |
| CFStringGetCString(cfDiskName, info->DiskName, MAX_DISK_NAME, CFStringGetSystemEncoding()); |
| stats = (CFDictionaryRef)CFDictionaryGetValue( diskProps, CFSTR(kIOBlockStorageDriverStatisticsKey)); |
| |
| if (stats == NULL) { |
| // stat fetch failed... |
| CFRelease(parentProps); |
| CFRelease(diskProps); |
| IOObjectRelease(parentEntry); |
| IOObjectRelease(diskEntry); |
| return -1; |
| } |
| |
| CFNumberRef cfnum; |
| |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->Reads); |
| } else { |
| info->Reads = 0; |
| } |
| |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->Writes); |
| } else { |
| info->Writes = 0; |
| } |
| |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->ReadBytes); |
| } else { |
| info->ReadBytes = 0; |
| } |
| |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->WriteBytes); |
| } else { |
| info->WriteBytes = 0; |
| } |
| |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->ReadTime); |
| } else { |
| info->ReadTime = 0; |
| } |
| if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) { |
| CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->WriteTime); |
| } else { |
| info->WriteTime = 0; |
| } |
| |
| // note: read/write time are in ns, but we want ms. |
| info->ReadTime = info->ReadTime / 1000 / 1000; |
| info->WriteTime = info->WriteTime / 1000 / 1000; |
| |
| CFRelease(parentProps); |
| CFRelease(diskProps); |
| IOObjectRelease(parentEntry); |
| IOObjectRelease(diskEntry); |
| return 0; |
| } |
| |