109 lines
3.6 KiB
Objective-C
109 lines
3.6 KiB
Objective-C
#import "MMStorageTelemetryProvider.h"
|
|
|
|
@implementation MMStorageTelemetryProvider
|
|
|
|
- (MMTelemetryDomain)domain {
|
|
return MMTelemetryDomainStorage;
|
|
}
|
|
|
|
- (NSString *)providerIdentifier {
|
|
return @"com.i3omb.macmonitor.telemetry.storage";
|
|
}
|
|
|
|
- (BOOL)isAvailable {
|
|
return YES;
|
|
}
|
|
|
|
+ (NSArray<NSDictionary<NSString *, id> *> *)parseStatFSBuffer:(const struct statfs *)buffer count:(int)count {
|
|
if (!buffer || count <= 0) return @[];
|
|
|
|
NSMutableArray<NSDictionary<NSString *, id> *> *volumes = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
const struct statfs *mnt = &buffer[i];
|
|
|
|
// Only include local mounts
|
|
if (!(mnt->f_flags & MNT_LOCAL)) {
|
|
continue;
|
|
}
|
|
|
|
NSString *mountPoint = [NSString stringWithUTF8String:mnt->f_mntonname];
|
|
NSString *deviceName = [NSString stringWithUTF8String:mnt->f_mntfromname];
|
|
NSString *fsType = [NSString stringWithUTF8String:mnt->f_fstypename];
|
|
|
|
// Exclude virtual/internal devfs, autodefs, and synthetic system roots
|
|
if ([fsType isEqualToString:@"devfs"] ||
|
|
[fsType isEqualToString:@"autofs"] ||
|
|
[mountPoint hasPrefix:@"/System/Volumes/Data/"] ||
|
|
[deviceName containsString:@"com.apple.os.update"]) {
|
|
continue;
|
|
}
|
|
|
|
uint64_t blockSize = (uint64_t)mnt->f_bsize;
|
|
uint64_t totalBytes = (uint64_t)mnt->f_blocks * blockSize;
|
|
uint64_t freeBytes = (uint64_t)mnt->f_bfree * blockSize;
|
|
uint64_t availBytes = (uint64_t)mnt->f_bavail * blockSize;
|
|
uint64_t usedBytes = (totalBytes >= freeBytes) ? (totalBytes - freeBytes) : 0;
|
|
|
|
if (totalBytes == 0) continue;
|
|
|
|
double usedPercent = ((double)usedBytes / (double)totalBytes) * 100.0;
|
|
if (usedPercent < 0.0) usedPercent = 0.0;
|
|
if (usedPercent > 100.0) usedPercent = 100.0;
|
|
|
|
NSString *volumeName = [mountPoint lastPathComponent];
|
|
if ([mountPoint isEqualToString:@"/"]) {
|
|
volumeName = @"Macintosh HD";
|
|
}
|
|
|
|
BOOL isReadOnly = (mnt->f_flags & MNT_RDONLY) != 0;
|
|
|
|
[volumes addObject:@{
|
|
@"volumeName": volumeName,
|
|
@"mountPoint": mountPoint,
|
|
@"devicePath": deviceName,
|
|
@"fsType": fsType,
|
|
@"totalBytes": @(totalBytes),
|
|
@"usedBytes": @(usedBytes),
|
|
@"freeBytes": @(freeBytes),
|
|
@"availableBytes": @(availBytes),
|
|
@"usedPercent": @(usedPercent),
|
|
@"isReadOnly": @(isReadOnly)
|
|
}];
|
|
}
|
|
|
|
return [volumes copy];
|
|
}
|
|
|
|
- (nullable NSDictionary<NSString *, id> *)sampleTelemetryWithError:(NSError **)error {
|
|
struct statfs *mntbuf = NULL;
|
|
int count = getmntinfo(&mntbuf, MNT_WAIT);
|
|
|
|
if (count <= 0 || mntbuf == NULL) {
|
|
if (error) {
|
|
*error = [NSError errorWithDomain:@"com.i3omb.MacMonitor.Storage"
|
|
code:-1
|
|
userInfo:@{NSLocalizedDescriptionKey: @"getmntinfo returned no mounted filesystems"}];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
NSArray *volumes = [MMStorageTelemetryProvider parseStatFSBuffer:mntbuf count:count];
|
|
|
|
uint64_t totalStorage = 0;
|
|
uint64_t totalUsed = 0;
|
|
for (NSDictionary *v in volumes) {
|
|
totalStorage += [v[@"totalBytes"] unsignedLongLongValue];
|
|
totalUsed += [v[@"usedBytes"] unsignedLongLongValue];
|
|
}
|
|
|
|
return @{
|
|
@"volumes": volumes,
|
|
@"volumeCount": @(volumes.count),
|
|
@"aggregateTotalBytes": @(totalStorage),
|
|
@"aggregateUsedBytes": @(totalUsed)
|
|
};
|
|
}
|
|
|
|
@end
|