I'm using the FSEvents API to get notifications of changes in a local directory that I'm tracking.
Is it possible to get a notification that the watched directory has been moved to another location on disk, using FSEvents or anything else?
Update:
Here is the code I have so far, I'm now trying to use the kFSEventStreamCreateFlagWatchRoot flag with FSEventStreamCreate to get the root changed notification, so far without success.
- (void)registerForFileSystemNotifications {
NSString *watchedDirectoryPath = [[NSUserDefaults standardUserDefaults] valueForKey:kMyWatchedDirectoryPathKey];
self.watchedDirectoryFileDescriptor = open([watchedDirectoryPath cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
NSArray *paths = [NSArray arrayWithObject:watchedDirectoryPath];
void *appController = (void *)self;
FSEventStreamContext context = {0, appController, NULL, NULL, NULL};
FSEventStreamRef streamRef = FSEventStreamCreate(NULL,
&fsevents_callback,
&context,
(CFArrayRef) paths,
kFSEventStreamEventIdSinceNow,
(CFTimeInterval)2.0,
kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagWatchRoot);
FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(streamRef);
}
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numumberOfEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
MyAppController *appController = (MyAppController *)userData;
char *newPath = calloc(4096, sizeof(char));
int pathIntPointer = (int)newPath;
int length = fcntl(appController.watchedDirectoryFileDescriptor, F_GETPATH, pathIntPointer);
NSString *newPathString = [[NSString alloc] initWithBytes:newPath length:(NSUInteger)length encoding:NSUTF8StringEncoding];
NSLog(@"newPathString: %@", newPathString); // empty
}
Yes. Pass kFSEventStreamCreateFlagWatchRoot
as the last argument to FSEventStreamCreate
, and you'll be notified if the directory's moved or renamed. From the docs:
Request notifications of changes along the path to the path(s) you're watching. For example, with this flag, if you watch "/foo/bar" and it is renamed to "/foo/bar.old", you would receive a RootChanged event. The same is true if the directory "/foo" were renamed. The event you receive is a special event: the path for the event is the original path you specified, the flag kFSEventStreamEventFlagRootChanged is set and event ID is zero. RootChanged events are useful to indicate that you should rescan a particular hierarchy because it changed completely (as opposed to the things inside of it changing). If you want to track the current location of a directory, it is best to open the directory before creating the stream so that you have a file descriptor for it and can issue an F_GETPATH fcntl() to find the current path.
Edit: adding fcntl example
That cocoadev example suggests the author's a bit inexperienced with pointers. The pathIntPointer is not only unnecessary, it's also the cause of your problem. Error checking of the return code from fnctl would have caught it. Here's a revised version of your callback:
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numumberOfEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
MyAppController *appController = (MyAppController *)userData;
char newPath[ MAXPATHLEN ];
int rc;
rc = fcntl( appController.watchedDirectoryFileDescriptor, F_GETPATH, newPath );
if ( rc == -1 ) {
perror( "fnctl F_GETPATH" );
return;
}
NSString *newPathString = [[NSString alloc] initWithUTF8String: newPath ];
NSLog(@"newPathString: %@", newPathString);
[ newPathString release ];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With