Thread: [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() |
From: Ronald O. <ron...@ma...> - 2017-01-18 19:46:59
|
> On 2 Jan 2017, at 17:08, Merlijn B.W. Wajer <me...@ar...> wrote: > > 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. :-) Which version of PyObjC are you using? Likewise for the python version. Ronald > > 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 > _______________________________________________ > Pyobjc-dev mailing list > Pyo...@li... > https://lists.sourceforge.net/lists/listinfo/pyobjc-dev |