13#include "libmythbase/mythconfig.h"
21#include <IOKit/IOKitLib.h>
22#include <IOKit/storage/IOMedia.h>
23#include <IOKit/storage/IOCDMedia.h>
24#include <IOKit/storage/IODVDMedia.h>
25#include <IOKit/storage/IOBlockStorageDevice.h>
26#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
27#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
28#include <DiskArbitration/DiskArbitration.h>
31#define IOMainPort IOMasterPort
39 CFArrayRef keys,
void *context);
50 kern_return_t kernResult = 0;
51 io_iterator_t iter = 0;
53 QString msg = QString(
"FindMediaType() - ");
56 kernResult = IORegistryEntryCreateIterator(service,
58 kIORegistryIterateRecursively
59 | kIORegistryIterateParents,
62 if (KERN_SUCCESS != kernResult)
64 LOG(VB_GENERAL, LOG_CRIT, msg +
65 QString(
"IORegistryEntryCreateIterator returned %1")
70 LOG(VB_GENERAL, LOG_CRIT, msg +
71 "IORegistryEntryCreateIterator returned NULL iterator");
77 IOObjectRetain(service);
81 bool isWholeMedia =
false;
82 if (IOObjectConformsTo(service, kIOMediaClass))
84 CFTypeRef wholeMedia =
nullptr;
86 wholeMedia = IORegistryEntryCreateCFProperty
87 (service, CFSTR(kIOMediaWholeKey),
88 kCFAllocatorDefault, 0);
92 LOG(VB_GENERAL, LOG_ALERT, msg +
93 "Could not retrieve Whole property");
97 isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
98 CFRelease(wholeMedia);
104 if (IOObjectConformsTo(service, kIODVDMediaClass))
106 else if (IOObjectConformsTo(service, kIOCDMediaClass))
110 IOObjectRelease(service);
111 service = IOIteratorNext(iter);
114 IOObjectRelease(iter);
124 CFMutableDictionaryRef matchingDict =
nullptr;
125 kern_return_t kernResult = 0;
126 io_iterator_t iter = 0;
127 io_service_t service = 0;
128 QString msg = QString(
"MediaTypeForBSDName(%1)")
131 if (!bsdName || !*bsdName)
133 LOG(VB_GENERAL, LOG_ALERT, msg +
" - No name supplied?");
137 matchingDict = IOBSDNameMatching(
sMasterPort, 0, bsdName);
140 LOG(VB_GENERAL, LOG_ALERT,
141 msg +
" - IOBSDNameMatching() returned a NULL dictionary.");
147 kernResult = IOServiceGetMatchingServices(
sMasterPort, matchingDict, &iter);
149 if (KERN_SUCCESS != kernResult)
151 LOG(VB_GENERAL, LOG_ALERT,
152 QString(msg +
" - IOServiceGetMatchingServices() returned %2")
158 LOG(VB_GENERAL, LOG_ALERT,
159 msg +
" - IOServiceGetMatchingServices() returned a NULL "
164 service = IOIteratorNext(iter);
168 IOObjectRelease(iter);
172 LOG(VB_GENERAL, LOG_ALERT,
173 msg +
" - IOIteratorNext() returned a NULL iterator");
177 IOObjectRelease(service);
187 CFStringRef name =
nullptr;
191 CFDictionaryGetValue(diskDetails, kDADiskDescriptionVolumeNameKey);
196 size = CFStringGetLength(name) + 1;
199 volName.resize(size);
200 }
catch (std::bad_alloc &ex) {
201 LOG(VB_GENERAL, LOG_ALERT,
202 QString(
"getVolName() - Can't resize string(%1)?").arg(size));
206 if (!CFStringGetCString(name, volName.data(), size, kCFStringEncodingUTF8))
215static QString
getModel(CFDictionaryRef diskDetails)
218 const void *strRef =
nullptr;
221 if (kCFBooleanTrue ==
222 CFDictionaryGetValue(diskDetails,
223 kDADiskDescriptionDeviceInternalKey))
224 desc.append(
"Internal ");
227 strRef = CFDictionaryGetValue(diskDetails,
228 kDADiskDescriptionDeviceVendorKey);
231 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
232 kCFStringEncodingMacRoman));
237 strRef = CFDictionaryGetValue(diskDetails,
238 kDADiskDescriptionDeviceModelKey);
241 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
242 kCFStringEncodingMacRoman));
247 desc.truncate(desc.length() - 1);
263 const char *BSDname = DADiskGetBSDName(disk);
264 CFDictionaryRef details =
nullptr;
265 bool isCDorDVD =
false;
268 QString msg =
"diskAppearedCallback() - ";
274 LOG(VB_MEDIA, LOG_INFO, msg +
"Skipping non-local device");
280 LOG(VB_GENERAL, LOG_ALERT, msg +
"Error. Invoked with a NULL context.");
292 details = DADiskCopyDescription(disk);
294 if (kCFBooleanFalse ==
295 CFDictionaryGetValue(details, kDADiskDescriptionMediaRemovableKey))
297 LOG(VB_MEDIA, LOG_INFO, msg + QString(
"Skipping non-removable %1")
307 LOG(VB_MEDIA, LOG_INFO, msg + QString(
"No volume name for dev %1")
315 if (model.contains(
"Disk Image"))
317 LOG(VB_MEDIA, LOG_INFO, msg + QString(
"DMG %1 mounted, ignoring")
330 LOG(VB_MEDIA, LOG_INFO, QString(
"Found disk %1 - volume name '%2'.")
331 .arg(BSDname).arg(volName.c_str()));
333 mtd->
diskInsert(BSDname, volName.c_str(), model, isCDorDVD);
340 const char *BSDname = DADiskGetBSDName(disk);
348 if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)),
349 kDADiskDescriptionVolumeNameKey))
351 const char *BSDname = DADiskGetBSDName(disk);
352 CFDictionaryRef details = DADiskCopyDescription(disk);
355 LOG(VB_MEDIA, LOG_INFO, QString(
"Disk %1 - changed name to '%2'.")
356 .arg(BSDname).arg(volName.c_str()));
359 ->diskRename(BSDname, volName.c_str());
371 CFDictionaryRef match = kDADiskDescriptionMatchVolumeMountable;
372 DASessionRef daSession = DASessionCreate(kCFAllocatorDefault);
374 if (daSession ==
nullptr)
376 LOG(VB_GENERAL, LOG_ALERT,
"Couldn't create session for MonitorThreadDarwin.");
383 DARegisterDiskAppearedCallback(daSession, match,
385 DARegisterDiskDisappearedCallback(daSession, match,
387 DARegisterDiskDescriptionChangedCallback(daSession, match,
388 kDADiskDescriptionWatchVolumeName,
391 DASessionScheduleWithRunLoop(daSession,
392 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
401 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
408 CFRelease(daSession);
420 const QString& model,
bool isCDorDVD)
423 QString msg =
"MonitorThreadDarwin::diskInsert";
425 LOG(VB_MEDIA, LOG_DEBUG, msg + QString(
"(%1,%2,'%3',%4)")
426 .arg(devName).arg(volName).arg(model).arg(isCDorDVD));
435 LOG(VB_GENERAL, LOG_ALERT, msg +
"Couldn't create MythMediaDevice.");
444 QString mnt =
"/Volumes/"; mnt += volName;
451 LOG(VB_MEDIA, LOG_WARNING,
452 (msg +
"() - Waiting for mount '%1' to become stable.").arg(mnt));
453 std::this_thread::sleep_for(120ms);
454 if ( ++attempts > 4 )
455 std::this_thread::sleep_for(200ms);
459 LOG(VB_MEDIA, LOG_ALERT, msg +
"() - Giving up");
481 LOG(VB_MEDIA, LOG_DEBUG,
482 QString(
"MonitorThreadDarwin::diskRemove(%1)").arg(devName));
489 LOG(VB_MEDIA, LOG_INFO,
"Couldn't find MythMediaDevice: " + devName);
502 LOG(VB_MEDIA, LOG_DEBUG,
503 QString(
"MonitorThreadDarwin::diskRename(%1,%2)")
504 .arg(devName).arg(volName));
514 pDevice->
setMountPath((QString(
"/Volumes/") + volName).toLatin1().constData());
523 LOG(VB_MEDIA, LOG_INFO,
524 QString(
"Couldn't find MythMediaDevice: %1").arg(devName));
549 qRegisterMetaType<MythMediaStatus>(
"MythMediaStatus");
551 LOG(VB_MEDIA, LOG_NOTICE,
"Starting MediaMonitor");
565 LOG(VB_GENERAL, LOG_ERR,
"MediaMonitor::AddDevice(null)");
599 CFMutableDictionaryRef props =
nullptr;
601 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyProtocolCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
605 const void *location = CFDictionaryGetValue(props, CFSTR(kIOPropertyPhysicalInterconnectLocationKey));
606 if (CFEqual(location, CFSTR(
"Internal")))
607 desc.append(
"Internal ");
610 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
613 const void *product = CFDictionaryGetValue(props, CFSTR(kIOPropertyProductNameKey));
614 const void *vendor = CFDictionaryGetValue(props, CFSTR(kIOPropertyVendorNameKey));
617 desc.append(CFStringGetCStringPtr((CFStringRef)vendor, kCFStringEncodingMacRoman));
622 desc.append(CFStringGetCStringPtr((CFStringRef)product, kCFStringEncodingMacRoman));
628 desc.truncate(desc.length() - 1);
645 kern_return_t kernResult = 0;
646 CFMutableDictionaryRef devices =
nullptr;
647 io_iterator_t iter = 0;
649 QString msg = QString(
"GetCDRomBlockDevices() - ");
652 devices = IOServiceMatching(kIOBlockStorageDeviceClass);
655 LOG(VB_GENERAL, LOG_ALERT, msg +
"No Storage Devices? Unlikely!");
660 kernResult = IOServiceGetMatchingServices(
sMasterPort, devices, &iter);
662 if (KERN_SUCCESS != kernResult)
664 LOG(VB_GENERAL, LOG_ALERT, msg +
665 QString(
"IORegistryEntryCreateIterator returned %1")
671 LOG(VB_GENERAL, LOG_ALERT, msg +
672 "IORegistryEntryCreateIterator returned a NULL iterator");
676 io_object_t drive = 0;
678 while ((drive = IOIteratorNext(iter)))
680 CFMutableDictionaryRef
p =
nullptr;
682 IORegistryEntryCreateCFProperties(drive, &
p, kCFAllocatorDefault, 0);
685 const void *
type = CFDictionaryGetValue(
p, CFSTR(
"device-type"));
687 if (CFEqual(
type, CFSTR(
"DVD")) || CFEqual(
type, CFSTR(
"CD")))
692 LOG(VB_MEDIA, LOG_INFO, desc.prepend(
"Found CD/DVD: "));
698 LOG(VB_GENERAL, LOG_ALERT,
699 msg +
"Could not retrieve drive properties");
702 IOObjectRelease(drive);
705 IOObjectRelease(iter);
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void diskInsert(const char *devName, const char *volName, const QString &model, bool isCDorDVD=1)
Create a MythMedia instance and insert in MythMediaMonitor list.
void diskRename(const char *devName, const char *volName)
Deal with the user, or another program, renaming a volume.
void diskRemove(const QString &devName)
void run(void) override
Use the DiskArbitration Daemon to inform us of media changes.
QPointer< MediaMonitor > m_monitor
static MythCDROM * get(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
static MythHDD * Get(QObject *par, const char *devicePath, bool SuperMount, bool AllowEject)
Helper function used to create a new instance of a hard disk device.
static const iso6937table * d
#define LOG(_MASK_, _LEVEL_, _QSTRING_)