2016-05-25

File monitoring on OS X using FSEvents

(sagittarius filewatch) on OS X is using kqueue (2) currently. Using kqueue isn't bad just having couple of limitation such as no capability of directory monitoring (due to my laziness). It is OK on BSD environment since this is the only choice to do it. However, on OS X, there are FSEvents APIs which allow users to monitor filesystem. When I research it, it doesn't require loads of file descriptors nor limit file/directory only monitoring. So I thought this might be a good underlying implementation for OS X and implemented like this.

If it ends without any problem, as usual, I don't write any blog post. Yes, there's a huge problem. It doesn't allow me to write tail emulator. I first thought my implementation has an issue. So I've written this small piece of code to check if it works as my expect.
#include <CoreServices/CoreServices.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

static void callback(ConstFSEventStreamRef stream,
                     void *callbackInfo,
                     size_t numEvents,
                     void *evPaths,
                     const FSEventStreamEventFlags evFlags[],
                     const FSEventStreamEventId evIds[])
{
  FILE *fp = (FILE *)callbackInfo;
  char buf[1024];
  const char **paths = (const char **)evPaths;
  for (int i = 0; i < numEvents; i++) {
    while (1) {
      int n = fread(buf, 1, sizeof(buf), fp);
      fwrite(buf, 1, n, stdout);
      if (feof(fp)) break;
    }
    fflush(stdout);
  }
}

int main(int argc, char **args)
{
  if (argc != 2) {
    fputs("fstail file", stderr);
    exit(-1);
  }
  FILE *fp = fopen(args[1], "r");
  fseek(fp, 0, SEEK_END);
  
  CFStringRef s = CFStringCreateWithCString(kCFAllocatorDefault, args[1],
                                            kCFStringEncodingUTF8);
  CFArrayRef ar = CFArrayCreate(NULL, (const void **)&s, 1, NULL);
  FSEventStreamContext ctx = {0, fp, NULL, NULL, NULL};
  int flags = kFSEventStreamCreateFlagFileEvents;
  FSEventStreamRef stream = FSEventStreamCreate(NULL, &callback, &ctx, ar,
                                                kFSEventStreamEventIdSinceNow,
                                                0, flags);
  FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),
                                   kCFRunLoopDefaultMode);
  FSEventStreamStart(stream);
  CFRunLoopRun();
  return 0;
}
This didn't work like tail command, unfortunately. It didn't receive any event after it got first event. If you get a technical problem, you probably search Google or Stack Overflow. Yes, I've found the very similar issue: No callback get called from FSEventStreamCreate with modifications created by self in watched file. It seems the question didn't get any useful answer. Feeling like I'm missing something, but I don't know what. I've also changed latency argument to non zero value but got no luck.

As long as this problem is not solved, I can't use FSEvents. So for now, I use kqueue, which works perfectly fine for my purpose, on OS X...

No comments:

Post a Comment