Files
MacMonitor/Sources/Telemetry/Storage/MMStorageTelemetryProvider.m
gronod 67bfa9f681
MacMonitor CI/CD Pipeline / Build & Test (Intel x86_64) (push) Canceled after 0s
feat(storage): implement MMStorageTelemetryProvider for mounted volumes and capacity (Issue #9)
2026-09-08 12:20:37 +01:00

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