inotifytools.c

00001 // kate: replace-tabs off; space-indent off;
00002 
00015 #include "../../config.h"
00016 #include "inotifytools/inotifytools.h"
00017 
00018 #include <string.h>
00019 #include <strings.h>
00020 #include <stdlib.h>
00021 #include <stdio.h>
00022 #include <errno.h>
00023 #include <sys/select.h>
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <sys/ioctl.h>
00027 #include <unistd.h>
00028 #include <dirent.h>
00029 #include <time.h>
00030 #include <regex.h>
00031 #include <setjmp.h>
00032 
00033 #include "inotifytools/inotify.h"
00034 
00130 #define MAX_EVENTS 4096
00131 #define MAX_STRLEN 4096
00132 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00133 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00134 #define QUEUE_SIZE_PATH   INOTIFY_PROCDIR "max_queued_watches"
00135 #define INSTANCES_PATH    INOTIFY_PROCDIR "max_user_instances"
00136 
00137 static int inotify_fd;
00138 static unsigned int * hit_access = 0;
00139 static unsigned int * hit_modify = 0;
00140 static unsigned int * hit_attrib = 0;
00141 static unsigned int * hit_close_nowrite = 0;
00142 static unsigned int * hit_close_write = 0;
00143 static unsigned int * hit_open = 0;
00144 static unsigned int * hit_move_self = 0;
00145 static unsigned int * hit_moved_from = 0;
00146 static unsigned int * hit_moved_to = 0;
00147 static unsigned int * hit_create = 0;
00148 static unsigned int * hit_delete = 0;
00149 static unsigned int * hit_delete_self = 0;
00150 static unsigned int * hit_unmount = 0;
00151 static unsigned int * hit_total = 0;
00152 static unsigned int num_access;
00153 static unsigned int num_modify;
00154 static unsigned int num_attrib;
00155 static unsigned int num_close_nowrite;
00156 static unsigned int num_close_write;
00157 static unsigned int num_open;
00158 static unsigned int num_move_self;
00159 static unsigned int num_moved_to;
00160 static unsigned int num_moved_from;
00161 static unsigned int num_create;
00162 static unsigned int num_delete;
00163 static unsigned int num_delete_self;
00164 static unsigned int num_unmount;
00165 static unsigned int num_total;
00166 static int collect_stats = 0;
00167 static int WATCHES_SIZE;
00168 static char ** watches;
00169 static int error;
00170 static unsigned int num_watches;
00171 static unsigned int watches_buf_size;
00172 static int init = 0;
00173 static char * timefmt = 0;
00174 static regex_t *regex = 0;
00175 
00176 void allocatestatmem( int from, int to );
00177 int isdir( char const * path );
00178 void record_stats( struct inotify_event const * event );
00179 int onestr_to_event(char const * event);
00180 
00198 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00199                                           #cond, mesg)
00200 
00201 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00202 
00220 void _niceassert( long cond, int line, char const * file, char const * condstr,
00221                   char const * mesg ) {
00222         if ( cond ) return;
00223 
00224         if ( mesg ) {
00225                 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00226                         condstr, mesg );
00227         }
00228         else {
00229                 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00230         }
00231 }
00232 
00247 void * recalloc( void * ptr, size_t from, size_t to ) {
00248         static void * ret;
00249         ret = realloc( ptr, to );
00250         niceassert( ret, "out of memory" );
00251         if ( ret && to > from ) {
00252                 memset( ret + from, 0, (to - from) );
00253         }
00254         return ret;
00255 }
00256 
00263 void resize_buffers( int diff ) {
00264         static int oldsize;
00265         oldsize = watches_buf_size;
00266         watches_buf_size += diff;
00267         watches = (char **)recalloc( watches, oldsize*sizeof(char *),
00268                                      watches_buf_size*sizeof(char *) );
00269         if ( collect_stats ) allocatestatmem( oldsize*sizeof(char *),
00270                                               watches_buf_size*sizeof(char *) );
00271 }
00272 
00282 char * chrtostr(char ch) {
00283         static char str[2] = { '\0', '\0' };
00284         str[0] = ch;
00285         return str;
00286 }
00287 
00291 int read_num_from_file( char * filename, int * num ) {
00292         FILE * file = fopen( filename, "r" );
00293         if ( !file ) {
00294                 error = errno;
00295                 return 0;
00296         }
00297 
00298         if ( EOF == fscanf( file, "%d", num ) ) {
00299                 error = errno;
00300                 return 0;
00301         }
00302 
00303         niceassert( 0 == fclose( file ), 0 );
00304 
00305         return 1;
00306 }
00307 
00317 int inotifytools_initialize() {
00318         error = 0;
00319         // Try to initialise inotify
00320         inotify_fd = inotify_init();
00321         if (inotify_fd < 0)     {
00322                 error = inotify_fd;
00323                 return 0;
00324         }
00325 
00326         if ( !read_num_from_file( WATCHES_SIZE_PATH, &WATCHES_SIZE ) ) return 0;
00327 
00328         watches_buf_size = WATCHES_SIZE;
00329         watches = (char **)calloc( watches_buf_size, sizeof(char *) );
00330         niceassert( watches, "out of memory, gosh darn it" );
00331 
00332         collect_stats = 0;
00333         num_watches = 0;
00334         init = 1;
00335 
00336         return 1;
00337 }
00338 
00347 void allocatestatmem( int from, int to ) {
00348         hit_access = (unsigned int *)recalloc( hit_access, from, to );
00349         hit_modify = (unsigned int *)recalloc( hit_modify, from, to );
00350         hit_attrib = (unsigned int *)recalloc( hit_attrib, from, to );
00351         hit_close_nowrite = (unsigned int *)recalloc( hit_close_nowrite, from, to );
00352         hit_close_write = (unsigned int *)recalloc( hit_close_write, from, to );
00353         hit_open = (unsigned int *)recalloc( hit_open, from, to );
00354         hit_move_self = (unsigned int *)recalloc( hit_move_self, from, to );
00355         hit_moved_from = (unsigned int *)recalloc( hit_moved_from, from, to );
00356         hit_moved_to = (unsigned int *)recalloc( hit_moved_to, from, to );
00357         hit_create = (unsigned int *)recalloc( hit_create, from, to );
00358         hit_delete = (unsigned int *)recalloc( hit_delete, from, to );
00359         hit_delete_self = (unsigned int *)recalloc( hit_delete_self, from, to );
00360         hit_unmount = (unsigned int *)recalloc( hit_unmount, from, to );
00361         hit_total = (unsigned int *)recalloc( hit_total, from, to );
00362 }
00363 
00376 void inotifytools_initialize_stats() {
00377         niceassert( init, "inotifytools_initialize not called yet" );
00378 
00379         // if not already collecting stats, allocate buffer
00380         if ( !collect_stats ) {
00381                 allocatestatmem( 0, watches_buf_size );
00382         }
00383         // if already collecting stats, reset stats
00384         else {
00385                 memset( hit_access, 0, watches_buf_size );
00386                 memset( hit_modify, 0, watches_buf_size );
00387                 memset( hit_attrib, 0, watches_buf_size );
00388                 memset( hit_close_nowrite, 0, watches_buf_size );
00389                 memset( hit_close_write, 0, watches_buf_size );
00390                 memset( hit_open, 0, watches_buf_size );
00391                 memset( hit_move_self, 0, watches_buf_size );
00392                 memset( hit_moved_from, 0, watches_buf_size );
00393                 memset( hit_moved_to, 0, watches_buf_size );
00394                 memset( hit_create, 0, watches_buf_size );
00395                 memset( hit_delete, 0, watches_buf_size );
00396                 memset( hit_delete_self, 0, watches_buf_size );
00397                 memset( hit_unmount, 0, watches_buf_size );
00398                 memset( hit_total, 0, watches_buf_size );
00399         }
00400 
00401         num_access = 0;
00402         num_modify = 0;
00403         num_attrib = 0;
00404         num_close_nowrite = 0;
00405         num_close_write = 0;
00406         num_open = 0;
00407         num_move_self = 0;
00408         num_moved_from = 0;
00409         num_moved_to = 0;
00410         num_create = 0;
00411         num_delete = 0;
00412         num_delete_self = 0;
00413         num_unmount = 0;
00414         num_total = 0;
00415 
00416         collect_stats = 1;
00417 }
00418 
00446 int inotifytools_str_to_event_sep(char const * event, char sep) {
00447         if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00448                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00449                 return -1;
00450         }
00451 
00452         int ret, ret1, len;
00453         char * event1, * event2;
00454         char eventstr[4096];
00455         ret = 0;
00456 
00457         if ( !event || !event[0] ) return 0;
00458 
00459         event1 = (char *)event;
00460         event2 = strchr( event1, sep );
00461         while ( event1 && event1[0] ) {
00462                 if ( event2 ) {
00463                         len = event2 - event1;
00464                         niceassert( len < 4096, "malformed event string (very long)" );
00465                 }
00466                 else {
00467                         len = strlen(event1);
00468                 }
00469                 if ( len > 4095 ) len = 4095;
00470                 strncpy( eventstr, event1, len );
00471                 eventstr[len] = 0;
00472 
00473                 ret1 = onestr_to_event( eventstr );
00474                 if ( 0 == ret1 || -1 == ret1 ) {
00475                         ret = ret1;
00476                         break;
00477                 }
00478                 ret |= ret1;
00479 
00480                 event1 = event2;
00481                 if ( event1 && event1[0] ) {
00482                         // jump over 'sep' character
00483                         ++event1;
00484                         // if last character was 'sep'...
00485                         if ( !event1[0] ) return 0;
00486                         event2 = strchr( event1, sep );
00487                 }
00488         }
00489 
00490         return ret;
00491 }
00492 
00516 int inotifytools_str_to_event(char const * event) {
00517         return inotifytools_str_to_event_sep( event, ',' );
00518 }
00519 
00531 int onestr_to_event(char const * event)
00532 {
00533         static int ret;
00534         ret = -1;
00535 
00536         if ( !event || !event[0] )
00537                 ret = 0;
00538         else if ( 0 == strcasecmp(event, "ACCESS") )
00539                 ret = IN_ACCESS;
00540         else if ( 0 == strcasecmp(event, "MODIFY") )
00541                 ret = IN_MODIFY;
00542         else if ( 0 == strcasecmp(event, "ATTRIB") )
00543                 ret = IN_ATTRIB;
00544         else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00545                 ret = IN_CLOSE_WRITE;
00546         else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00547                 ret = IN_CLOSE_NOWRITE;
00548         else if ( 0 == strcasecmp(event, "OPEN") )
00549                 ret = IN_OPEN;
00550         else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00551                 ret = IN_MOVED_FROM;
00552         else if ( 0 == strcasecmp(event, "MOVED_TO") )
00553                 ret = IN_MOVED_TO;
00554         else if ( 0 == strcasecmp(event, "CREATE") )
00555                 ret = IN_CREATE;
00556         else if ( 0 == strcasecmp(event, "DELETE") )
00557                 ret = IN_DELETE;
00558         else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00559                 ret = IN_DELETE_SELF;
00560         else if ( 0 == strcasecmp(event, "UNMOUNT") )
00561                 ret = IN_UNMOUNT;
00562         else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00563                 ret = IN_Q_OVERFLOW;
00564         else if ( 0 == strcasecmp(event, "IGNORED") )
00565                 ret = IN_IGNORED;
00566         else if ( 0 == strcasecmp(event, "CLOSE") )
00567                 ret = IN_CLOSE;
00568         else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00569                 ret = IN_MOVE_SELF;
00570         else if ( 0 == strcasecmp(event, "MOVE") )
00571                 ret = IN_MOVE;
00572         else if ( 0 == strcasecmp(event, "ISDIR") )
00573                 ret = IN_ISDIR;
00574         else if ( 0 == strcasecmp(event, "ONESHOT") )
00575                 ret = IN_ONESHOT;
00576         else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00577                 ret = IN_ALL_EVENTS;
00578 
00579         return ret;
00580 }
00581 
00603 char * inotifytools_event_to_str(int events) {
00604         return inotifytools_event_to_str_sep(events, ',');
00605 }
00606 
00631 char * inotifytools_event_to_str_sep(int events, char sep)
00632 {
00633         static char ret[1024];
00634         ret[0] = '\0';
00635         ret[1] = '\0';
00636 
00637         if ( IN_ACCESS & events ) {
00638                 strcat( ret, chrtostr(sep) );
00639                 strcat( ret, "ACCESS" );
00640         }
00641         if ( IN_MODIFY & events ) {
00642                 strcat( ret, chrtostr(sep) );
00643                 strcat( ret, "MODIFY" );
00644         }
00645         if ( IN_ATTRIB & events ) {
00646                 strcat( ret, chrtostr(sep) );
00647                 strcat( ret, "ATTRIB" );
00648         }
00649         if ( IN_CLOSE_WRITE & events ) {
00650                 strcat( ret, chrtostr(sep) );
00651                 strcat( ret, "CLOSE_WRITE" );
00652         }
00653         if ( IN_CLOSE_NOWRITE & events ) {
00654                 strcat( ret, chrtostr(sep) );
00655                 strcat( ret, "CLOSE_NOWRITE" );
00656         }
00657         if ( IN_OPEN & events ) {
00658                 strcat( ret, chrtostr(sep) );
00659                 strcat( ret, "OPEN" );
00660         }
00661         if ( IN_MOVED_FROM & events ) {
00662                 strcat( ret, chrtostr(sep) );
00663                 strcat( ret, "MOVED_FROM" );
00664         }
00665         if ( IN_MOVED_TO & events ) {
00666                 strcat( ret, chrtostr(sep) );
00667                 strcat( ret, "MOVED_TO" );
00668         }
00669         if ( IN_CREATE & events ) {
00670                 strcat( ret, chrtostr(sep) );
00671                 strcat( ret, "CREATE" );
00672         }
00673         if ( IN_DELETE & events ) {
00674                 strcat( ret, chrtostr(sep) );
00675                 strcat( ret, "DELETE" );
00676         }
00677         if ( IN_DELETE_SELF & events ) {
00678                 strcat( ret, chrtostr(sep) );
00679                 strcat( ret, "DELETE_SELF" );
00680         }
00681         if ( IN_UNMOUNT & events ) {
00682                 strcat( ret, chrtostr(sep) );
00683                 strcat( ret, "UNMOUNT" );
00684         }
00685         if ( IN_Q_OVERFLOW & events ) {
00686                 strcat( ret, chrtostr(sep) );
00687                 strcat( ret, "Q_OVERFLOW" );
00688         }
00689         if ( IN_IGNORED & events ) {
00690                 strcat( ret, chrtostr(sep) );
00691                 strcat( ret, "IGNORED" );
00692         }
00693         if ( IN_CLOSE & events ) {
00694                 strcat( ret, chrtostr(sep) );
00695                 strcat( ret, "CLOSE" );
00696         }
00697         if ( IN_MOVE_SELF & events ) {
00698                 strcat( ret, chrtostr(sep) );
00699                 strcat( ret, "MOVE_SELF" );
00700         }
00701         if ( IN_ISDIR & events ) {
00702                 strcat( ret, chrtostr(sep) );
00703                 strcat( ret, "ISDIR" );
00704         }
00705         if ( IN_ONESHOT & events ) {
00706                 strcat( ret, chrtostr(sep) );
00707                 strcat( ret, "ONESHOT" );
00708         }
00709 
00710         // Maybe we didn't match any... ?
00711         if (ret[0] == '\0') {
00712                 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00713         }
00714 
00715         return &ret[1];
00716 }
00717 
00738 char * inotifytools_filename_from_wd( int wd ) {
00739         if ( wd <= 0 ) return 0;
00740         niceassert( init, "inotifytools_initialize not called yet" );
00741         return watches[wd-1];
00742 }
00743 
00758 int inotifytools_wd_from_filename( char const * filename ) {
00759         niceassert( filename, 0 );
00760         niceassert( init, "inotifytools_initialize not called yet" );
00761         static int i;
00762         for ( i = 0; i < WATCHES_SIZE; ++i ) {
00763                 if ( watches[i] && 0 == strcmp( filename, watches[i] ) ) {
00764                         return i+1;
00765                 }
00766         }
00767         return -1;
00768 }
00769 
00784 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00785         if ( wd <= 0 ) return;
00786         niceassert( init, "inotifytools_initialize not called yet" );
00787         if ( watches[wd-1] ) free( watches[wd-1] );
00788         watches[wd-1] = strdup(filename);
00789 }
00790 
00805 void inotifytools_set_filename_by_filename( char const * oldname,
00806                                             char const * newname ) {
00807         if ( !oldname ) return;
00808         inotifytools_set_filename_by_wd(
00809           inotifytools_wd_from_filename( oldname ), newname
00810         );
00811 }
00812 
00835 void inotifytools_replace_filename( char const * oldname,
00836                                     char const * newname ) {
00837         if ( !oldname || !newname ) return;
00838         int oldlen = strlen(oldname);
00839         static char * name = 0;
00840         for ( int i = 0; i < WATCHES_SIZE; ++i ) {
00841                 if ( watches[i] && 0 == strncmp( oldname, watches[i], oldlen ) ) {
00842                         nasprintf( &name, "%s%s", newname, &(watches[i][oldlen]) );
00843                         free( watches[i] );
00844                         watches[i] = name;
00845                 }
00846         }
00847 }
00848 
00860 int inotifytools_remove_watch_by_filename( char const * filename ) {
00861         return inotifytools_remove_watch_by_wd( inotifytools_wd_from_filename(
00862                filename ) );
00863 }
00864 
00876 int inotifytools_remove_watch_by_wd( int wd ) {
00877         niceassert( wd > 0, 0 );
00878         niceassert( watches[wd - 1], "No watch exists for requested wd" );
00879         niceassert( init, "inotifytools_initialize not called yet" );
00880         error = 0;
00881         static int status;
00882         status = inotify_rm_watch( inotify_fd, wd );
00883         if ( status < 0 ) {
00884                 fprintf(stderr, "Failed to remove watch on %s: %s\n", watches[wd - 1],
00885                         strerror(status) );
00886                 error = status;
00887                 return 0;
00888         }
00889         free( watches[wd - 1] );
00890         watches[wd - 1] = NULL;
00891         num_watches--;
00892         if ( (int)num_watches < (int)watches_buf_size - (int)WATCHES_SIZE - 1 ) {
00893                 resize_buffers( -WATCHES_SIZE );
00894         }
00895         return 1;
00896 }
00897 
00909 int inotifytools_watch_file( char const * filename, int events ) {
00910         static char const * filenames[2];
00911         filenames[0] = filename;
00912         filenames[1] = NULL;
00913         return inotifytools_watch_files( filenames, events );
00914 }
00915 
00931 int inotifytools_watch_files( char const * filenames[], int events ) {
00932         niceassert( init, "inotifytools_initialize not called yet" );
00933         error = 0;
00934 
00935         static int i;
00936         for ( i = 0; filenames[i]; ++i ) {
00937                 static int wd;
00938                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00939                 if ( wd < 0 ) {
00940                         if ( wd == -1 ) {
00941                                 error = errno;
00942                                 return 0;
00943                         } // if ( wd == -1 )
00944                         else {
00945                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00946                                          "(expected -1 or >0 )", filenames[i], wd );
00947                                 // no appropriate value for error
00948                                 return 0;
00949                         } // else
00950                 } // if ( wd < 0 )
00951 
00952                 // Always end filename with / if it is a directory
00953                 if ( !isdir(filenames[i])
00954                      || filenames[i][strlen(filenames[i])-1] == '/') {
00955                         watches[wd - 1] = strdup(filenames[i]);
00956                 }
00957                 else {
00958                         nasprintf( &watches[wd - 1], "%s/", filenames[i] );
00959                 }
00960                 num_watches++;
00961                 if ( num_watches == watches_buf_size ) {
00962                         resize_buffers( WATCHES_SIZE );
00963                 }
00964         } // for
00965 
00966         return 1;
00967 }
00968 
00995 struct inotify_event * inotifytools_next_event( int timeout ) {
00996         return inotifytools_next_events( timeout, 1 );
00997 }
00998 
00999 
01049 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01050         niceassert( init, "inotifytools_initialize not called yet" );
01051         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01052 
01053         if ( num_events < 1 ) return NULL;
01054 
01055         static struct inotify_event event[MAX_EVENTS];
01056         static struct inotify_event * ret;
01057         static int first_byte = 0;
01058         static ssize_t bytes;
01059         static jmp_buf jmp;
01060         static char match_name[MAX_STRLEN];
01061 
01062 #define RETURN(A) {\
01063         if (regex) {\
01064                 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01065                 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01066                         longjmp(jmp,0);\
01067                 }\
01068         }\
01069         if ( collect_stats ) {\
01070                 record_stats( A );\
01071         }\
01072         return A;\
01073 }
01074 
01075         setjmp(jmp);
01076 
01077         error = 0;
01078 
01079         // first_byte is index into event buffer
01080         if ( first_byte != 0
01081           && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01082 
01083                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01084                 first_byte += sizeof(struct inotify_event) + ret->len;
01085 
01086                 // if the pointer to the next event exactly hits end of bytes read,
01087                 // that's good.  next time we're called, we'll read.
01088                 if ( first_byte == bytes ) {
01089                         first_byte = 0;
01090                 }
01091                 else if ( first_byte > bytes ) {
01092                         // oh... no.  this can't be happening.  An incomplete event.
01093                         // Copy what we currently have into first element, call self to
01094                         // read remainder.
01095                         // oh, and they BETTER NOT overlap.
01096                         // Boy I hope this code works.
01097                         // But I think this can never happen due to how inotify is written.
01098                         niceassert( (long)((char *)&event[0] +
01099                                     sizeof(struct inotify_event) +
01100                                     event[0].len) <= (long)ret,
01101                                     "extremely unlucky user, death imminent" );
01102                         // how much of the event do we have?
01103                         bytes = (char *)&event[0] + bytes - (char *)ret;
01104                         memcpy( &event[0], ret, bytes );
01105                         return inotifytools_next_events( timeout, num_events );
01106                 }
01107                 if ( collect_stats ) {
01108                         record_stats( ret );
01109                 }
01110                 RETURN(ret);
01111 
01112         }
01113 
01114         else if ( first_byte == 0 ) {
01115                 bytes = 0;
01116         }
01117 
01118 
01119         static ssize_t this_bytes;
01120         static unsigned int bytes_to_read;
01121         static int rc;
01122         static fd_set read_fds;
01123 
01124         static struct timeval read_timeout;
01125         read_timeout.tv_sec = timeout;
01126         read_timeout.tv_usec = 0;
01127         static struct timeval * read_timeout_ptr;
01128         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01129 
01130         FD_ZERO(&read_fds);
01131         FD_SET(inotify_fd, &read_fds);
01132         rc = select(inotify_fd + 1, &read_fds,
01133                     NULL, NULL, read_timeout_ptr);
01134         if ( rc < 0 ) {
01135                 // error
01136                 error = errno;
01137                 return NULL;
01138         }
01139         else if ( rc == 0 ) {
01140                 // timeout
01141                 return NULL;
01142         }
01143 
01144         // wait until we have enough bytes to read
01145         do {
01146                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01147         } while ( !rc &&
01148                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01149 
01150         if ( rc == -1 ) {
01151                 error = errno;
01152                 return NULL;
01153         }
01154 
01155         this_bytes = read(inotify_fd, &event[0] + bytes,
01156                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01157         if ( this_bytes < 0 ) {
01158                 error = errno;
01159                 return NULL;
01160         }
01161         if ( this_bytes == 0 ) {
01162                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01163                                 "events occurred at once.\n");
01164                 return NULL;
01165         }
01166         if ( this_bytes < (int)bytes_to_read ) {
01167                 fprintf(stderr, "Warning, too few bytes read from inotify!\n" );
01168         }
01169         bytes += this_bytes;
01170 
01171         ret = &event[0];
01172         first_byte = sizeof(struct inotify_event) + ret->len;
01173         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01174                                          "almost certainly screw up." );
01175         if ( first_byte == bytes ) {
01176                 first_byte = 0;
01177         }
01178 
01179         RETURN(ret);
01180 
01181 #undef RETURN
01182 }
01183 
01209 int inotifytools_watch_recursively( char const * path, int events ) {
01210         return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01211 }
01212 
01245 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01246                                                  char const ** exclude_list ) {
01247         niceassert( init, "inotifytools_initialize not called yet" );
01248 
01249         DIR * dir;
01250         char * my_path;
01251         error = 0;
01252         dir = opendir( path );
01253         if ( !dir ) {
01254                 // If not a directory, don't need to do anything special
01255                 if ( errno == ENOTDIR ) {
01256                         return inotifytools_watch_file( path, events );
01257                 }
01258                 else {
01259                         error = errno;
01260                         return 0;
01261                 }
01262         }
01263 
01264         if ( path[strlen(path)-1] != '/' ) {
01265                 nasprintf( &my_path, "%s/", path );
01266         }
01267         else {
01268                 my_path = (char *)path;
01269         }
01270 
01271         static struct dirent * ent;
01272         char * next_file;
01273         static struct stat64 my_stat;
01274         ent = readdir( dir );
01275         // Watch each directory within this directory
01276         while ( ent ) {
01277                 if ( (0 != strcmp( ent->d_name, "." )) &&
01278                      (0 != strcmp( ent->d_name, ".." )) ) {
01279                         nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01280                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01281                                 error = errno;
01282                                 free( next_file );
01283                                 if ( errno != EACCES ) {
01284                                         error = errno;
01285                                         if ( my_path != path ) free( my_path );
01286                                         closedir( dir );
01287                                         return 0;
01288                                 }
01289                         }
01290                         else if ( S_ISDIR( my_stat.st_mode ) &&
01291                                   !S_ISLNK( my_stat.st_mode )) {
01292                                 free( next_file );
01293                                 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01294                                 static unsigned int no_watch;
01295                                 static char const ** exclude_entry;
01296 
01297                                 no_watch = 0;
01298                                 for (exclude_entry = exclude_list;
01299                                          exclude_entry && *exclude_entry && !no_watch;
01300                                          ++exclude_entry) {
01301                                         static int exclude_length;
01302 
01303                                         exclude_length = strlen(*exclude_entry);
01304                                         if ((*exclude_entry)[exclude_length-1] == '/') {
01305                                                 --exclude_length;
01306                                         }
01307                                         if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01308                                             !strncmp(*exclude_entry, next_file, exclude_length)) {
01309                                                 // directory found in exclude list
01310                                                 no_watch = 1;
01311                                         }
01312                                 }
01313                                 if (!no_watch) {
01314                                         static int status;
01315                                         status = inotifytools_watch_recursively_with_exclude(
01316                                                       next_file,
01317                                                       events,
01318                                                       exclude_list );
01319                                         // For some errors, we will continue.
01320                                         if ( !status && (EACCES != error) && (ENOENT != error) &&
01321                                              (ELOOP != error) ) {
01322                                                 free( next_file );
01323                                                 if ( my_path != path ) free( my_path );
01324                                                 closedir( dir );
01325                                                 return 0;
01326                                         }
01327                                 } // if !no_watch
01328                                 free( next_file );
01329                         } // if isdir and not islnk
01330                         else {
01331                                 free( next_file );
01332                         }
01333                 }
01334                 ent = readdir( dir );
01335                 error = 0;
01336         }
01337 
01338         if ( my_path != path ) free( my_path );
01339         closedir( dir );
01340 
01341         return inotifytools_watch_file( path, events );
01342 }
01343 
01347 void record_stats( struct inotify_event const * event ) {
01348 
01349         if ( IN_ACCESS & event->mask ) {
01350                 hit_access[event->wd - 1]++;
01351                 num_access++;
01352         }
01353         if ( IN_MODIFY & event->mask ) {
01354                 hit_modify[event->wd - 1]++;
01355                 num_modify++;
01356         }
01357         if ( IN_ATTRIB & event->mask ) {
01358                 hit_attrib[event->wd - 1]++;
01359                 num_attrib++;
01360         }
01361         if ( IN_CLOSE_WRITE & event->mask ) {
01362                 hit_close_write[event->wd - 1]++;
01363                 num_close_write++;
01364         }
01365         if ( IN_CLOSE_NOWRITE & event->mask ) {
01366                 hit_close_nowrite[event->wd - 1]++;
01367                 num_close_nowrite++;
01368         }
01369         if ( IN_OPEN & event->mask ) {
01370                 hit_open[event->wd - 1]++;
01371                 num_open++;
01372         }
01373         if ( IN_MOVED_FROM & event->mask ) {
01374                 hit_moved_from[event->wd - 1]++;;
01375                 num_moved_from++;
01376         }
01377         if ( IN_MOVED_TO & event->mask ) {
01378                 hit_moved_to[event->wd - 1]++;
01379                 num_moved_to++;
01380         }
01381         if ( IN_CREATE & event->mask ) {
01382                 hit_create[event->wd - 1]++;
01383                 num_create++;
01384         }
01385         if ( IN_DELETE & event->mask ) {
01386                 hit_delete[event->wd - 1]++;
01387                 num_delete++;
01388         }
01389         if ( IN_DELETE_SELF & event->mask ) {
01390                 hit_delete_self[event->wd - 1]++;
01391                 num_delete_self++;
01392         }
01393         if ( IN_UNMOUNT & event->mask ) {
01394                 hit_unmount[event->wd - 1]++;
01395                 num_unmount++;
01396         }
01397         if ( IN_MOVE_SELF & event->mask ) {
01398                 hit_move_self[event->wd - 1]++;
01399                 num_move_self++;
01400         }
01401 
01402         hit_total[event->wd - 1]++;
01403         num_total++;
01404 
01405 }
01406 
01422 int inotifytools_get_stat_by_wd( int wd, int event ) {
01423         niceassert( collect_stats, "stats requested but stats collection not "
01424                                    "enabled" );
01425 
01426         if ( wd < 0 ) return -1;
01427 
01428         if ( IN_ACCESS == event )
01429                 return hit_access[wd - 1];
01430         if ( IN_MODIFY == event )
01431                 return hit_modify[wd - 1];
01432         if ( IN_ATTRIB == event )
01433                 return hit_attrib[wd - 1];
01434         if ( IN_CLOSE_WRITE == event )
01435                 return hit_close_write[wd - 1];
01436         if ( IN_CLOSE_NOWRITE == event )
01437                 return hit_close_nowrite[wd - 1];
01438         if ( IN_OPEN == event )
01439                 return hit_open[wd - 1];
01440         if ( IN_MOVED_FROM == event )
01441                 return hit_moved_from[wd - 1];;
01442         if ( IN_MOVED_TO == event )
01443                 return hit_moved_to[wd - 1];
01444         if ( IN_CREATE == event )
01445                 return hit_create[wd - 1];
01446         if ( IN_DELETE == event )
01447                 return hit_delete[wd - 1];
01448         if ( IN_DELETE_SELF == event )
01449                 return hit_delete_self[wd - 1];
01450         if ( IN_UNMOUNT == event )
01451                 return hit_unmount[wd - 1];
01452         if ( IN_MOVE_SELF == event )
01453                 return hit_move_self[wd - 1];
01454 
01455         if ( 0 == event )
01456                 return hit_total[wd - 1];
01457 
01458         return -1;
01459 }
01460 
01474 int inotifytools_get_stat_total( int event ) {
01475         niceassert( collect_stats, "stats requested but stats collection not "
01476                                    "enabled" );
01477         if ( IN_ACCESS == event )
01478                 return num_access;
01479         if ( IN_MODIFY == event )
01480                 return num_modify;
01481         if ( IN_ATTRIB == event )
01482                 return num_attrib;
01483         if ( IN_CLOSE_WRITE == event )
01484                 return num_close_write;
01485         if ( IN_CLOSE_NOWRITE == event )
01486                 return num_close_nowrite;
01487         if ( IN_OPEN == event )
01488                 return num_open;
01489         if ( IN_MOVED_FROM == event )
01490                 return num_moved_from;
01491         if ( IN_MOVED_TO == event )
01492                 return num_moved_to;
01493         if ( IN_CREATE == event )
01494                 return num_create;
01495         if ( IN_DELETE == event )
01496                 return num_delete;
01497         if ( IN_DELETE_SELF == event )
01498                 return num_delete_self;
01499         if ( IN_UNMOUNT == event )
01500                 return num_unmount;
01501         if ( IN_MOVE_SELF == event )
01502                 return num_move_self;
01503 
01504         if ( 0 == event )
01505                 return num_total;
01506 
01507         return -1;
01508 }
01509 
01529 int inotifytools_get_stat_by_filename( char const * filename,
01530                                                 int event ) {
01531         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01532                filename ), event );
01533 }
01534 
01545 int inotifytools_error() {
01546         return error;
01547 }
01548 
01552 int isdir( char const * path ) {
01553         static struct stat64 my_stat;
01554 
01555         if ( -1 == lstat64( path, &my_stat ) ) {
01556                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01557                 return 0;
01558         }
01559 
01560         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01561 }
01562 
01563 
01570 int inotifytools_get_num_watches() {
01571         return num_watches;
01572 }
01573 
01614 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01615         return inotifytools_fprintf( stdout, event, fmt );
01616 }
01617 
01659 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01660         static char out[MAX_STRLEN+1];
01661         static int ret;
01662         ret = inotifytools_sprintf( out, event, fmt );
01663         if ( -1 != ret ) fprintf( file, "%s", out );
01664         return ret;
01665 }
01666 
01717 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01718         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01719 }
01720 
01721 
01768 int inotifytools_snprintf( char * out, int size,
01769                            struct inotify_event* event, char* fmt ) {
01770         static char * filename, * eventname, * eventstr;
01771         static unsigned int i, ind;
01772         static char ch1;
01773         static char timestr[MAX_STRLEN];
01774         static time_t now;
01775 
01776 
01777         if ( event->len > 0 ) {
01778                 eventname = event->name;
01779         }
01780         else {
01781                 eventname = NULL;
01782         }
01783 
01784 
01785         filename = inotifytools_filename_from_wd( event->wd );
01786 
01787         if ( !fmt || 0 == strlen(fmt) ) {
01788                 error = EINVAL;
01789                 return -1;
01790         }
01791         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01792                 error = EMSGSIZE;
01793                 return -1;
01794         }
01795 
01796         ind = 0;
01797         for ( i = 0; i < strlen(fmt) &&
01798                      (int)ind < size - 1; ++i ) {
01799                 if ( fmt[i] != '%' ) {
01800                         out[ind++] = fmt[i];
01801                         continue;
01802                 }
01803 
01804                 if ( i == strlen(fmt) - 1 ) {
01805                         // last character is %, invalid
01806                         error = EINVAL;
01807                         return ind;
01808                 }
01809 
01810                 ch1 = fmt[i+1];
01811 
01812                 if ( ch1 == '%' ) {
01813                         out[ind++] = '%';
01814                         ++i;
01815                         continue;
01816                 }
01817 
01818                 if ( ch1 == 'w' ) {
01819                         if ( filename ) {
01820                                 strncpy( &out[ind], filename, MAX_STRLEN - ind );
01821                                 ind += strlen(filename);
01822                         }
01823                         ++i;
01824                         continue;
01825                 }
01826 
01827                 if ( ch1 == 'f' ) {
01828                         if ( eventname ) {
01829                                 strncpy( &out[ind], eventname, MAX_STRLEN - ind );
01830                                 ind += strlen(eventname);
01831                         }
01832                         ++i;
01833                         continue;
01834                 }
01835 
01836                 if ( ch1 == 'e' ) {
01837                         eventstr = inotifytools_event_to_str( event->mask );
01838                         strncpy( &out[ind], eventstr, MAX_STRLEN - ind );
01839                         ind += strlen(eventstr);
01840                         ++i;
01841                         continue;
01842                 }
01843 
01844                 if ( ch1 == 'T' ) {
01845 
01846                         if ( timefmt ) {
01847 
01848                                 now = time(0);
01849                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01850                                                     localtime( &now ) ) ) {
01851 
01852                                         // time format probably invalid
01853                                         error = EINVAL;
01854                                         return ind;
01855                                 }
01856                         }
01857                         else {
01858                                 timestr[0] = 0;
01859                         }
01860 
01861                         strncpy( &out[ind], timestr, MAX_STRLEN - ind );
01862                         ind += strlen(timestr);
01863                         ++i;
01864                         continue;
01865                 }
01866 
01867                 // Check if next char in fmt is e
01868                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01869                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01870                         strncpy( &out[ind], eventstr, MAX_STRLEN - ind );
01871                         ind += strlen(eventstr);
01872                         i += 2;
01873                         continue;
01874                 }
01875 
01876                 // OK, this wasn't a special format character, just output it as normal
01877                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01878                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01879                 ++i;
01880         }
01881         out[ind] = 0;
01882 
01883         return ind - 1;
01884 }
01885 
01895 void inotifytools_set_printf_timefmt( char * fmt ) {
01896         timefmt = fmt;
01897 }
01898 
01907 int inotifytools_get_max_queued_events() {
01908         int ret;
01909         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01910         return ret;
01911 }
01912 
01922 int inotifytools_get_max_user_instances() {
01923         int ret;
01924         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01925         return ret;
01926 }
01927 
01937 int inotifytools_get_max_user_watches() {
01938         int ret;
01939         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01940         return ret;
01941 }
01942 
01954 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
01955         if (!pattern) {
01956                 if (regex) {
01957                         regfree(regex);
01958                         free(regex);
01959                         regex = 0;
01960                 }
01961                 return 1;
01962         }
01963 
01964         if (regex) { regfree(regex); }
01965         else       { regex = (regex_t *)malloc(sizeof(regex_t)); }
01966 
01967         int ret = regcomp(regex, pattern, flags | REG_NOSUB);
01968         if (0 == ret) return 1;
01969 
01970         regfree(regex);
01971         free(regex);
01972         regex = 0;
01973         error = EINVAL;
01974         return 0;
01975 }

Generated on Wed Apr 18 22:35:44 2007 for libinotifytools by  doxygen 1.5.1