Login Register






Thread Rating:
  • 0 Vote(s) - 0 Average


Tutorial Watching file system events with inotify filter_list
Author
Message
Watching file system events with inotify #1
If you're trying to write event-driven code that fires on certain file system events (e.g. file access, modification, creation, deletion) for projects like auto-build systems, event logs, or weird shit like this (which I should finish eventually), inotify is the tool for the job. If you're running any derivative of Unix (e.g. Linux or OSX) the inotify library is almost definitely on your system given it's part of glibc, so there's virtually no need to check for it.

To start, we need to include some headers and define some constants.
Code:
// io & file functions
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
// system limits (e.g. file name length)
#include <limits.h>
// type aliases & inotify
#include <sys/types.h>
#include <sys/inotify.h>

// maximum number of concurrent events
#define MAX_EVENTS 256
// size of one inotify event
#define E_SIZE (sizeof (struct inotify_event))
// event buffer size; (size of event + max path length) * event max
#define BUF_LENGTH MAX_EVENTS*(E_SIZE+PATH_MAX)
These headers provide all the functionality we need for the program.

Next we need to create our buffer and initialize inotify, checking for errors returned by the function.
Code:
int main(int argc,char **argv){
    /* allocate memory for the size of inotify_event + the
       maximum path length, MAX_EVENTS times */
    char buf[BUF_LENGTH];
    // file/watch descriptors
    int fd,wd;

    // initialize inotify, check for and reporting errors
    if((fd=inotify_init())<0)
        perror("Couldn't initialize inotify");
}

After initializing, we can start watching directories using the inotify_add_watch() function. In our case, we'll make a function and call it using the first argument passed to our program (it's argv[1], as argv[0] is the program name). The mask in the third argument represents some of many event flags offered by the library, all of which you can find here.
As well, we need to call some cleanup functions at the end of main(), before we return.
Code:
int add_watch_dir(int fd,char *dirname,uint32_t mask){
    int wd;

    /* add directory watch to the inotify file descriptor and
       check for errors */
    if((wd=inotify_add_watch(fd,dirname,mask))==-1)
        perror("Failed to watch directory");
    else
        printf("Watching directory %s\n",dirname);

    return wd;
}

int main(int argc,char **argv){
    ...
    // check that a directory argument was supplied
    if(argc<2){
        fprintf(stderr,"No watch directory supplied\n");
        return 1;
    }

    // call our new function, listening for create, modify, and delete events
    wd=add_watch_dir(fd,argv[1],IN_CREATE|IN_MODIFY|IN_DELETE);

    // rest of the program goes here

    inotify_rm_watch(fd,wd);
    close(fd);
    return 0;
}

Next, we can finally start getting event reports. We need to create an infinite loop to constantly check, two variables to hold the current size of the buffer and the current event index in the given iteration, and a final variable to hold the event itself.
After checking errors at the start of the loop, we can go through the available events, check their masks, and take whatever action we see fit from there. I choose to make this a separate function to keep things organized, but you can cram it into main if you'd like (but that doesn't mean you should Tongue).
Code:
// macro for reporting file or directory
#define target_ftype(e) (e->mask&IN_ISDIR?"directory":"file")

void handle_event(int fd,char *buf){
    size_t idx=0,length=read(fd,buf,BUF_LENGTH); // buffer index and read length
    struct inotify_event *event; // pointer to event

    // report read() errors
    if(length<0) perror("read");

    // while the index is under the length read from fd
    while(idx<length){
        event=(struct inotify_event*) &buf[idx]; // get event at current index
        if(!event->len) continue; // skip if the event is zero-length

        if(event->mask&IN_CREATE) // check for create flag in mask
            printf("%s %s created\n",target_ftype(event),event->name);
        else if(event->mask&IN_MODIFY) // check for modify flag
            printf("%s %s modified\n",target_ftype(event),event->name);
        else if(event->mask&IN_DELETE) // check for delete flag
            printf("%s %s deleted\n",target_ftype(event),event->name);

        // add event size to idx
        idx+=E_SIZE+event->len;
    }
}

int main(int argc,char **argv){
    ...
    while(1) handle_event(fd,buf);

    inotify_rm_watch(fd,wd);
    close(fd);
    return 0;
}

This serves as a working example (see this gist for the code), but we can go one better by recursing subdirectories (thread coming soon).
(This post was last modified: 08-28-2017, 04:32 PM by Inori.)
It's often the outcasts, the iconoclasts ... those who have the least to lose because they
don't have much in the first place, who feel the new currents and ride them the farthest.

Reply

RE: Watching file system events with inotify #2
This could come in handy. I was relying on the inotify bin to update my media library when using minidlna for my recently added folder. I might just have to implement this instead.

Reply







Users browsing this thread: 1 Guest(s)