libinotifytools

inotifytools.c

00001 // kate: replace-tabs off; space-indent off;
00002 
00015 #include "../../config.h"
00016 #include "inotifytools/inotifytools.h"
00017 #include "inotifytools_p.h"
00018 
00019 #include <string.h>
00020 #include <strings.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <errno.h>
00024 #include <sys/select.h>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/ioctl.h>
00028 #include <unistd.h>
00029 #include <dirent.h>
00030 #include <time.h>
00031 #include <regex.h>
00032 #include <setjmp.h>
00033 
00034 #include "inotifytools/inotify.h"
00035 
00122 #define MAX_EVENTS 4096
00123 #define MAX_STRLEN 4096
00124 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00125 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00126 #define QUEUE_SIZE_PATH   INOTIFY_PROCDIR "max_queued_watches"
00127 #define INSTANCES_PATH    INOTIFY_PROCDIR "max_user_instances"
00128 
00129 static int inotify_fd;
00130 static unsigned  num_access;
00131 static unsigned  num_modify;
00132 static unsigned  num_attrib;
00133 static unsigned  num_close_nowrite;
00134 static unsigned  num_close_write;
00135 static unsigned  num_open;
00136 static unsigned  num_move_self;
00137 static unsigned  num_moved_to;
00138 static unsigned  num_moved_from;
00139 static unsigned  num_create;
00140 static unsigned  num_delete;
00141 static unsigned  num_delete_self;
00142 static unsigned  num_unmount;
00143 static unsigned  num_total;
00144 static int collect_stats = 0;
00145 
00146 struct rbtree *tree_wd = 0;
00147 struct rbtree *tree_filename = 0;
00148 static int error = 0;
00149 static int init = 0;
00150 static char* timefmt = 0;
00151 static regex_t* regex = 0;
00152 
00153 int isdir( char const * path );
00154 void record_stats( struct inotify_event const * event );
00155 int onestr_to_event(char const * event);
00156 
00174 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00175                                           #cond, mesg)
00176 
00177 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00178 
00196 void _niceassert( long cond, int line, char const * file, char const * condstr,
00197                   char const * mesg ) {
00198         if ( cond ) return;
00199 
00200         if ( mesg ) {
00201                 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00202                         condstr, mesg );
00203         }
00204         else {
00205                 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00206         }
00207 }
00208 
00218 char * chrtostr(char ch) {
00219         static char str[2] = { '\0', '\0' };
00220         str[0] = ch;
00221         return str;
00222 }
00223 
00227 int read_num_from_file( char * filename, int * num ) {
00228         FILE * file = fopen( filename, "r" );
00229         if ( !file ) {
00230                 error = errno;
00231                 return 0;
00232         }
00233 
00234         if ( EOF == fscanf( file, "%d", num ) ) {
00235                 error = errno;
00236                 return 0;
00237         }
00238 
00239         niceassert( 0 == fclose( file ), 0 );
00240 
00241         return 1;
00242 }
00243 
00244 int wd_compare(const void *d1, const void *d2, const void *config) {
00245         if (!d1 || !d2) return d1 - d2;
00246         return ((watch*)d1)->wd - ((watch*)d2)->wd;
00247 }
00248 
00249 int filename_compare(const void *d1, const void *d2, const void *config) {
00250         if (!d1 || !d2) return d1 - d2;
00251         return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
00252 }
00253 
00257 watch *watch_from_wd( int wd ) {
00258         watch w;
00259         w.wd = wd;
00260         return (watch*)rbfind(&w, tree_wd);
00261 }
00262 
00266 watch *watch_from_filename( char const *filename ) {
00267         watch w;
00268         w.filename = (char*)filename;
00269         return (watch*)rbfind(&w, tree_filename);
00270 }
00271 
00281 int inotifytools_initialize() {
00282         if (init) return 1;
00283 
00284         error = 0;
00285         // Try to initialise inotify
00286         inotify_fd = inotify_init();
00287         if (inotify_fd < 0)     {
00288                 error = inotify_fd;
00289                 return 0;
00290         }
00291 
00292         collect_stats = 0;
00293         init = 1;
00294         tree_wd = rbinit(wd_compare, 0);
00295         tree_filename = rbinit(filename_compare, 0);
00296         timefmt = 0;
00297 
00298         return 1;
00299 }
00300 
00304 void destroy_watch(watch *w) {
00305         if (w->filename) free(w->filename);
00306         free(w);
00307 }
00308 
00312 void cleanup_tree(const void *nodep,
00313                  const VISIT which,
00314                  const int depth, void* arg) {
00315         if (which != endorder && which != leaf) return;
00316         watch *w = (watch*)nodep;
00317         destroy_watch(w);
00318 }
00319 
00326 void inotifytools_cleanup() {
00327         if (!init) return;
00328 
00329         init = 0;
00330         close(inotify_fd);
00331         collect_stats = 0;
00332         error = 0;
00333         timefmt = 0;
00334 
00335         if (regex) {
00336                 regfree(regex);
00337                 free(regex);
00338                 regex = 0;
00339         }
00340 
00341         rbwalk(tree_wd, cleanup_tree, 0);
00342         rbdestroy(tree_wd); tree_wd = 0;
00343         rbdestroy(tree_filename); tree_filename = 0;
00344 }
00345 
00349 void empty_stats(const void *nodep,
00350                  const VISIT which,
00351                  const int depth, void *arg) {
00352     if (which != endorder && which != leaf) return;
00353         watch *w = (watch*)nodep;
00354         w->hit_access = 0;
00355         w->hit_modify = 0;
00356         w->hit_attrib = 0;
00357         w->hit_close_nowrite = 0;
00358         w->hit_close_write = 0;
00359         w->hit_open = 0;
00360         w->hit_move_self = 0;
00361         w->hit_moved_from = 0;
00362         w->hit_moved_to = 0;
00363         w->hit_create = 0;
00364         w->hit_delete = 0;
00365         w->hit_delete_self = 0;
00366         w->hit_unmount = 0;
00367         w->hit_total = 0;
00368 }
00369 
00373 void replace_filename(const void *nodep,
00374                       const VISIT which,
00375                       const int depth, void *arg) {
00376     if (which != endorder && which != leaf) return;
00377         watch *w = (watch*)nodep;
00378         char *old_name = ((char**)arg)[0];
00379         char *new_name = ((char**)arg)[1];
00380         int old_len = *((int*)&((char**)arg)[2]);
00381         char *name;
00382         if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
00383                 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
00384                 if (!strcmp( w->filename, new_name )) {
00385                         free(name);
00386                 } else {
00387                         rbdelete(w, tree_filename);
00388                         free( w->filename );
00389                         w->filename = name;
00390                         rbsearch(w, tree_filename);
00391                 }
00392         }
00393 }
00394 
00398 void get_num(const void *nodep,
00399              const VISIT which,
00400              const int depth, void *arg) {
00401     if (which != endorder && which != leaf) return;
00402         ++(*((int*)arg));
00403 }
00404 
00405 
00418 void inotifytools_initialize_stats() {
00419         niceassert( init, "inotifytools_initialize not called yet" );
00420 
00421         // if already collecting stats, reset stats
00422         if (collect_stats) {
00423                 rbwalk(tree_wd, empty_stats, 0);
00424         }
00425 
00426         num_access = 0;
00427         num_modify = 0;
00428         num_attrib = 0;
00429         num_close_nowrite = 0;
00430         num_close_write = 0;
00431         num_open = 0;
00432         num_move_self = 0;
00433         num_moved_from = 0;
00434         num_moved_to = 0;
00435         num_create = 0;
00436         num_delete = 0;
00437         num_delete_self = 0;
00438         num_unmount = 0;
00439         num_total = 0;
00440 
00441         collect_stats = 1;
00442 }
00443 
00471 int inotifytools_str_to_event_sep(char const * event, char sep) {
00472         if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00473                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00474                 return -1;
00475         }
00476 
00477         int ret, ret1, len;
00478         char * event1, * event2;
00479         char eventstr[4096];
00480         ret = 0;
00481 
00482         if ( !event || !event[0] ) return 0;
00483 
00484         event1 = (char *)event;
00485         event2 = strchr( event1, sep );
00486         while ( event1 && event1[0] ) {
00487                 if ( event2 ) {
00488                         len = event2 - event1;
00489                         niceassert( len < 4096, "malformed event string (very long)" );
00490                 }
00491                 else {
00492                         len = strlen(event1);
00493                 }
00494                 if ( len > 4095 ) len = 4095;
00495                 strncpy( eventstr, event1, len );
00496                 eventstr[len] = 0;
00497 
00498                 ret1 = onestr_to_event( eventstr );
00499                 if ( 0 == ret1 || -1 == ret1 ) {
00500                         ret = ret1;
00501                         break;
00502                 }
00503                 ret |= ret1;
00504 
00505                 event1 = event2;
00506                 if ( event1 && event1[0] ) {
00507                         // jump over 'sep' character
00508                         ++event1;
00509                         // if last character was 'sep'...
00510                         if ( !event1[0] ) return 0;
00511                         event2 = strchr( event1, sep );
00512                 }
00513         }
00514 
00515         return ret;
00516 }
00517 
00541 int inotifytools_str_to_event(char const * event) {
00542         return inotifytools_str_to_event_sep( event, ',' );
00543 }
00544 
00556 int onestr_to_event(char const * event)
00557 {
00558         static int ret;
00559         ret = -1;
00560 
00561         if ( !event || !event[0] )
00562                 ret = 0;
00563         else if ( 0 == strcasecmp(event, "ACCESS") )
00564                 ret = IN_ACCESS;
00565         else if ( 0 == strcasecmp(event, "MODIFY") )
00566                 ret = IN_MODIFY;
00567         else if ( 0 == strcasecmp(event, "ATTRIB") )
00568                 ret = IN_ATTRIB;
00569         else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00570                 ret = IN_CLOSE_WRITE;
00571         else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00572                 ret = IN_CLOSE_NOWRITE;
00573         else if ( 0 == strcasecmp(event, "OPEN") )
00574                 ret = IN_OPEN;
00575         else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00576                 ret = IN_MOVED_FROM;
00577         else if ( 0 == strcasecmp(event, "MOVED_TO") )
00578                 ret = IN_MOVED_TO;
00579         else if ( 0 == strcasecmp(event, "CREATE") )
00580                 ret = IN_CREATE;
00581         else if ( 0 == strcasecmp(event, "DELETE") )
00582                 ret = IN_DELETE;
00583         else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00584                 ret = IN_DELETE_SELF;
00585         else if ( 0 == strcasecmp(event, "UNMOUNT") )
00586                 ret = IN_UNMOUNT;
00587         else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00588                 ret = IN_Q_OVERFLOW;
00589         else if ( 0 == strcasecmp(event, "IGNORED") )
00590                 ret = IN_IGNORED;
00591         else if ( 0 == strcasecmp(event, "CLOSE") )
00592                 ret = IN_CLOSE;
00593         else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00594                 ret = IN_MOVE_SELF;
00595         else if ( 0 == strcasecmp(event, "MOVE") )
00596                 ret = IN_MOVE;
00597         else if ( 0 == strcasecmp(event, "ISDIR") )
00598                 ret = IN_ISDIR;
00599         else if ( 0 == strcasecmp(event, "ONESHOT") )
00600                 ret = IN_ONESHOT;
00601         else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00602                 ret = IN_ALL_EVENTS;
00603 
00604         return ret;
00605 }
00606 
00628 char * inotifytools_event_to_str(int events) {
00629         return inotifytools_event_to_str_sep(events, ',');
00630 }
00631 
00656 char * inotifytools_event_to_str_sep(int events, char sep)
00657 {
00658         static char ret[1024];
00659         ret[0] = '\0';
00660         ret[1] = '\0';
00661 
00662         if ( IN_ACCESS & events ) {
00663                 strcat( ret, chrtostr(sep) );
00664                 strcat( ret, "ACCESS" );
00665         }
00666         if ( IN_MODIFY & events ) {
00667                 strcat( ret, chrtostr(sep) );
00668                 strcat( ret, "MODIFY" );
00669         }
00670         if ( IN_ATTRIB & events ) {
00671                 strcat( ret, chrtostr(sep) );
00672                 strcat( ret, "ATTRIB" );
00673         }
00674         if ( IN_CLOSE_WRITE & events ) {
00675                 strcat( ret, chrtostr(sep) );
00676                 strcat( ret, "CLOSE_WRITE" );
00677         }
00678         if ( IN_CLOSE_NOWRITE & events ) {
00679                 strcat( ret, chrtostr(sep) );
00680                 strcat( ret, "CLOSE_NOWRITE" );
00681         }
00682         if ( IN_OPEN & events ) {
00683                 strcat( ret, chrtostr(sep) );
00684                 strcat( ret, "OPEN" );
00685         }
00686         if ( IN_MOVED_FROM & events ) {
00687                 strcat( ret, chrtostr(sep) );
00688                 strcat( ret, "MOVED_FROM" );
00689         }
00690         if ( IN_MOVED_TO & events ) {
00691                 strcat( ret, chrtostr(sep) );
00692                 strcat( ret, "MOVED_TO" );
00693         }
00694         if ( IN_CREATE & events ) {
00695                 strcat( ret, chrtostr(sep) );
00696                 strcat( ret, "CREATE" );
00697         }
00698         if ( IN_DELETE & events ) {
00699                 strcat( ret, chrtostr(sep) );
00700                 strcat( ret, "DELETE" );
00701         }
00702         if ( IN_DELETE_SELF & events ) {
00703                 strcat( ret, chrtostr(sep) );
00704                 strcat( ret, "DELETE_SELF" );
00705         }
00706         if ( IN_UNMOUNT & events ) {
00707                 strcat( ret, chrtostr(sep) );
00708                 strcat( ret, "UNMOUNT" );
00709         }
00710         if ( IN_Q_OVERFLOW & events ) {
00711                 strcat( ret, chrtostr(sep) );
00712                 strcat( ret, "Q_OVERFLOW" );
00713         }
00714         if ( IN_IGNORED & events ) {
00715                 strcat( ret, chrtostr(sep) );
00716                 strcat( ret, "IGNORED" );
00717         }
00718         if ( IN_CLOSE & events ) {
00719                 strcat( ret, chrtostr(sep) );
00720                 strcat( ret, "CLOSE" );
00721         }
00722         if ( IN_MOVE_SELF & events ) {
00723                 strcat( ret, chrtostr(sep) );
00724                 strcat( ret, "MOVE_SELF" );
00725         }
00726         if ( IN_ISDIR & events ) {
00727                 strcat( ret, chrtostr(sep) );
00728                 strcat( ret, "ISDIR" );
00729         }
00730         if ( IN_ONESHOT & events ) {
00731                 strcat( ret, chrtostr(sep) );
00732                 strcat( ret, "ONESHOT" );
00733         }
00734 
00735         // Maybe we didn't match any... ?
00736         if (ret[0] == '\0') {
00737                 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00738         }
00739 
00740         return &ret[1];
00741 }
00742 
00763 char * inotifytools_filename_from_wd( int wd ) {
00764         niceassert( init, "inotifytools_initialize not called yet" );
00765         watch *w = watch_from_wd(wd);
00766         if (!w)
00767         return NULL;
00768 
00769         return w->filename;
00770 }
00771 
00786 int inotifytools_wd_from_filename( char const * filename ) {
00787         niceassert( init, "inotifytools_initialize not called yet" );
00788         watch *w = watch_from_filename(filename);
00789         if (!w) return -1;
00790         return w->wd;
00791 }
00792 
00807 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00808         niceassert( init, "inotifytools_initialize not called yet" );
00809         watch *w = watch_from_wd(wd);
00810         if (!w) return;
00811         if (w->filename) free(w->filename);
00812         w->filename = strdup(filename);
00813 }
00814 
00829 void inotifytools_set_filename_by_filename( char const * oldname,
00830                                             char const * newname ) {
00831         watch *w = watch_from_filename(oldname);
00832         if (!w) return;
00833         if (w->filename) free(w->filename);
00834         w->filename = strdup(newname);
00835 }
00836 
00859 void inotifytools_replace_filename( char const * oldname,
00860                                     char const * newname ) {
00861         if ( !oldname || !newname ) return;
00862         char *names[2+sizeof(int)/sizeof(char*)];
00863         names[0] = (char*)oldname;
00864         names[1] = (char*)newname;
00865         *((int*)&names[2]) = strlen(oldname);
00866         rbwalk(tree_filename, replace_filename, (void*)names);
00867 }
00868 
00872 int remove_inotify_watch(watch *w) {
00873         error = 0;
00874         int status = inotify_rm_watch( inotify_fd, w->wd );
00875         if ( status < 0 ) {
00876                 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
00877                         strerror(status) );
00878                 error = status;
00879                 return 0;
00880         }
00881         return 1;
00882 }
00883 
00887 watch *create_watch(int wd, char *filename) {
00888         if ( wd <= 0 || !filename) return 0;
00889 
00890         watch *w = (watch*)calloc(1, sizeof(watch));
00891         w->wd = wd;
00892         w->filename = strdup(filename);
00893         rbsearch(w, tree_wd);
00894         rbsearch(w, tree_filename);
00895         return w;
00896 }
00897 
00910 int inotifytools_remove_watch_by_wd( int wd ) {
00911         niceassert( init, "inotifytools_initialize not called yet" );
00912         watch *w = watch_from_wd(wd);
00913         if (!w) return 1;
00914 
00915         if (!remove_inotify_watch(w)) return 0;
00916         rbdelete(w, tree_wd);
00917         rbdelete(w, tree_filename);
00918         destroy_watch(w);
00919         return 1;
00920 }
00921 
00933 int inotifytools_remove_watch_by_filename( char const * filename ) {
00934         niceassert( init, "inotifytools_initialize not called yet" );
00935         watch *w = watch_from_filename(filename);
00936         if (!w) return 1;
00937 
00938         if (!remove_inotify_watch(w)) return 0;
00939         rbdelete(w, tree_wd);
00940         rbdelete(w, tree_filename);
00941         destroy_watch(w);
00942         return 1;
00943 }
00944 
00956 int inotifytools_watch_file( char const * filename, int events ) {
00957         static char const * filenames[2];
00958         filenames[0] = filename;
00959         filenames[1] = NULL;
00960         return inotifytools_watch_files( filenames, events );
00961 }
00962 
00978 int inotifytools_watch_files( char const * filenames[], int events ) {
00979         niceassert( init, "inotifytools_initialize not called yet" );
00980         error = 0;
00981 
00982         static int i;
00983         for ( i = 0; filenames[i]; ++i ) {
00984                 static int wd;
00985                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00986                 if ( wd < 0 ) {
00987                         if ( wd == -1 ) {
00988                                 error = errno;
00989                                 return 0;
00990                         } // if ( wd == -1 )
00991                         else {
00992                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00993                                          "(expected -1 or >0 )", filenames[i], wd );
00994                                 // no appropriate value for error
00995                                 return 0;
00996                         } // else
00997                 } // if ( wd < 0 )
00998 
00999                 char *filename;
01000                 // Always end filename with / if it is a directory
01001                 if ( !isdir(filenames[i])
01002                      || filenames[i][strlen(filenames[i])-1] == '/') {
01003                         filename = strdup(filenames[i]);
01004                 }
01005                 else {
01006                         nasprintf( &filename, "%s/", filenames[i] );
01007                 }
01008                 create_watch(wd, filename);
01009                 free(filename);
01010         } // for
01011 
01012         return 1;
01013 }
01014 
01041 struct inotify_event * inotifytools_next_event( int timeout ) {
01042         return inotifytools_next_events( timeout, 1 );
01043 }
01044 
01045 
01095 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01096         niceassert( init, "inotifytools_initialize not called yet" );
01097         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01098 
01099         if ( num_events < 1 ) return NULL;
01100 
01101         static struct inotify_event event[MAX_EVENTS];
01102         static struct inotify_event * ret;
01103         static int first_byte = 0;
01104         static ssize_t bytes;
01105         static jmp_buf jmp;
01106         static char match_name[MAX_STRLEN];
01107 
01108 #define RETURN(A) {\
01109         if (regex) {\
01110                 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01111                 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01112                         longjmp(jmp,0);\
01113                 }\
01114         }\
01115         if ( collect_stats ) {\
01116                 record_stats( A );\
01117         }\
01118         return A;\
01119 }
01120 
01121         setjmp(jmp);
01122 
01123         error = 0;
01124 
01125         // first_byte is index into event buffer
01126         if ( first_byte != 0
01127           && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01128 
01129                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01130                 first_byte += sizeof(struct inotify_event) + ret->len;
01131 
01132                 // if the pointer to the next event exactly hits end of bytes read,
01133                 // that's good.  next time we're called, we'll read.
01134                 if ( first_byte == bytes ) {
01135                         first_byte = 0;
01136                 }
01137                 else if ( first_byte > bytes ) {
01138                         // oh... no.  this can't be happening.  An incomplete event.
01139                         // Copy what we currently have into first element, call self to
01140                         // read remainder.
01141                         // oh, and they BETTER NOT overlap.
01142                         // Boy I hope this code works.
01143                         // But I think this can never happen due to how inotify is written.
01144                         niceassert( (long)((char *)&event[0] +
01145                                     sizeof(struct inotify_event) +
01146                                     event[0].len) <= (long)ret,
01147                                     "extremely unlucky user, death imminent" );
01148                         // how much of the event do we have?
01149                         bytes = (char *)&event[0] + bytes - (char *)ret;
01150                         memcpy( &event[0], ret, bytes );
01151                         return inotifytools_next_events( timeout, num_events );
01152                 }
01153                 RETURN(ret);
01154 
01155         }
01156 
01157         else if ( first_byte == 0 ) {
01158                 bytes = 0;
01159         }
01160 
01161 
01162         static ssize_t this_bytes;
01163         static unsigned int bytes_to_read;
01164         static int rc;
01165         static fd_set read_fds;
01166 
01167         static struct timeval read_timeout;
01168         read_timeout.tv_sec = timeout;
01169         read_timeout.tv_usec = 0;
01170         static struct timeval * read_timeout_ptr;
01171         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01172 
01173         FD_ZERO(&read_fds);
01174         FD_SET(inotify_fd, &read_fds);
01175         rc = select(inotify_fd + 1, &read_fds,
01176                     NULL, NULL, read_timeout_ptr);
01177         if ( rc < 0 ) {
01178                 // error
01179                 error = errno;
01180                 return NULL;
01181         }
01182         else if ( rc == 0 ) {
01183                 // timeout
01184                 return NULL;
01185         }
01186 
01187         // wait until we have enough bytes to read
01188         do {
01189                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01190         } while ( !rc &&
01191                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01192 
01193         if ( rc == -1 ) {
01194                 error = errno;
01195                 return NULL;
01196         }
01197 
01198         this_bytes = read(inotify_fd, &event[0] + bytes,
01199                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01200         if ( this_bytes < 0 ) {
01201                 error = errno;
01202                 return NULL;
01203         }
01204         if ( this_bytes == 0 ) {
01205                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01206                                 "events occurred at once.\n");
01207                 return NULL;
01208         }
01209         bytes += this_bytes;
01210 
01211         ret = &event[0];
01212         first_byte = sizeof(struct inotify_event) + ret->len;
01213         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01214                                          "almost certainly screw up." );
01215         if ( first_byte == bytes ) {
01216                 first_byte = 0;
01217         }
01218 
01219         RETURN(ret);
01220 
01221 #undef RETURN
01222 }
01223 
01249 int inotifytools_watch_recursively( char const * path, int events ) {
01250         return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01251 }
01252 
01285 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01286                                                  char const ** exclude_list ) {
01287         niceassert( init, "inotifytools_initialize not called yet" );
01288 
01289         DIR * dir;
01290         char * my_path;
01291         error = 0;
01292         dir = opendir( path );
01293         if ( !dir ) {
01294                 // If not a directory, don't need to do anything special
01295                 if ( errno == ENOTDIR ) {
01296                         return inotifytools_watch_file( path, events );
01297                 }
01298                 else {
01299                         error = errno;
01300                         return 0;
01301                 }
01302         }
01303 
01304         if ( path[strlen(path)-1] != '/' ) {
01305                 nasprintf( &my_path, "%s/", path );
01306         }
01307         else {
01308                 my_path = (char *)path;
01309         }
01310 
01311         static struct dirent * ent;
01312         char * next_file;
01313         static struct stat64 my_stat;
01314         ent = readdir( dir );
01315         // Watch each directory within this directory
01316         while ( ent ) {
01317                 if ( (0 != strcmp( ent->d_name, "." )) &&
01318                      (0 != strcmp( ent->d_name, ".." )) ) {
01319                         nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01320                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01321                                 error = errno;
01322                                 free( next_file );
01323                                 if ( errno != EACCES ) {
01324                                         error = errno;
01325                                         if ( my_path != path ) free( my_path );
01326                                         closedir( dir );
01327                                         return 0;
01328                                 }
01329                         }
01330                         else if ( S_ISDIR( my_stat.st_mode ) &&
01331                                   !S_ISLNK( my_stat.st_mode )) {
01332                                 free( next_file );
01333                                 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01334                                 static unsigned int no_watch;
01335                                 static char const ** exclude_entry;
01336 
01337                                 no_watch = 0;
01338                                 for (exclude_entry = exclude_list;
01339                                          exclude_entry && *exclude_entry && !no_watch;
01340                                          ++exclude_entry) {
01341                                         static int exclude_length;
01342 
01343                                         exclude_length = strlen(*exclude_entry);
01344                                         if ((*exclude_entry)[exclude_length-1] == '/') {
01345                                                 --exclude_length;
01346                                         }
01347                                         if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01348                                             !strncmp(*exclude_entry, next_file, exclude_length)) {
01349                                                 // directory found in exclude list
01350                                                 no_watch = 1;
01351                                         }
01352                                 }
01353                                 if (!no_watch) {
01354                                         static int status;
01355                                         status = inotifytools_watch_recursively_with_exclude(
01356                                                       next_file,
01357                                                       events,
01358                                                       exclude_list );
01359                                         // For some errors, we will continue.
01360                                         if ( !status && (EACCES != error) && (ENOENT != error) &&
01361                                              (ELOOP != error) ) {
01362                                                 free( next_file );
01363                                                 if ( my_path != path ) free( my_path );
01364                                                 closedir( dir );
01365                                                 return 0;
01366                                         }
01367                                 } // if !no_watch
01368                                 free( next_file );
01369                         } // if isdir and not islnk
01370                         else {
01371                                 free( next_file );
01372                         }
01373                 }
01374                 ent = readdir( dir );
01375                 error = 0;
01376         }
01377 
01378         closedir( dir );
01379 
01380         int ret = inotifytools_watch_file( my_path, events );
01381         if ( my_path != path ) free( my_path );
01382         return ret;
01383 }
01384 
01388 void record_stats( struct inotify_event const * event ) {
01389         if (!event) return;
01390         watch *w = watch_from_wd(event->wd);
01391         if (!w) return;
01392         if ( IN_ACCESS & event->mask ) {
01393                 ++w->hit_access;
01394                 ++num_access;
01395         }
01396         if ( IN_MODIFY & event->mask ) {
01397                 ++w->hit_modify;
01398                 ++num_modify;
01399         }
01400         if ( IN_ATTRIB & event->mask ) {
01401                 ++w->hit_attrib;
01402                 ++num_attrib;
01403         }
01404         if ( IN_CLOSE_WRITE & event->mask ) {
01405                 ++w->hit_close_write;
01406                 ++num_close_write;
01407         }
01408         if ( IN_CLOSE_NOWRITE & event->mask ) {
01409                 ++w->hit_close_nowrite;
01410                 ++num_close_nowrite;
01411         }
01412         if ( IN_OPEN & event->mask ) {
01413                 ++w->hit_open;
01414                 ++num_open;
01415         }
01416         if ( IN_MOVED_FROM & event->mask ) {
01417                 ++w->hit_moved_from;
01418                 ++num_moved_from;
01419         }
01420         if ( IN_MOVED_TO & event->mask ) {
01421                 ++w->hit_moved_to;
01422                 ++num_moved_to;
01423         }
01424         if ( IN_CREATE & event->mask ) {
01425                 ++w->hit_create;
01426                 ++num_create;
01427         }
01428         if ( IN_DELETE & event->mask ) {
01429                 ++w->hit_delete;
01430                 ++num_delete;
01431         }
01432         if ( IN_DELETE_SELF & event->mask ) {
01433                 ++w->hit_delete_self;
01434                 ++num_delete_self;
01435         }
01436         if ( IN_UNMOUNT & event->mask ) {
01437                 ++w->hit_unmount;
01438                 ++num_unmount;
01439         }
01440         if ( IN_MOVE_SELF & event->mask ) {
01441                 ++w->hit_move_self;
01442                 ++num_move_self;
01443         }
01444 
01445         ++w->hit_total;
01446         ++num_total;
01447 
01448 }
01449 
01450 int *stat_ptr(watch *w, int event)
01451 {
01452         if ( IN_ACCESS == event )
01453                 return &w->hit_access;
01454         if ( IN_MODIFY == event )
01455                 return &w->hit_modify;
01456         if ( IN_ATTRIB == event )
01457                 return &w->hit_attrib;
01458         if ( IN_CLOSE_WRITE == event )
01459                 return &w->hit_close_write;
01460         if ( IN_CLOSE_NOWRITE == event )
01461                 return &w->hit_close_nowrite;
01462         if ( IN_OPEN == event )
01463                 return &w->hit_open;
01464         if ( IN_MOVED_FROM == event )
01465                 return &w->hit_moved_from;
01466         if ( IN_MOVED_TO == event )
01467                 return &w->hit_moved_to;
01468         if ( IN_CREATE == event )
01469                 return &w->hit_create;
01470         if ( IN_DELETE == event )
01471                 return &w->hit_delete;
01472         if ( IN_DELETE_SELF == event )
01473                 return &w->hit_delete_self;
01474         if ( IN_UNMOUNT == event )
01475                 return &w->hit_unmount;
01476         if ( IN_MOVE_SELF == event )
01477                 return &w->hit_move_self;
01478         if ( 0 == event )
01479                 return &w->hit_total;
01480         return 0;
01481 }
01482 
01498 int inotifytools_get_stat_by_wd( int wd, int event ) {
01499         if (!collect_stats) return -1;
01500 
01501         watch *w = watch_from_wd(wd);
01502         if (!w) return -1;
01503         int *i = stat_ptr(w, event);
01504         if (!i) return -1;
01505         return *i;
01506 }
01507 
01521 int inotifytools_get_stat_total( int event ) {
01522         if (!collect_stats) return -1;
01523         if ( IN_ACCESS == event )
01524                 return num_access;
01525         if ( IN_MODIFY == event )
01526                 return num_modify;
01527         if ( IN_ATTRIB == event )
01528                 return num_attrib;
01529         if ( IN_CLOSE_WRITE == event )
01530                 return num_close_write;
01531         if ( IN_CLOSE_NOWRITE == event )
01532                 return num_close_nowrite;
01533         if ( IN_OPEN == event )
01534                 return num_open;
01535         if ( IN_MOVED_FROM == event )
01536                 return num_moved_from;
01537         if ( IN_MOVED_TO == event )
01538                 return num_moved_to;
01539         if ( IN_CREATE == event )
01540                 return num_create;
01541         if ( IN_DELETE == event )
01542                 return num_delete;
01543         if ( IN_DELETE_SELF == event )
01544                 return num_delete_self;
01545         if ( IN_UNMOUNT == event )
01546                 return num_unmount;
01547         if ( IN_MOVE_SELF == event )
01548                 return num_move_self;
01549 
01550         if ( 0 == event )
01551                 return num_total;
01552 
01553         return -1;
01554 }
01555 
01575 int inotifytools_get_stat_by_filename( char const * filename,
01576                                                 int event ) {
01577         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01578                filename ), event );
01579 }
01580 
01591 int inotifytools_error() {
01592         return error;
01593 }
01594 
01598 int isdir( char const * path ) {
01599         static struct stat64 my_stat;
01600 
01601         if ( -1 == lstat64( path, &my_stat ) ) {
01602                 if (errno == ENOENT) return 0;
01603                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01604                 return 0;
01605         }
01606 
01607         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01608 }
01609 
01610 
01617 int inotifytools_get_num_watches() {
01618         int ret = 0;
01619         rbwalk(tree_filename, get_num, (void*)&ret);
01620         return ret;
01621 }
01622 
01663 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01664         return inotifytools_fprintf( stdout, event, fmt );
01665 }
01666 
01708 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01709         static char out[MAX_STRLEN+1];
01710         static int ret;
01711         ret = inotifytools_sprintf( out, event, fmt );
01712         if ( -1 != ret ) fprintf( file, "%s", out );
01713         return ret;
01714 }
01715 
01766 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01767         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01768 }
01769 
01770 
01817 int inotifytools_snprintf( char * out, int size,
01818                            struct inotify_event* event, char* fmt ) {
01819         static char * filename, * eventname, * eventstr;
01820         static unsigned int i, ind;
01821         static char ch1;
01822         static char timestr[MAX_STRLEN];
01823         static time_t now;
01824 
01825 
01826         if ( event->len > 0 ) {
01827                 eventname = event->name;
01828         }
01829         else {
01830                 eventname = NULL;
01831         }
01832 
01833 
01834         filename = inotifytools_filename_from_wd( event->wd );
01835 
01836         if ( !fmt || 0 == strlen(fmt) ) {
01837                 error = EINVAL;
01838                 return -1;
01839         }
01840         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01841                 error = EMSGSIZE;
01842                 return -1;
01843         }
01844 
01845         ind = 0;
01846         for ( i = 0; i < strlen(fmt) &&
01847                      (int)ind < size - 1; ++i ) {
01848                 if ( fmt[i] != '%' ) {
01849                         out[ind++] = fmt[i];
01850                         continue;
01851                 }
01852 
01853                 if ( i == strlen(fmt) - 1 ) {
01854                         // last character is %, invalid
01855                         error = EINVAL;
01856                         return ind;
01857                 }
01858 
01859                 ch1 = fmt[i+1];
01860 
01861                 if ( ch1 == '%' ) {
01862                         out[ind++] = '%';
01863                         ++i;
01864                         continue;
01865                 }
01866 
01867                 if ( ch1 == 'w' ) {
01868                         if ( filename ) {
01869                                 strncpy( &out[ind], filename, size - ind );
01870                                 ind += strlen(filename);
01871                         }
01872                         ++i;
01873                         continue;
01874                 }
01875 
01876                 if ( ch1 == 'f' ) {
01877                         if ( eventname ) {
01878                                 strncpy( &out[ind], eventname, size - ind );
01879                                 ind += strlen(eventname);
01880                         }
01881                         ++i;
01882                         continue;
01883                 }
01884 
01885                 if ( ch1 == 'e' ) {
01886                         eventstr = inotifytools_event_to_str( event->mask );
01887                         strncpy( &out[ind], eventstr, size - ind );
01888                         ind += strlen(eventstr);
01889                         ++i;
01890                         continue;
01891                 }
01892 
01893                 if ( ch1 == 'T' ) {
01894 
01895                         if ( timefmt ) {
01896 
01897                                 now = time(0);
01898                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01899                                                     localtime( &now ) ) ) {
01900 
01901                                         // time format probably invalid
01902                                         error = EINVAL;
01903                                         return ind;
01904                                 }
01905                         }
01906                         else {
01907                                 timestr[0] = 0;
01908                         }
01909 
01910                         strncpy( &out[ind], timestr, size - ind );
01911                         ind += strlen(timestr);
01912                         ++i;
01913                         continue;
01914                 }
01915 
01916                 // Check if next char in fmt is e
01917                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01918                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01919                         strncpy( &out[ind], eventstr, size - ind );
01920                         ind += strlen(eventstr);
01921                         i += 2;
01922                         continue;
01923                 }
01924 
01925                 // OK, this wasn't a special format character, just output it as normal
01926                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01927                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01928                 ++i;
01929         }
01930         out[ind] = 0;
01931 
01932         return ind - 1;
01933 }
01934 
01944 void inotifytools_set_printf_timefmt( char * fmt ) {
01945         timefmt = fmt;
01946 }
01947 
01956 int inotifytools_get_max_queued_events() {
01957         int ret;
01958         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01959         return ret;
01960 }
01961 
01971 int inotifytools_get_max_user_instances() {
01972         int ret;
01973         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01974         return ret;
01975 }
01976 
01986 int inotifytools_get_max_user_watches() {
01987         int ret;
01988         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01989         return ret;
01990 }
01991 
02003 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
02004         if (!pattern) {
02005                 if (regex) {
02006                         regfree(regex);
02007                         free(regex);
02008                         regex = 0;
02009                 }
02010                 return 1;
02011         }
02012 
02013         if (regex) { regfree(regex); }
02014         else       { regex = (regex_t *)malloc(sizeof(regex_t)); }
02015 
02016         int ret = regcomp(regex, pattern, flags | REG_NOSUB);
02017         if (0 == ret) return 1;
02018 
02019         regfree(regex);
02020         free(regex);
02021         regex = 0;
02022         error = EINVAL;
02023         return 0;
02024 }
02025 
02026 int event_compare(const void *p1, const void *p2, const void *config)
02027 {
02028         if (!p1 || !p2) return p1 - p2;
02029         char asc = 1;
02030         int sort_event = (int)config;
02031         if (sort_event == -1) {
02032                 sort_event = 0;
02033                 asc = 0;
02034         } else if (sort_event < 0) {
02035                 sort_event = -sort_event;
02036                 asc = 0;
02037         }
02038         int *i1 = stat_ptr((watch*)p1, sort_event);
02039         int *i2 = stat_ptr((watch*)p2, sort_event);
02040         if (0 == *i1 - *i2) {
02041                 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02042         }
02043         if (asc)
02044                 return *i1 - *i2;
02045         else
02046                 return *i2 - *i1;
02047 }
02048 
02049 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02050 {
02051         struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02052         RBLIST *all = rbopenlist(tree_wd);
02053         void const *p = rbreadlist(all);
02054         while (p) {
02055                 void const *r = rbsearch(p, ret);
02056                 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02057                 p = rbreadlist(all);
02058         }
02059         rbcloselist(all);
02060         return ret;
02061 }