Creating a file system watcher for Mac

Archives Forums/MacOS X Discussion/Creating a file system watcher for Mac

JoshK(Posted 2012) [#1]
I'm attempting to create a file system watcher like Windows has:
http://blitzmax.com/codearcs/codearcs.php?code=2747

I'm including a single .mm file in my BlitzMax project and wrapping the code up so my BlitzMax program can just call a few functions and detect file changes to a directory I am watching.
http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html

I wrote some code out to the best of my ability below, but I am completely confounded with Objective-C and the FSEventStream API:
#include <CoreServices/CoreServices.h>

FSEventStreamRef fsevenrstreamref;

void Callback(ConstFSEventStreamRef streamRef,void *clientCallBackInfo,size_t numEvents,void *eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[])
{
	printf("File change occured!\n");
}

void InitializeFSEventStream(std::string path)
{
	CFAllocatorRef allocator = NULL;
	FSEventStreamCallback callback = Callback;
	FSEventStreamContext *context = NULL;
	CFArrayRef pathsToWatch;//???
	FSEventStreamEventId sinceWhen = kFSEventStreamEventIdSinceNow;
	CFTimeInterval latency = 1;
	FSEventStreamCreateFlags flags = kFSEventStreamCreateFlagFileEvents;
	fsevenrstreamref = FSEventStreamCreate(allocator,callback,context,pathsToWatch,sinceWhen,latency,flags);
	
	CFRunLoopRef runLoop;//???
	CFStringRef runLoopMode;//???
	FSEventStreamScheduleWithRunLoop(fsevenrstreamref,runLoop,runLoopMode);
}

void UpdateFSEventStream()
{
	if (fsevenrstreamref.Start())
	{
		fsevenrstreamref.Stop()
	}
}


Are there any simple examples available that will do what I am trying to do? Thank you.


JoshK(Posted 2012) [#2]
I'm making some progress here:
https://devforums.apple.com/message/748539


JoshK(Posted 2012) [#3]
Here's a failed attempt that only watches a single file path:
http://www.leadwerks.com/werkspace/files/file/380-failed-attempt-at-a-filesystem-watcher-for-mac/


skidracer(Posted 2012) [#4]
A quick google on the subject found this alternative approach.

http://stackoverflow.com/questions/11556545/fsevents-c-example

Sticking with an objective c solution will help you become expert in OSX which is always a good thing of course.


JoshK(Posted 2012) [#5]
I ran the code from Xcode. Had to make one small change. It detects events but does not appear to be recursive:
//
//  main.cpp
//  FileWatch
//
//  Created by Josh Klint on 10/30/12.
//  Copyright (c) 2012 Josh Klint. All rights reserved.
//

#include <iostream>

#include <errno.h>       // for errno
#include <fcntl.h>       // for O_RDONLY
#include <stdio.h>       // for fprintf()
#include <stdlib.h>      // for EXIT_SUCCESS
#include <string.h>      // for strerror()
#include <sys/event.h>   // for kqueue() etc.
#include <unistd.h>      // for close()

int main (int argc, const char *argv[])
{
    int kq = kqueue ();
    
    char *dirname = "./";
    
    // dir name is in argv[1], NO checks for errors here
    int dirfd = open (dirname, O_RDONLY);
    
    struct kevent direvent;
    EV_SET (&direvent, dirfd, EVFILT_VNODE, EV_ADD | EV_CLEAR | EV_ENABLE,
            NOTE_WRITE, 0, (void *)dirname);
    
    kevent(kq, &direvent, 1, NULL, 0, NULL);
    
    // Register interest in SIGINT with the queue.  The user data
    // is NULL, which is how we'll differentiate between
    // a directory-modification event and a SIGINT-received event.
    struct kevent sigevent;
    EV_SET (&sigevent, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, NULL);
    // kqueue event handling happens after the legacy API, so make
    // sure it doesn eat the signal before the kqueue can see it.
    signal (SIGINT, SIG_IGN);
    
    // Register the signal event.
    kevent(kq, &sigevent, 1, NULL, 0, NULL);
    
    while (1) {
        // camp on kevent() until something interesting happens
        struct kevent change;
        if (kevent(kq, NULL, 0, &change, 1, NULL) == -1) { exit(1); }
        // The signal event has NULL in the user data.  Check for that first.
        if (change.udata == NULL) {
            break;
        } else {
            // udata is non-null, so it's the name of the directory
            printf ("%s\n", (char*)change.udata);
        }
    }
    close (kq);
    return 0;
}


Last edited 2012


JoshK(Posted 2012) [#6]
I figured it out. It will take extra work because the granularity of the Mac FSEvents system is not as fine as I would like, but I'm done with the Objective-C stuff.


JoshK(Posted 2012) [#7]
I finished it:
http://www.leadwerks.com/werkspace/blog/1/entry-1005-halloween-horrors-with-objective-c/