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

Generated on Mon Dec 3 12:19:26 2007 for libinotifytools by  doxygen 1.5.4