Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File monitoring using Grand Central Dispatch

I'm using the code example by David Hamrick to monitor a file using GCD.

int fildes = open("/path/to/config.plist", O_RDONLY);

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fildes, 
                                                  DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
                                                  queue);
dispatch_source_set_event_handler(source, ^
{
    //Reload the config file
});
dispatch_source_set_cancel_handler(source, ^
{
    //Handle the cancel
});
dispatch_resume(source);

I want to use to monitor a change of a plist. I get a notification after the first change but not for the following changes. Why?

like image 726
Matthieu Riegler Avatar asked Jul 06 '12 02:07

Matthieu Riegler


2 Answers

You can indeed just re-open the file and re-register a source (deleting the previous one) when DISPATCH_VNODE_DELETE is received. Or you can use a call which was devised for just this kind of scenario, namely dispatch_io_create_with_path() - that will not only watch by path, it will open the file for you and let you read the contents asynchronously.

Since you asked (not sure which technique you asked for, but here's the simplest) here's a stand-alone code sample:

#include <dispatch/dispatch.h>
#include <stdio.h>

int main(int ac, char *av[])
{
  int fdes = open("/tmp/pleasewatchthis", O_RDONLY);
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  void (^eventHandler)(void), (^cancelHandler)(void);
  unsigned long mask = DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE;
  __block dispatch_source_t source;

  eventHandler = ^{
    unsigned long l = dispatch_source_get_data(source);
    if (l & DISPATCH_VNODE_DELETE) {
      printf("watched file deleted!  cancelling source\n");
      dispatch_source_cancel(source);
    }
    else {
      // handle the file has data case
      printf("watched file has data\n");
    }
  };
  cancelHandler = ^{
    int fdes = dispatch_source_get_handle(source);
    close(fdes);
    // Wait for new file to exist.
    while ((fdes = open("/tmp/pleasewatchthis", O_RDONLY)) == -1)
      sleep(1);
    printf("re-opened target file in cancel handler\n");
    source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdes, mask, queue);
    dispatch_source_set_event_handler(source, eventHandler);
    dispatch_source_set_cancel_handler(source, cancelHandler);
    dispatch_resume(source);
  };

  source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fdes, mask, queue);
  dispatch_source_set_event_handler(source, eventHandler);
  dispatch_source_set_cancel_handler(source, cancelHandler);
  dispatch_resume(source);
  dispatch_main();
}
like image 68
jkh Avatar answered Sep 24 '22 13:09

jkh


After a little bit a research I found out :

=> I was getting the flag DISPATCH_VNODE_DELETE

I was monitoring ~/Library/Preferences/com.apple.dock.plist As I found out, changing the dock orientation, deletes the original file and replaces it with a new one.

Therefore, the monitoring stopped.

The same author proposes a solution where in case of deletion, the GCD block call itself to continue the monitoring.

like image 22
Matthieu Riegler Avatar answered Sep 22 '22 13:09

Matthieu Riegler