Trouble using DiskArbitration interface
"Merlijn B.W. Wajer" <[email protected]> Mon, 2 Jan 2017 17:08:05 +0100
| Newsgroups | gmane.comp.python.pyobjc.devel |
|---|---|
| Message-ID | <[email protected]> |
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()
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot