Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get diskutil info output in a cocoa application

Is there a way to programmatically get the same information that diskutil info / | grep "Free Space" gives you? (For obvious reasons I'd rather have a better way to do this than just parsing the result of that command.)

Currently I'm using statfs; however, it was brought to my attention that the space this reports is not always accurate, because OS X also places temporary files such as Time Machine snapshots on the drive. These files automatically get deleted if space is running out, and the OS does not report the usage of these files. In other words, statfs often gives a lower number of free space than diskutil info or looking at the disk information in Finder.

like image 783
houbysoft Avatar asked Jan 11 '23 15:01

houbysoft


1 Answers

You can use popen(3):

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    FILE *f;
    char info[256];

    f = popen("/usr/sbin/diskutil info /", "r");
    if (f == NULL) {
        perror("Failed to run diskutil");
        exit(0);
    }

    while (fgets(info, sizeof(info), f) != NULL) {
        printf("%s", info);
    }

    pclose(f);

    return 0;
}

EDIT

Sorry, I didn't read the question carefully. You can also use the Disk Arbitration Framework. There's also some sample code that might be helpful (FSMegaInfo).

UPDATE

I took a look at the output from otool -L $(which diskutil) and it seems that it's using a private framework called DiskManagement.framework. After looking at the output from class-dump I saw there's a volumeFreeSpaceForDisk:error: method. So the sizes I got from diskutil -info / and FSMegaInfo FSGetVolumeInfo / and my tool were:

  • diskutil: 427031642112 Bytes

  • my tool: volumeFreeSpaceForDisk: 427031642112

  • FSMegaInfo: freeBytes = 427031642112 (397 GB)

I also observed that the sizes differ (with a few KB) every time I ran one of the tools and also that diskutil is dividing by 1000 and FSMegaInfo is dividing by 1024, so the size in GB will be always different (same reason as with df -h and df -H and diskutil - base 10 and base 2).

Here's my sample tool:

#import <Foundation/Foundation.h>
#import "DiskManagement.h"
#import <DiskArbitration/DADisk.h>

int main(int argc, char *argv[])
{
    int                 err;
    const char *        bsdName = "disk0s2";
    DASessionRef        session;
    DADiskRef           disk;
    CFDictionaryRef     descDict;
    session  = NULL;
    disk     = NULL;
    descDict = NULL;
    if (err == 0) {session = DASessionCreate(NULL); if (session == NULL) {err = EINVAL;}}
    if (err == 0) {disk = DADiskCreateFromBSDName(NULL, session, bsdName); if (disk == NULL) {err = EINVAL;}}
    if (err == 0) {descDict = DADiskCopyDescription(disk); if (descDict == NULL) {err = EINVAL;}}

    DMManager *dmMan = [DMManager sharedManager];
    NSLog(@"blockSizeForDisk: %@", [dmMan blockSizeForDisk:disk error:nil]);
    NSLog(@"totalSizeForDisk: %@", [dmMan totalSizeForDisk:disk error:nil]);
    NSLog(@"volumeTotalSizeForDisk: %@", [dmMan volumeTotalSizeForDisk:disk error:nil]);
    NSLog(@"volumeFreeSpaceForDisk: %@", [dmMan volumeFreeSpaceForDisk:disk error:nil]);

    return 0;
}

You can obtain the DiskManagement.h by running class-dump /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/Current/DiskManagement > DiskManagement.h and you can link to that framework by including the private frameworks path using -F/System/Library/PrivateFrameworks/ and add -framework.

Compile:

clang -g tool.m -F/System/Library/PrivateFrameworks/ -framework Foundation -framework DiskArbitration -framework DiskManagement -o tool

UPDATE2: You can also take a look here and here. If the FSMegaInfo sample is not working for you, then you can just stat the /Volumes/.MobileBackups and subtract it's size from what you get from statfs("/", &stats).

like image 193
Ivan Genchev Avatar answered Jan 22 '23 15:01

Ivan Genchev