How to listen for file system changes MAC - kFSEventStreamCreateFlagWatchRoot

♀尐吖头ヾ 提交于 2019-12-31 22:25:52

问题


I am listening for Directory and disk changes in a Cocoa project using FSEvents. I need to get events when a root folder is renamed or deleted. So, I passed kFSEventStreamCreateFlagWatchRoot while creating the FSEventStream. But even if I delete or rename the root folder I am not getting corresponding FSEventStreamEventFlags. Any idea what could possibly be the issue. I am listening for changes in a USB mounted device. I used both FSEventStreamCreate and FSEventStreamCreateRelativeToDevice. One thing I notices is when I try with FSEventStreamCreate I get the following error message while creating FSEventStream:

(CarbonCore.framework) FSEventStreamCreate: watch_all_parents:
error trying to add kqueue for fd 7 (/Volumes/NO NAME; Operation not supported)

But with FSEventStreamCreateRelativeToDevice there are no errors but still not getting kFSEventStreamEventFlagRootChanged in event flags. Also, while creation using FSEventStreamCreateRelativeToDevice apple say's if I want to listen to root path changes pass emty string "". But I am not able to listen to root path changes by passing empty string. But when I pass "/" it works. But even for "/" I do not get any proper FSEventStreamEventFlags. I am pasting the code here:

-(void) subscribeFileSystemChanges:(NSString*) path
{
    PRINT_FUNCTION_BEGIN;

    // if already subscribed then unsubscribe
    if (stream)
    {
        FSEventStreamStop(stream);
        FSEventStreamInvalidate(stream); /* will remove from runloop */
        FSEventStreamRelease(stream);
    }

    FSEventStreamContext cntxt = {0};
    cntxt.info = self;

    CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void**)&path, 1, NULL);


    stream = FSEventStreamCreate(NULL, &feCallback, &cntxt, 
                                 pathsToWatch, kFSEventStreamEventIdSinceNow, 1,
                                 kFSEventStreamCreateFlagWatchRoot );


    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), 
                                     kCFRunLoopDefaultMode);

    FSEventStreamStart(stream);


}

Call back function:

static void feCallback(ConstFSEventStreamRef streamRef, void* pClientCallBackInfo, 
                       size_t numEvents, void* pEventPaths, const    FSEventStreamEventFlags eventFlags[], 
                       const FSEventStreamEventId eventIds[]) 

{
char** ppPaths = (char**)pEventPaths; int i;

    for (i = 0; i < numEvents; i++)
    {
        NSLog(@"Event Flags %lu Event Id %llu", eventFlags[i], eventIds[i]); 
        NSLog(@"Path changed: %@", 
              [NSString stringWithUTF8String:ppPaths[i]]); 
    }    
}

Thanks a lot in advance.


回答1:


I had the same problem and I think I figured it out. Apparently kFSEventStreamCreateFlagWatchRoot is just simply busted when using FSEventStreamCreateRelativeToDevice. You have to use FSEventStreamCreate. Since the former form is preferable if you're relying on historical event ids, you might need to create 2 streams. Also, note that it appears that you don't get kEventFlagChangedRoot sent to you if your app isn't running, so you'll need to stat the directory when you start up.




回答2:


I think the change of the volume name is not counted as a change in the file system reported by FSEvents. Remember, the volume name itself does not really exists as a file system entry. The ones under /Volumes is cooked up by the OS.

It's instead covered by Disk Arbitration.

A short sample code follows. First, define the callback

#import <DiskArbitration/DiskArbitration.h>
void callBack(DADiskRef disk,CFArrayRef keys,void *context )
{
    CFDictionaryRef dict=DADiskCopyDescription(disk);
    NSString*mountPoint=[(NSDictionary*)dict objectForKey:(NSString*)kDADiskDescriptionVolumePathKey];
    NSLog(@"disk at %@:",mountPoint);
    for(NSString*key in (NSArray*)keys){
    NSLog(@"key %@ changed: %@",key,[(NSDictionary*)dict objectForKey:key]);    
    }
    CFRelease(dict);
}

and then install the handler like this

DASessionRef session=DASessionCreate(NULL);
DARegisterDiskDescriptionChangedCallback(session, NULL, NULL, callBack, NULL);
DASessionScheduleWithRunLoop(session, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);


来源:https://stackoverflow.com/questions/2154845/how-to-listen-for-file-system-changes-mac-kfseventstreamcreateflagwatchroot

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!