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

Generated on Tue Jun 12 15:55:12 2012 for libinotifytools by  doxygen 1.4.6