[Pyobjc-dev] Trouble using DiskArbitration interface
Brought to you by:
ronaldoussoren
From: Merlijn B.W. W. <me...@ar...> - 2017-01-02 16:27:39
|
Hi, I'm working on porting some CD archival software from Ubuntu to OS X, and I am trying to prevent OS X from mounting CDs, because it interferes with the CD ripping process. I've found that I can register with 'diskarbitrationd' and register a callback when a disk is about to be mounted: using DiskArbitration.da.DARegisterDiskMountApprovalCallback Using this interface, I've been able to block mount requests successfully. However, after some time, usually after blocking a few mounts, my simple python program segfaults, and I am pretty sure it has something to do with memory management. I've tried to find information about this on the official documentation, but that was a bit hard to find, apart from "Do not worry about reference counting". I am trying to re-create code from here, but in python with pyobjc: https://developer.apple.com/library/content/documentation/DriversKernelHardware/Conceptual/DiskArbitrationProgGuide/ArbitrationBasics/ArbitrationBasics.html (section "Approving or Refusing a Request") Example [1] will always segfault, after (or while) returning from allow_mount. Example [2] is hacky, and works for a while, and will then segfault, but confirms my suspicion that memory management issues are causing these problems. Specifically, I fear that the dissenter object that I create is somehow being freed or corrupted. However, if create it only once for the program lifetime, I get similar issues - so it seems like it is simply being freed at some point? Triggering OS X to mount can be done like so: diskutil mount /dev/disk1 if the mount is blocked, it will say: Volume on disk1 failed to mount; if it has a partitioning scheme, use "diskutil mountDisk" If the volume is damaged, try the "readOnly" option And unmounting can be done like so: diskutil unmount /Volumes/Audio\ CD/ Any suggestions on how to tackle or debug this problem are most welcome. :-) Regards, Merlijn Example 1: Output: $ python osxdiskarbitration-example.py OSXDA: start_blocking started OSXDA: Session: <DASession 0x7fc9a9fa2ee0 [0x1059e9ed0]>{id = python [3595]:7947} OSXDA: allow_mount call: <DADisk 0x7fc9ab800600 [0x1059e9ed0]>{id = /dev/disk1} 0 OSXDA: Blocking mount Code: from __future__ import print_function import objc objc.options.verbose = True import DiskArbitration as da @objc.callbackFor(da.DARegisterDiskMountApprovalCallback) def allow_mount(disk, context): print('OSXDA: allow_mount call: ', disk, context) if True: #dissenter = da.DADissenterCreate(da.kCFAllocatorDefault, # da.kDAReturnExclusiveAccess, None) # We cannot pass kDAReturnExclusiveAccess initially, for some reason, this raises: # ValueError: depythonifying 'int', got 'int' of wrong magnitude # So set it to 42, and change it later dissenter = da.DADissenterCreate(da.kCFAllocatorDefault, 42, None) dissenter["DAStatus"] = da.kDAReturnExclusiveAccess print('OSXDA: Blocking mount') return dissenter # Deny else: print('OSXDA: Allowing mount') return None # Allow def start_blocking(): print('OSXDA: start_blocking started') session = da.DASessionCreate(da.kCFAllocatorDefault) if not session: print('OSXDA: Failed to create session') exit(1) print('OSXDA: Session:', session) da.DASessionScheduleWithRunLoop(session, da.CFRunLoopGetMain(), da.kCFRunLoopCommonModes); #da.DASessionScheduleWithRunLoop(session, da.CFRunLoopGetCurrent(), # da.kCFRunLoopDefaultMode); # Register callback da.DARegisterDiskMountApprovalCallback(session, None, allow_mount, None) da.CFRunLoopRun() da.DASessionUnscheduleWithRunLoop(session, da.CFRunLoopGetCurrent(), da.kCFRunLoopDefaultMode); print('OSXDA: Done with loop') CFRelease(session) if __name__ == '__main__': start_blocking() Example 2: Output: $ python osxdiskarbitration-example-2.py OSXDA: start_blocking started OSXDA: Session: <DASession 0x7f8e9bf56cf0 [0x1022aced0]>{id = python [3644]:7947} OSXDA: allow_mount call: <DADisk 0x7f8e9be73340 [0x1022aced0]>{id = /dev/disk1} 0 OSXDA: Blocking mount OSXDA: allow_mount call: <DADisk 0x7f8e9bd052e0 [0x1022aced0]>{id = /dev/disk1} 0 OSXDA: Blocking mount OSXDA: allow_mount call: <DADisk 0x7f8e9be73340 [0x1022aced0]>{id = /dev/disk1} 0 OSXDA: Blocking mount OSXDA: allow_mount call: <DADisk 0x7f8e9be73340 [0x1022aced0]>{id = /dev/disk1} 0 OSXDA: Blocking mount OSXDA: allow_mount call: <DADisk 0x7f8e9be73b80 [0x1022aced0]>{id = /dev/disk1} 0 OSXDA: Blocking mount OSXDA: allow_mount call: <DADisk 0x7f8e9be75240 [0x1022aced0]>{id = /dev/disk1} 0 Segmentation fault: 11 Code: from __future__ import print_function import objc objc.options.verbose = True import DiskArbitration as da memhack = [] @objc.callbackFor(da.DARegisterDiskMountApprovalCallback) def allow_mount(disk, context): print('OSXDA: allow_mount call: ', disk, context) if True: #dissenter = da.DADissenterCreate(da.kCFAllocatorDefault, # da.kDAReturnExclusiveAccess, None) # We cannot pass kDAReturnExclusiveAccess initially, for some reason, this raises: # ValueError: depythonifying 'int', got 'int' of wrong magnitude # So set it to 42, and change it later dissenter = da.DADissenterCreate(da.kCFAllocatorDefault, 42, None) dissenter["DAStatus"] = da.kDAReturnExclusiveAccess memhack.append(dissenter) print('OSXDA: Blocking mount') return dissenter # Deny else: print('OSXDA: Allowing mount') return None # Allow def start_blocking(): print('OSXDA: start_blocking started') session = da.DASessionCreate(da.kCFAllocatorDefault) if not session: print('OSXDA: Failed to create session') exit(1) print('OSXDA: Session:', session) da.DASessionScheduleWithRunLoop(session, da.CFRunLoopGetMain(), da.kCFRunLoopCommonModes); #da.DASessionScheduleWithRunLoop(session, da.CFRunLoopGetCurrent(), # da.kCFRunLoopDefaultMode); # Register callback da.DARegisterDiskMountApprovalCallback(session, None, allow_mount, None) da.CFRunLoopRun() da.DASessionUnscheduleWithRunLoop(session, da.CFRunLoopGetCurrent(), da.kCFRunLoopDefaultMode); print('OSXDA: Done with loop') CFRelease(session) if __name__ == '__main__': start_blocking() |