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 }
00896 
00909 int inotifytools_remove_watch_by_wd( int wd ) {
00910         niceassert( init, "inotifytools_initialize not called yet" );
00911         watch *w = watch_from_wd(wd);
00912         if (!w) return 1;
00913 
00914         if (!remove_inotify_watch(w)) return 0;
00915         rbdelete(w, tree_wd);
00916         rbdelete(w, tree_filename);
00917         destroy_watch(w);
00918         return 1;
00919 }
00920 
00932 int inotifytools_remove_watch_by_filename( char const * filename ) {
00933         niceassert( init, "inotifytools_initialize not called yet" );
00934         watch *w = watch_from_filename(filename);
00935         if (!w) return 1;
00936 
00937         if (!remove_inotify_watch(w)) return 0;
00938         rbdelete(w, tree_wd);
00939         rbdelete(w, tree_filename);
00940         destroy_watch(w);
00941         return 1;
00942 }
00943 
00955 int inotifytools_watch_file( char const * filename, int events ) {
00956         static char const * filenames[2];
00957         filenames[0] = filename;
00958         filenames[1] = NULL;
00959         return inotifytools_watch_files( filenames, events );
00960 }
00961 
00977 int inotifytools_watch_files( char const * filenames[], int events ) {
00978         niceassert( init, "inotifytools_initialize not called yet" );
00979         error = 0;
00980 
00981         static int i;
00982         for ( i = 0; filenames[i]; ++i ) {
00983                 static int wd;
00984                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00985                 if ( wd < 0 ) {
00986                         if ( wd == -1 ) {
00987                                 error = errno;
00988                                 return 0;
00989                         } // if ( wd == -1 )
00990                         else {
00991                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00992                                          "(expected -1 or >0 )", filenames[i], wd );
00993                                 // no appropriate value for error
00994                                 return 0;
00995                         } // else
00996                 } // if ( wd < 0 )
00997 
00998                 char *filename;
00999                 // Always end filename with / if it is a directory
01000                 if ( !isdir(filenames[i])
01001                      || filenames[i][strlen(filenames[i])-1] == '/') {
01002                         filename = strdup(filenames[i]);
01003                 }
01004                 else {
01005                         nasprintf( &filename, "%s/", filenames[i] );
01006                 }
01007                 create_watch(wd, filename);
01008                 free(filename);
01009         } // for
01010 
01011         return 1;
01012 }
01013 
01040 struct inotify_event * inotifytools_next_event( int timeout ) {
01041         return inotifytools_next_events( timeout, 1 );
01042 }
01043 
01044 
01094 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01095         niceassert( init, "inotifytools_initialize not called yet" );
01096         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01097 
01098         if ( num_events < 1 ) return NULL;
01099 
01100         static struct inotify_event event[MAX_EVENTS];
01101         static struct inotify_event * ret;
01102         static int first_byte = 0;
01103         static ssize_t bytes;
01104         static jmp_buf jmp;
01105         static char match_name[MAX_STRLEN];
01106 
01107 #define RETURN(A) {\
01108         if (regex) {\
01109                 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01110                 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01111                         longjmp(jmp,0);\
01112                 }\
01113         }\
01114         if ( collect_stats ) {\
01115                 record_stats( A );\
01116         }\
01117         return A;\
01118 }
01119 
01120         setjmp(jmp);
01121 
01122         error = 0;
01123 
01124         // first_byte is index into event buffer
01125         if ( first_byte != 0
01126           && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01127 
01128                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01129                 first_byte += sizeof(struct inotify_event) + ret->len;
01130 
01131                 // if the pointer to the next event exactly hits end of bytes read,
01132                 // that's good.  next time we're called, we'll read.
01133                 if ( first_byte == bytes ) {
01134                         first_byte = 0;
01135                 }
01136                 else if ( first_byte > bytes ) {
01137                         // oh... no.  this can't be happening.  An incomplete event.
01138                         // Copy what we currently have into first element, call self to
01139                         // read remainder.
01140                         // oh, and they BETTER NOT overlap.
01141                         // Boy I hope this code works.
01142                         // But I think this can never happen due to how inotify is written.
01143                         niceassert( (long)((char *)&event[0] +
01144                                     sizeof(struct inotify_event) +
01145                                     event[0].len) <= (long)ret,
01146                                     "extremely unlucky user, death imminent" );
01147                         // how much of the event do we have?
01148                         bytes = (char *)&event[0] + bytes - (char *)ret;
01149                         memcpy( &event[0], ret, bytes );
01150                         return inotifytools_next_events( timeout, num_events );
01151                 }
01152                 RETURN(ret);
01153 
01154         }
01155 
01156         else if ( first_byte == 0 ) {
01157                 bytes = 0;
01158         }
01159 
01160 
01161         static ssize_t this_bytes;
01162         static unsigned int bytes_to_read;
01163         static int rc;
01164         static fd_set read_fds;
01165 
01166         static struct timeval read_timeout;
01167         read_timeout.tv_sec = timeout;
01168         read_timeout.tv_usec = 0;
01169         static struct timeval * read_timeout_ptr;
01170         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01171 
01172         FD_ZERO(&read_fds);
01173         FD_SET(inotify_fd, &read_fds);
01174         rc = select(inotify_fd + 1, &read_fds,
01175                     NULL, NULL, read_timeout_ptr);
01176         if ( rc < 0 ) {
01177                 // error
01178                 error = errno;
01179                 return NULL;
01180         }
01181         else if ( rc == 0 ) {
01182                 // timeout
01183                 return NULL;
01184         }
01185 
01186         // wait until we have enough bytes to read
01187         do {
01188                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01189         } while ( !rc &&
01190                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01191 
01192         if ( rc == -1 ) {
01193                 error = errno;
01194                 return NULL;
01195         }
01196 
01197         this_bytes = read(inotify_fd, &event[0] + bytes,
01198                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01199         if ( this_bytes < 0 ) {
01200                 error = errno;
01201                 return NULL;
01202         }
01203         if ( this_bytes == 0 ) {
01204                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01205                                 "events occurred at once.\n");
01206                 return NULL;
01207         }
01208         bytes += this_bytes;
01209 
01210         ret = &event[0];
01211         first_byte = sizeof(struct inotify_event) + ret->len;
01212         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01213                                          "almost certainly screw up." );
01214         if ( first_byte == bytes ) {
01215                 first_byte = 0;
01216         }
01217 
01218         RETURN(ret);
01219 
01220 #undef RETURN
01221 }
01222 
01248 int inotifytools_watch_recursively( char const * path, int events ) {
01249         return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01250 }
01251 
01284 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01285                                                  char const ** exclude_list ) {
01286         niceassert( init, "inotifytools_initialize not called yet" );
01287 
01288         DIR * dir;
01289         char * my_path;
01290         error = 0;
01291         dir = opendir( path );
01292         if ( !dir ) {
01293                 // If not a directory, don't need to do anything special
01294                 if ( errno == ENOTDIR ) {
01295                         return inotifytools_watch_file( path, events );
01296                 }
01297                 else {
01298                         error = errno;
01299                         return 0;
01300                 }
01301         }
01302 
01303         if ( path[strlen(path)-1] != '/' ) {
01304                 nasprintf( &my_path, "%s/", path );
01305         }
01306         else {
01307                 my_path = (char *)path;
01308         }
01309 
01310         static struct dirent * ent;
01311         char * next_file;
01312         static struct stat64 my_stat;
01313         ent = readdir( dir );
01314         // Watch each directory within this directory
01315         while ( ent ) {
01316                 if ( (0 != strcmp( ent->d_name, "." )) &&
01317                      (0 != strcmp( ent->d_name, ".." )) ) {
01318                         nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01319                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01320                                 error = errno;
01321                                 free( next_file );
01322                                 if ( errno != EACCES ) {
01323                                         error = errno;
01324                                         if ( my_path != path ) free( my_path );
01325                                         closedir( dir );
01326                                         return 0;
01327                                 }
01328                         }
01329                         else if ( S_ISDIR( my_stat.st_mode ) &&
01330                                   !S_ISLNK( my_stat.st_mode )) {
01331                                 free( next_file );
01332                                 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01333                                 static unsigned int no_watch;
01334                                 static char const ** exclude_entry;
01335 
01336                                 no_watch = 0;
01337                                 for (exclude_entry = exclude_list;
01338                                          exclude_entry && *exclude_entry && !no_watch;
01339                                          ++exclude_entry) {
01340                                         static int exclude_length;
01341 
01342                                         exclude_length = strlen(*exclude_entry);
01343                                         if ((*exclude_entry)[exclude_length-1] == '/') {
01344                                                 --exclude_length;
01345                                         }
01346                                         if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01347                                             !strncmp(*exclude_entry, next_file, exclude_length)) {
01348                                                 // directory found in exclude list
01349                                                 no_watch = 1;
01350                                         }
01351                                 }
01352                                 if (!no_watch) {
01353                                         static int status;
01354                                         status = inotifytools_watch_recursively_with_exclude(
01355                                                       next_file,
01356                                                       events,
01357                                                       exclude_list );
01358                                         // For some errors, we will continue.
01359                                         if ( !status && (EACCES != error) && (ENOENT != error) &&
01360                                              (ELOOP != error) ) {
01361                                                 free( next_file );
01362                                                 if ( my_path != path ) free( my_path );
01363                                                 closedir( dir );
01364                                                 return 0;
01365                                         }
01366                                 } // if !no_watch
01367                                 free( next_file );
01368                         } // if isdir and not islnk
01369                         else {
01370                                 free( next_file );
01371                         }
01372                 }
01373                 ent = readdir( dir );
01374                 error = 0;
01375         }
01376 
01377         closedir( dir );
01378 
01379         int ret = inotifytools_watch_file( my_path, events );
01380         if ( my_path != path ) free( my_path );
01381         return ret;
01382 }
01383 
01387 void record_stats( struct inotify_event const * event ) {
01388         if (!event) return;
01389         watch *w = watch_from_wd(event->wd);
01390         if (!w) return;
01391         if ( IN_ACCESS & event->mask ) {
01392                 ++w->hit_access;
01393                 ++num_access;
01394         }
01395         if ( IN_MODIFY & event->mask ) {
01396                 ++w->hit_modify;
01397                 ++num_modify;
01398         }
01399         if ( IN_ATTRIB & event->mask ) {
01400                 ++w->hit_attrib;
01401                 ++num_attrib;
01402         }
01403         if ( IN_CLOSE_WRITE & event->mask ) {
01404                 ++w->hit_close_write;
01405                 ++num_close_write;
01406         }
01407         if ( IN_CLOSE_NOWRITE & event->mask ) {
01408                 ++w->hit_close_nowrite;
01409                 ++num_close_nowrite;
01410         }
01411         if ( IN_OPEN & event->mask ) {
01412                 ++w->hit_open;
01413                 ++num_open;
01414         }
01415         if ( IN_MOVED_FROM & event->mask ) {
01416                 ++w->hit_moved_from;
01417                 ++num_moved_from;
01418         }
01419         if ( IN_MOVED_TO & event->mask ) {
01420                 ++w->hit_moved_to;
01421                 ++num_moved_to;
01422         }
01423         if ( IN_CREATE & event->mask ) {
01424                 ++w->hit_create;
01425                 ++num_create;
01426         }
01427         if ( IN_DELETE & event->mask ) {
01428                 ++w->hit_delete;
01429                 ++num_delete;
01430         }
01431         if ( IN_DELETE_SELF & event->mask ) {
01432                 ++w->hit_delete_self;
01433                 ++num_delete_self;
01434         }
01435         if ( IN_UNMOUNT & event->mask ) {
01436                 ++w->hit_unmount;
01437                 ++num_unmount;
01438         }
01439         if ( IN_MOVE_SELF & event->mask ) {
01440                 ++w->hit_move_self;
01441                 ++num_move_self;
01442         }
01443 
01444         ++w->hit_total;
01445         ++num_total;
01446 
01447 }
01448 
01449 int *stat_ptr(watch *w, int event)
01450 {
01451         if ( IN_ACCESS == event )
01452                 return &w->hit_access;
01453         if ( IN_MODIFY == event )
01454                 return &w->hit_modify;
01455         if ( IN_ATTRIB == event )
01456                 return &w->hit_attrib;
01457         if ( IN_CLOSE_WRITE == event )
01458                 return &w->hit_close_write;
01459         if ( IN_CLOSE_NOWRITE == event )
01460                 return &w->hit_close_nowrite;
01461         if ( IN_OPEN == event )
01462                 return &w->hit_open;
01463         if ( IN_MOVED_FROM == event )
01464                 return &w->hit_moved_from;
01465         if ( IN_MOVED_TO == event )
01466                 return &w->hit_moved_to;
01467         if ( IN_CREATE == event )
01468                 return &w->hit_create;
01469         if ( IN_DELETE == event )
01470                 return &w->hit_delete;
01471         if ( IN_DELETE_SELF == event )
01472                 return &w->hit_delete_self;
01473         if ( IN_UNMOUNT == event )
01474                 return &w->hit_unmount;
01475         if ( IN_MOVE_SELF == event )
01476                 return &w->hit_move_self;
01477         if ( 0 == event )
01478                 return &w->hit_total;
01479         return 0;
01480 }
01481 
01497 int inotifytools_get_stat_by_wd( int wd, int event ) {
01498         if (!collect_stats) return -1;
01499 
01500         watch *w = watch_from_wd(wd);
01501         if (!w) return -1;
01502         int *i = stat_ptr(w, event);
01503         if (!i) return -1;
01504         return *i;
01505 }
01506 
01520 int inotifytools_get_stat_total( int event ) {
01521         if (!collect_stats) return -1;
01522         if ( IN_ACCESS == event )
01523                 return num_access;
01524         if ( IN_MODIFY == event )
01525                 return num_modify;
01526         if ( IN_ATTRIB == event )
01527                 return num_attrib;
01528         if ( IN_CLOSE_WRITE == event )
01529                 return num_close_write;
01530         if ( IN_CLOSE_NOWRITE == event )
01531                 return num_close_nowrite;
01532         if ( IN_OPEN == event )
01533                 return num_open;
01534         if ( IN_MOVED_FROM == event )
01535                 return num_moved_from;
01536         if ( IN_MOVED_TO == event )
01537                 return num_moved_to;
01538         if ( IN_CREATE == event )
01539                 return num_create;
01540         if ( IN_DELETE == event )
01541                 return num_delete;
01542         if ( IN_DELETE_SELF == event )
01543                 return num_delete_self;
01544         if ( IN_UNMOUNT == event )
01545                 return num_unmount;
01546         if ( IN_MOVE_SELF == event )
01547                 return num_move_self;
01548 
01549         if ( 0 == event )
01550                 return num_total;
01551 
01552         return -1;
01553 }
01554 
01574 int inotifytools_get_stat_by_filename( char const * filename,
01575                                                 int event ) {
01576         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01577                filename ), event );
01578 }
01579 
01590 int inotifytools_error() {
01591         return error;
01592 }
01593 
01597 int isdir( char const * path ) {
01598         static struct stat64 my_stat;
01599 
01600         if ( -1 == lstat64( path, &my_stat ) ) {
01601                 if (errno == ENOENT) return 0;
01602                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01603                 return 0;
01604         }
01605 
01606         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01607 }
01608 
01609 
01616 int inotifytools_get_num_watches() {
01617         int ret = 0;
01618         rbwalk(tree_filename, get_num, (void*)&ret);
01619         return ret;
01620 }
01621 
01662 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01663         return inotifytools_fprintf( stdout, event, fmt );
01664 }
01665 
01707 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01708         static char out[MAX_STRLEN+1];
01709         static int ret;
01710         ret = inotifytools_sprintf( out, event, fmt );
01711         if ( -1 != ret ) fprintf( file, "%s", out );
01712         return ret;
01713 }
01714 
01765 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01766         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01767 }
01768 
01769 
01816 int inotifytools_snprintf( char * out, int size,
01817                            struct inotify_event* event, char* fmt ) {
01818         static char * filename, * eventname, * eventstr;
01819         static unsigned int i, ind;
01820         static char ch1;
01821         static char timestr[MAX_STRLEN];
01822         static time_t now;
01823 
01824 
01825         if ( event->len > 0 ) {
01826                 eventname = event->name;
01827         }
01828         else {
01829                 eventname = NULL;
01830         }
01831 
01832 
01833         filename = inotifytools_filename_from_wd( event->wd );
01834 
01835         if ( !fmt || 0 == strlen(fmt) ) {
01836                 error = EINVAL;
01837                 return -1;
01838         }
01839         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01840                 error = EMSGSIZE;
01841                 return -1;
01842         }
01843 
01844         ind = 0;
01845         for ( i = 0; i < strlen(fmt) &&
01846                      (int)ind < size - 1; ++i ) {
01847                 if ( fmt[i] != '%' ) {
01848                         out[ind++] = fmt[i];
01849                         continue;
01850                 }
01851 
01852                 if ( i == strlen(fmt) - 1 ) {
01853                         // last character is %, invalid
01854                         error = EINVAL;
01855                         return ind;
01856                 }
01857 
01858                 ch1 = fmt[i+1];
01859 
01860                 if ( ch1 == '%' ) {
01861                         out[ind++] = '%';
01862                         ++i;
01863                         continue;
01864                 }
01865 
01866                 if ( ch1 == 'w' ) {
01867                         if ( filename ) {
01868                                 strncpy( &out[ind], filename, size - ind );
01869                                 ind += strlen(filename);
01870                         }
01871                         ++i;
01872                         continue;
01873                 }
01874 
01875                 if ( ch1 == 'f' ) {
01876                         if ( eventname ) {
01877                                 strncpy( &out[ind], eventname, size - ind );
01878                                 ind += strlen(eventname);
01879                         }
01880                         ++i;
01881                         continue;
01882                 }
01883 
01884                 if ( ch1 == 'e' ) {
01885                         eventstr = inotifytools_event_to_str( event->mask );
01886                         strncpy( &out[ind], eventstr, size - ind );
01887                         ind += strlen(eventstr);
01888                         ++i;
01889                         continue;
01890                 }
01891 
01892                 if ( ch1 == 'T' ) {
01893 
01894                         if ( timefmt ) {
01895 
01896                                 now = time(0);
01897                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01898                                                     localtime( &now ) ) ) {
01899 
01900                                         // time format probably invalid
01901                                         error = EINVAL;
01902                                         return ind;
01903                                 }
01904                         }
01905                         else {
01906                                 timestr[0] = 0;
01907                         }
01908 
01909                         strncpy( &out[ind], timestr, size - ind );
01910                         ind += strlen(timestr);
01911                         ++i;
01912                         continue;
01913                 }
01914 
01915                 // Check if next char in fmt is e
01916                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01917                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01918                         strncpy( &out[ind], eventstr, size - ind );
01919                         ind += strlen(eventstr);
01920                         i += 2;
01921                         continue;
01922                 }
01923 
01924                 // OK, this wasn't a special format character, just output it as normal
01925                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01926                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01927                 ++i;
01928         }
01929         out[ind] = 0;
01930 
01931         return ind - 1;
01932 }
01933 
01943 void inotifytools_set_printf_timefmt( char * fmt ) {
01944         timefmt = fmt;
01945 }
01946 
01955 int inotifytools_get_max_queued_events() {
01956         int ret;
01957         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01958         return ret;
01959 }
01960 
01970 int inotifytools_get_max_user_instances() {
01971         int ret;
01972         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01973         return ret;
01974 }
01975 
01985 int inotifytools_get_max_user_watches() {
01986         int ret;
01987         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01988         return ret;
01989 }
01990 
02002 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
02003         if (!pattern) {
02004                 if (regex) {
02005                         regfree(regex);
02006                         free(regex);
02007                         regex = 0;
02008                 }
02009                 return 1;
02010         }
02011 
02012         if (regex) { regfree(regex); }
02013         else       { regex = (regex_t *)malloc(sizeof(regex_t)); }
02014 
02015         int ret = regcomp(regex, pattern, flags | REG_NOSUB);
02016         if (0 == ret) return 1;
02017 
02018         regfree(regex);
02019         free(regex);
02020         regex = 0;
02021         error = EINVAL;
02022         return 0;
02023 }
02024 
02025 int event_compare(const void *p1, const void *p2, const void *config)
02026 {
02027         if (!p1 || !p2) return p1 - p2;
02028         char asc = 1;
02029         int sort_event = (int)config;
02030         if (sort_event == -1) {
02031                 sort_event = 0;
02032                 asc = 0;
02033         } else if (sort_event < 0) {
02034                 sort_event = -sort_event;
02035                 asc = 0;
02036         }
02037         int *i1 = stat_ptr((watch*)p1, sort_event);
02038         int *i2 = stat_ptr((watch*)p2, sort_event);
02039         if (0 == *i1 - *i2) {
02040                 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02041         }
02042         if (asc)
02043                 return *i1 - *i2;
02044         else
02045                 return *i2 - *i1;
02046 }
02047 
02048 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02049 {
02050         struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02051         RBLIST *all = rbopenlist(tree_wd);
02052         void const *p = rbreadlist(all);
02053         while (p) {
02054                 void const *r = rbsearch(p, ret);
02055                 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02056                 p = rbreadlist(all);
02057         }
02058         rbcloselist(all);
02059         return ret;
02060 }