tags:

views:

376

answers:

3

Hi All,

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  A: 

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);
Yuji
HI Thanks for the answer. But I am more looking for a solution with FSEVENTS because what really bothers me is that even if I give root path "/" or any subdirectories and then delete or rename it I am not getting the proper event flags in the call back. I have pasted code in the question.One other question, where do I get the full reference of Disk Arbiteration because I do not know which keys I should be looking or watching etc. Or inshort, where do you fins teh explanation of kDADiskDescriptionVolumePathKey or kDADiskDescriptionWatchVolumeName.
sbabu
The keys can be found at http://developer.apple.com/mac/library/documentation/Darwin/Reference/DiscArbitrationFramework/index.html ... Well, you know that there's a search box in the ADC website, right? Did you search the constant there? Let me think about your first question.
Yuji
Yuji
thanks a lot for your answers. I have added call back function as well. i am using 10.5.8 could that be the reason?
sbabu
Just to make sure: you know that `kFSEventStreamEventFlagRootChanged` is just `0x20`, right? The flags don't come in the string representation.
Yuji
yea... I checked all event flags.. but I always get 0
sbabu
OK, I booted into 10.5.8 and checked that it works. What I did is to watch `/tmp/boo`, and then `mv /tmp/boo /tmp/bar`. FSEvents correctly gave me the event flag `0x20`. What exactly did you do?
Yuji
I am currently not at my workplace. I will try and let you know the results.Thanks.
sbabu
+1  A: 

I'm not sure what's wrong there because I've never used FSEvents. Having said that, it sounds like what you're trying to do might be better done using DiskArbitration framework.

Ken Aspeslagh
Specifically, `DARegisterDiskDescriptionChangedCallback` with `kDADiskDescriptionWatchVolumeName`.
Peter Hosey
A: 

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.

Scott S