5
\$\begingroup\$

I have the following code that makes use of the DiskArbitration framework to mount a disk to its default location:

#import <CoreFoundation/CoreFoundation.h>
#import <DiskArbitration/DiskArbitration.h>

void MountCallback(DADiskRef disk, DADissenterRef dissenter, void *context);

int main(int argc, const char *argv[]) {
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <disk>\n", argv[0]);
    return EXIT_FAILURE;
  }

  const char *deviceName = argv[1];

  DASessionRef session = DASessionCreate(kCFAllocatorDefault);
  DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, deviceName);

  // Pass NULL for a "standard" mount path.
  DADiskMount(disk, NULL, kDADiskMountOptionWhole, MountCallback, (void *)deviceName);

  DASessionSetDispatchQueue(session, NULL);
  CFRelease(session);
  session = NULL;

  return EXIT_SUCCESS;
}

void MountCallback(DADiskRef disk, DADissenterRef dissenter, void *context) {
  const char *mountedDisk = context;
  fprintf(stderr, "Device mounted: %s\n", mountedDisk);
  fflush(stderr);
  return;
}

To compile the code:

clang -Wall -Werror -g -v main.m -lobjc -framework DiskArbitration -framework Foundation -o mount

To run the program:

./mount diskN

The program works fine, however as a developer without much knowledge on the Apple platform, I'm interested in the following points:

  • The MountCallback function doesn't appear to be called. I don't see the line I'm printing to stderr.
  • In case the mount operation fails, how do I access the error, and use it return a different exit status on main? (I assume the error is caught from the callback, but can't find documentation about it).
  • Am I releasing the session and other resources I'm using for the task correctly?
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Strictly speaking, there's no ObjC here. This is all "just" C. \$\endgroup\$
    – jscs
    Commented Mar 25, 2016 at 3:59
  • \$\begingroup\$ I agree with @JoshCaswell. Although you're using a framework very commonly used in Objective-C (CoreFoundation), nothing about this code is Objective-C. I've changed the tag and removed some parts of the question that don't make a whole lot of sense. (You should be able to compile this as C rather than ObjC.) \$\endgroup\$
    – nhgrif
    Commented Mar 27, 2016 at 21:32

2 Answers 2

2
\$\begingroup\$

A few years later, and not being a C expert; As far as I know (using Pascal myself), your application needs to wait for the callback to happen. By the looks of it though (again: not an expert) your application may already closed/finished before the callback actually happened.

\$\endgroup\$
2
\$\begingroup\$

There are 2 things missing from your code. As @Hanzaplastique mentioned, you need to wait for the callback to be executed. Secondly, you need to pass a valid queue to DASessionSetDispatchQueue(). If you pass NULL as in your code, you're telling it to unschedule the session.

You can use a dispatch_semaphore_t to wait between threads. Putting it all together, I get this:

#include <stdio.h>
#import <CoreFoundation/CoreFoundation.h>
#import <DiskArbitration/DiskArbitration.h>

static dispatch_semaphore_t semaphore;

void MountCallback(DADiskRef disk, DADissenterRef dissenter, void *context);

int main(int argc, const char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <disk>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *deviceName = argv[1];

    // Create the semaphore we'll wait on.
    semaphore = dispatch_semaphore_create(0);

    DASessionRef session = DASessionCreate(kCFAllocatorDefault);
    DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, deviceName);

    // Pass NULL for a "standard" mount path.
    DADiskMount(disk, NULL, kDADiskMountOptionWhole, MountCallback, (void *)deviceName);

    // We'll pass the global concurrent queue here so it gets executed in the background
    DASessionSetDispatchQueue(session, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0));
    CFRelease(session);
    session = NULL;

    // Wait for the callback to signal that it's done
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // Clean up
    dispatch_release(semaphore);

    return EXIT_SUCCESS;
}

void MountCallback(DADiskRef disk, DADissenterRef dissenter, void *context) {
    const char *mountedDisk = context;
    fprintf(stderr, "Device mounted: %s\n", mountedDisk);
    fflush(stderr);

    // Tell the main thread that we've finished up.
    dispatch_semaphore_signal(semaphore);
    return;
}

One other suggestion - I would change the usage to say <device> instead of <disk>. It wasn't immediately clear to me wether I was supposed to have something like "Macintosh HD" (the disk name) in there or "disk5s1" (the device name). It looks to me like it's supposed to be the device name, correct?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.