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 
00031 #include "inotifytools/inotify.h"
00032 
00128 #define MAX_EVENTS 4096
00129 #define MAX_STRLEN 4096
00130 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00131 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00132 #define QUEUE_SIZE_PATH   INOTIFY_PROCDIR "max_queued_watches"
00133 #define INSTANCES_PATH    INOTIFY_PROCDIR "max_user_instances"
00134 
00135 static int inotify_fd;
00136 static unsigned int * hit_access = 0;
00137 static unsigned int * hit_modify = 0;
00138 static unsigned int * hit_attrib = 0;
00139 static unsigned int * hit_close_nowrite = 0;
00140 static unsigned int * hit_close_write = 0;
00141 static unsigned int * hit_open = 0;
00142 static unsigned int * hit_move_self = 0;
00143 static unsigned int * hit_moved_from = 0;
00144 static unsigned int * hit_moved_to = 0;
00145 static unsigned int * hit_create = 0;
00146 static unsigned int * hit_delete = 0;
00147 static unsigned int * hit_delete_self = 0;
00148 static unsigned int * hit_unmount = 0;
00149 static unsigned int * hit_total = 0;
00150 static unsigned int num_access;
00151 static unsigned int num_modify;
00152 static unsigned int num_attrib;
00153 static unsigned int num_close_nowrite;
00154 static unsigned int num_close_write;
00155 static unsigned int num_open;
00156 static unsigned int num_move_self;
00157 static unsigned int num_moved_to;
00158 static unsigned int num_moved_from;
00159 static unsigned int num_create;
00160 static unsigned int num_delete;
00161 static unsigned int num_delete_self;
00162 static unsigned int num_unmount;
00163 static unsigned int num_total;
00164 static int collect_stats = 0;
00165 static int WATCHES_SIZE;
00166 static char ** watches;
00167 static int error;
00168 static unsigned int num_watches;
00169 static unsigned int watches_buf_size;
00170 static int init = 0;
00171 static char * timefmt = 0;
00172 
00173 void allocatestatmem( int from, int to );
00174 int isdir( char const * path );
00175 void record_stats( struct inotify_event const * event );
00176 int onestr_to_event(char const * event);
00177 
00195 #define niceassert(cond,mesg) _niceassert((int)cond, __LINE__, __FILE__, \
00196                                           #cond, mesg)
00197 
00215 void _niceassert( int cond, int line, char const * file, char const * condstr,
00216                   char const * mesg ) {
00217         if ( cond ) return;
00218 
00219         if ( mesg ) {
00220                 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00221                         condstr, mesg );
00222         }
00223         else {
00224                 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00225         }
00226 }
00227 
00242 void * recalloc( void * ptr, size_t from, size_t to ) {
00243         static void * ret;
00244         ret = realloc( ptr, to );
00245         niceassert( ret, "out of memory" );
00246         if ( ret && to > from ) {
00247                 memset( ret + from, 0, (to - from) );
00248         }
00249         return ret;
00250 }
00251 
00258 void resize_buffers( int diff ) {
00259         static int oldsize;
00260         oldsize = watches_buf_size;
00261         watches_buf_size += diff;
00262         watches = (char **)recalloc( watches, oldsize*sizeof(char *),
00263                                      watches_buf_size*sizeof(char *) );
00264         if ( collect_stats ) allocatestatmem( oldsize*sizeof(char *),
00265                                               watches_buf_size*sizeof(char *) );
00266 }
00267 
00277 char * chrtostr(char ch) {
00278         static char str[2] = { '\0', '\0' };
00279         str[0] = ch;
00280         return str;
00281 }
00282 
00286 int read_num_from_file( char * filename, int * num ) {
00287         FILE * file = fopen( filename, "r" );
00288         if ( !file ) {
00289                 error = errno;
00290                 return 0;
00291         }
00292 
00293         if ( EOF == fscanf( file, "%d", num ) ) {
00294                 error = errno;
00295                 return 0;
00296         }
00297 
00298         niceassert( 0 == fclose( file ), 0 );
00299 
00300         return 1;
00301 }
00302 
00312 int inotifytools_initialize() {
00313         error = 0;
00314         // Try to initialise inotify
00315         inotify_fd = inotify_init();
00316         if (inotify_fd < 0)     {
00317                 error = inotify_fd;
00318                 return 0;
00319         }
00320 
00321         if ( !read_num_from_file( WATCHES_SIZE_PATH, &WATCHES_SIZE ) ) return 0;
00322 
00323         watches_buf_size = WATCHES_SIZE;
00324         watches = (char **)calloc( watches_buf_size, sizeof(char *) );
00325         niceassert( watches, "out of memory, gosh darn it" );
00326 
00327         collect_stats = 0;
00328         num_watches = 0;
00329         init = 1;
00330 
00331         return 1;
00332 }
00333 
00342 void allocatestatmem( int from, int to ) {
00343         hit_access = (unsigned int *)recalloc( hit_access, from, to );
00344         hit_modify = (unsigned int *)recalloc( hit_modify, from, to );
00345         hit_attrib = (unsigned int *)recalloc( hit_attrib, from, to );
00346         hit_close_nowrite = (unsigned int *)recalloc( hit_close_nowrite, from, to );
00347         hit_close_write = (unsigned int *)recalloc( hit_close_write, from, to );
00348         hit_open = (unsigned int *)recalloc( hit_open, from, to );
00349         hit_move_self = (unsigned int *)recalloc( hit_move_self, from, to );
00350         hit_moved_from = (unsigned int *)recalloc( hit_moved_from, from, to );
00351         hit_moved_to = (unsigned int *)recalloc( hit_moved_to, from, to );
00352         hit_create = (unsigned int *)recalloc( hit_create, from, to );
00353         hit_delete = (unsigned int *)recalloc( hit_delete, from, to );
00354         hit_delete_self = (unsigned int *)recalloc( hit_delete_self, from, to );
00355         hit_unmount = (unsigned int *)recalloc( hit_unmount, from, to );
00356         hit_total = (unsigned int *)recalloc( hit_total, from, to );
00357 }
00358 
00371 void inotifytools_initialize_stats() {
00372         niceassert( init, "inotifytools_initialize not called yet" );
00373 
00374         // if not already collecting stats, allocate buffer
00375         if ( !collect_stats ) {
00376                 allocatestatmem( 0, watches_buf_size );
00377         }
00378         // if already collecting stats, reset stats
00379         else {
00380                 memset( hit_access, 0, watches_buf_size );
00381                 memset( hit_modify, 0, watches_buf_size );
00382                 memset( hit_attrib, 0, watches_buf_size );
00383                 memset( hit_close_nowrite, 0, watches_buf_size );
00384                 memset( hit_close_write, 0, watches_buf_size );
00385                 memset( hit_open, 0, watches_buf_size );
00386                 memset( hit_move_self, 0, watches_buf_size );
00387                 memset( hit_moved_from, 0, watches_buf_size );
00388                 memset( hit_moved_to, 0, watches_buf_size );
00389                 memset( hit_create, 0, watches_buf_size );
00390                 memset( hit_delete, 0, watches_buf_size );
00391                 memset( hit_delete_self, 0, watches_buf_size );
00392                 memset( hit_unmount, 0, watches_buf_size );
00393                 memset( hit_total, 0, watches_buf_size );
00394         }
00395 
00396         num_access = 0;
00397         num_modify = 0;
00398         num_attrib = 0;
00399         num_close_nowrite = 0;
00400         num_close_write = 0;
00401         num_open = 0;
00402         num_move_self = 0;
00403         num_moved_from = 0;
00404         num_moved_to = 0;
00405         num_create = 0;
00406         num_delete = 0;
00407         num_delete_self = 0;
00408         num_unmount = 0;
00409         num_total = 0;
00410 
00411         collect_stats = 1;
00412 }
00413 
00441 int inotifytools_str_to_event_sep(char const * event, char sep) {
00442         if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00443                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00444                 return -1;
00445         }
00446 
00447         int ret, ret1, len;
00448         char * event1, * event2;
00449         char eventstr[4096];
00450         ret = 0;
00451 
00452         if ( !event || !event[0] ) return 0;
00453 
00454         event1 = (char *)event;
00455         event2 = strchr( event1, sep );
00456         while ( event1 && event1[0] ) {
00457                 if ( event2 ) {
00458                         len = event2 - event1;
00459                         niceassert( len < 4096, "malformed event string (very long)" );
00460                 }
00461                 else {
00462                         len = strlen(event1);
00463                 }
00464                 if ( len > 4095 ) len = 4095;
00465                 strncpy( eventstr, event1, len );
00466                 eventstr[len] = 0;
00467 
00468                 ret1 = onestr_to_event( eventstr );
00469                 if ( 0 == ret1 || -1 == ret1 ) {
00470                         ret = ret1;
00471                         break;
00472                 }
00473                 ret |= ret1;
00474 
00475                 event1 = event2;
00476                 if ( event1 && event1[0] ) {
00477                         // jump over 'sep' character
00478                         ++event1;
00479                         // if last character was 'sep'...
00480                         if ( !event1[0] ) return 0;
00481                         event2 = strchr( event1, sep );
00482                 }
00483         }
00484 
00485         return ret;
00486 }
00487 
00511 int inotifytools_str_to_event(char const * event) {
00512         return inotifytools_str_to_event_sep( event, ',' );
00513 }
00514 
00526 int onestr_to_event(char const * event)
00527 {
00528         static int ret;
00529         ret = -1;
00530 
00531         if ( !event || !event[0] )
00532                 ret = 0;
00533         else if ( 0 == strcasecmp(event, "ACCESS") )
00534                 ret = IN_ACCESS;
00535         else if ( 0 == strcasecmp(event, "MODIFY") )
00536                 ret = IN_MODIFY;
00537         else if ( 0 == strcasecmp(event, "ATTRIB") )
00538                 ret = IN_ATTRIB;
00539         else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00540                 ret = IN_CLOSE_WRITE;
00541         else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00542                 ret = IN_CLOSE_NOWRITE;
00543         else if ( 0 == strcasecmp(event, "OPEN") )
00544                 ret = IN_OPEN;
00545         else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00546                 ret = IN_MOVED_FROM;
00547         else if ( 0 == strcasecmp(event, "MOVED_TO") )
00548                 ret = IN_MOVED_TO;
00549         else if ( 0 == strcasecmp(event, "CREATE") )
00550                 ret = IN_CREATE;
00551         else if ( 0 == strcasecmp(event, "DELETE") )
00552                 ret = IN_DELETE;
00553         else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00554                 ret = IN_DELETE_SELF;
00555         else if ( 0 == strcasecmp(event, "UNMOUNT") )
00556                 ret = IN_UNMOUNT;
00557         else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00558                 ret = IN_Q_OVERFLOW;
00559         else if ( 0 == strcasecmp(event, "IGNORED") )
00560                 ret = IN_IGNORED;
00561         else if ( 0 == strcasecmp(event, "CLOSE") )
00562                 ret = IN_CLOSE;
00563         else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00564                 ret = IN_MOVE_SELF;
00565         else if ( 0 == strcasecmp(event, "MOVE") )
00566                 ret = IN_MOVE;
00567         else if ( 0 == strcasecmp(event, "ISDIR") )
00568                 ret = IN_ISDIR;
00569         else if ( 0 == strcasecmp(event, "ONESHOT") )
00570                 ret = IN_ONESHOT;
00571         else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00572                 ret = IN_ALL_EVENTS;
00573 
00574         return ret;
00575 }
00576 
00598 char * inotifytools_event_to_str(int events) {
00599         return inotifytools_event_to_str_sep(events, ',');
00600 }
00601 
00626 char * inotifytools_event_to_str_sep(int events, char sep)
00627 {
00628         static char ret[1024];
00629         ret[0] = '\0';
00630         ret[1] = '\0';
00631 
00632         if ( IN_ACCESS & events ) {
00633                 strcat( ret, chrtostr(sep) );
00634                 strcat( ret, "ACCESS" );
00635         }
00636         if ( IN_MODIFY & events ) {
00637                 strcat( ret, chrtostr(sep) );
00638                 strcat( ret, "MODIFY" );
00639         }
00640         if ( IN_ATTRIB & events ) {
00641                 strcat( ret, chrtostr(sep) );
00642                 strcat( ret, "ATTRIB" );
00643         }
00644         if ( IN_CLOSE_WRITE & events ) {
00645                 strcat( ret, chrtostr(sep) );
00646                 strcat( ret, "CLOSE_WRITE" );
00647         }
00648         if ( IN_CLOSE_NOWRITE & events ) {
00649                 strcat( ret, chrtostr(sep) );
00650                 strcat( ret, "CLOSE_NOWRITE" );
00651         }
00652         if ( IN_OPEN & events ) {
00653                 strcat( ret, chrtostr(sep) );
00654                 strcat( ret, "OPEN" );
00655         }
00656         if ( IN_MOVED_FROM & events ) {
00657                 strcat( ret, chrtostr(sep) );
00658                 strcat( ret, "MOVED_FROM" );
00659         }
00660         if ( IN_MOVED_TO & events ) {
00661                 strcat( ret, chrtostr(sep) );
00662                 strcat( ret, "MOVED_TO" );
00663         }
00664         if ( IN_CREATE & events ) {
00665                 strcat( ret, chrtostr(sep) );
00666                 strcat( ret, "CREATE" );
00667         }
00668         if ( IN_DELETE & events ) {
00669                 strcat( ret, chrtostr(sep) );
00670                 strcat( ret, "DELETE" );
00671         }
00672         if ( IN_DELETE_SELF & events ) {
00673                 strcat( ret, chrtostr(sep) );
00674                 strcat( ret, "DELETE_SELF" );
00675         }
00676         if ( IN_UNMOUNT & events ) {
00677                 strcat( ret, chrtostr(sep) );
00678                 strcat( ret, "UNMOUNT" );
00679         }
00680         if ( IN_Q_OVERFLOW & events ) {
00681                 strcat( ret, chrtostr(sep) );
00682                 strcat( ret, "Q_OVERFLOW" );
00683         }
00684         if ( IN_IGNORED & events ) {
00685                 strcat( ret, chrtostr(sep) );
00686                 strcat( ret, "IGNORED" );
00687         }
00688         if ( IN_CLOSE & events ) {
00689                 strcat( ret, chrtostr(sep) );
00690                 strcat( ret, "CLOSE" );
00691         }
00692         if ( IN_MOVE_SELF & events ) {
00693                 strcat( ret, chrtostr(sep) );
00694                 strcat( ret, "MOVE_SELF" );
00695         }
00696         if ( IN_ISDIR & events ) {
00697                 strcat( ret, chrtostr(sep) );
00698                 strcat( ret, "ISDIR" );
00699         }
00700         if ( IN_ONESHOT & events ) {
00701                 strcat( ret, chrtostr(sep) );
00702                 strcat( ret, "ONESHOT" );
00703         }
00704 
00705         // Maybe we didn't match any... ?
00706         if (ret[0] == '\0') {
00707                 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00708         }
00709 
00710         return &ret[1];
00711 }
00712 
00733 char * inotifytools_filename_from_wd( int wd ) {
00734         if ( wd <= 0 ) return 0;
00735         niceassert( init, "inotifytools_initialize not called yet" );
00736         return watches[wd-1];
00737 }
00738 
00753 int inotifytools_wd_from_filename( char const * filename ) {
00754         niceassert( filename, 0 );
00755         niceassert( init, "inotifytools_initialize not called yet" );
00756         static int i;
00757         for ( i = 0; i < WATCHES_SIZE; ++i ) {
00758                 if ( watches[i] && 0 == strcmp( filename, watches[i] ) ) {
00759                         return i+1;
00760                 }
00761         }
00762         return -1;
00763 }
00764 
00779 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00780         if ( wd <= 0 ) return;
00781         niceassert( init, "inotifytools_initialize not called yet" );
00782         if ( watches[wd-1] ) free( watches[wd-1] );
00783         watches[wd-1] = strdup(filename);
00784 }
00785 
00800 void inotifytools_set_filename_by_filename( char const * oldname,
00801                                             char const * newname ) {
00802         if ( !oldname ) return;
00803         inotifytools_set_filename_by_wd(
00804           inotifytools_wd_from_filename( oldname ), newname
00805         );
00806 }
00807 
00830 void inotifytools_replace_filename( char const * oldname,
00831                                     char const * newname ) {
00832         if ( !oldname || !newname ) return;
00833         int oldlen = strlen(oldname);
00834         static char * name = 0;
00835         for ( int i = 0; i < WATCHES_SIZE; ++i ) {
00836                 if ( watches[i] && 0 == strncmp( oldname, watches[i], oldlen ) ) {
00837                         asprintf( &name, "%s%s", newname, &(watches[i][oldlen]) );
00838                         free( watches[i] );
00839                         watches[i] = name;
00840                 }
00841         }
00842 }
00843 
00855 int inotifytools_remove_watch_by_filename( char const * filename ) {
00856         return inotifytools_remove_watch_by_wd( inotifytools_wd_from_filename(
00857                filename ) );
00858 }
00859 
00871 int inotifytools_remove_watch_by_wd( int wd ) {
00872         niceassert( wd > 0, 0 );
00873         niceassert( watches[wd - 1], "No watch exists for requested wd" );
00874         niceassert( init, "inotifytools_initialize not called yet" );
00875         error = 0;
00876         static int status;
00877         status = inotify_rm_watch( inotify_fd, wd );
00878         if ( status < 0 ) {
00879                 fprintf(stderr, "Failed to remove watch on %s: %s\n", watches[wd - 1],
00880                         strerror(status) );
00881                 error = status;
00882                 return 0;
00883         }
00884         free( watches[wd - 1] );
00885         watches[wd - 1] = NULL;
00886         num_watches--;
00887         if ( (int)num_watches < (int)watches_buf_size - (int)WATCHES_SIZE - 1 ) {
00888                 resize_buffers( -WATCHES_SIZE );
00889         }
00890         return 1;
00891 }
00892 
00904 int inotifytools_watch_file( char const * filename, int events ) {
00905         static char const * filenames[2];
00906         filenames[0] = filename;
00907         filenames[1] = NULL;
00908         return inotifytools_watch_files( filenames, events );
00909 }
00910 
00926 int inotifytools_watch_files( char const * filenames[], int events ) {
00927         niceassert( init, "inotifytools_initialize not called yet" );
00928         error = 0;
00929 
00930         static int i;
00931         for ( i = 0; filenames[i]; ++i ) {
00932                 static int wd;
00933                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00934                 if ( wd < 0 ) {
00935                         if ( wd == -1 ) {
00936                                 error = errno;
00937                                 return 0;
00938                         } // if ( wd == -1 )
00939                         else {
00940                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00941                                          "(expected -1 or >0 )", filenames[i], wd );
00942                                 // no appropriate value for error
00943                                 return 0;
00944                         } // else
00945                 } // if ( wd < 0 )
00946 
00947                 // Always end filename with / if it is a directory
00948                 if ( !isdir(filenames[i])
00949                      || filenames[i][strlen(filenames[i])-1] == '/') {
00950                         watches[wd - 1] = strdup(filenames[i]);
00951                 }
00952                 else {
00953                         niceassert(-1!=asprintf( &watches[wd - 1], "%s/", filenames[i] ),0);
00954                 }
00955                 num_watches++;
00956                 if ( num_watches == watches_buf_size ) {
00957                         resize_buffers( WATCHES_SIZE );
00958                 }
00959         } // for
00960 
00961         return 1;
00962 }
00963 
00985 struct inotify_event * inotifytools_next_event( int timeout ) {
00986         return inotifytools_next_events( timeout, 1 );
00987 }
00988 
00989 
01034 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01035         niceassert( init, "inotifytools_initialize not called yet" );
01036         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01037 
01038         if ( num_events < 1 ) return NULL;
01039 
01040         static struct inotify_event event[MAX_EVENTS];
01041         static struct inotify_event * ret;
01042         static int first_byte = 0;
01043         static ssize_t bytes;
01044 
01045         error = 0;
01046 
01047         // first_byte is index into event buffer
01048         if ( first_byte != 0
01049           && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01050 
01051                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01052                 first_byte += sizeof(struct inotify_event) + ret->len;
01053 
01054                 // if the pointer to the next event exactly hits end of bytes read,
01055                 // that's good.  next time we're called, we'll read.
01056                 if ( first_byte == bytes ) {
01057                         first_byte = 0;
01058                 }
01059                 else if ( first_byte > bytes ) {
01060                         // oh... no.  this can't be happening.  An incomplete event.
01061                         // Copy what we currently have into first element, call self to
01062                         // read remainder.
01063                         // oh, and they BETTER NOT overlap.
01064                         // Boy I hope this code works.
01065                         // But I think this can never happen due to how inotify is written.
01066                         niceassert( (int)((char *)&event[0] +
01067                                     sizeof(struct inotify_event) +
01068                                     event[0].len) <= (int)ret,
01069                                     "extremely unlucky user, death imminent" );
01070                         // how much of the event do we have?
01071                         bytes = (char *)&event[0] + bytes - (char *)ret;
01072                         memcpy( &event[0], ret, bytes );
01073                         return inotifytools_next_events( timeout, num_events );
01074                 }
01075                 if ( collect_stats ) {
01076                         record_stats( ret );
01077                 }
01078                 return ret;
01079 
01080         }
01081 
01082         else if ( first_byte == 0 ) {
01083                 bytes = 0;
01084         }
01085 
01086 
01087         static ssize_t this_bytes;
01088         static unsigned int bytes_to_read;
01089         static int rc;
01090         static fd_set read_fds;
01091 
01092         static struct timeval read_timeout;
01093         read_timeout.tv_sec = timeout;
01094         read_timeout.tv_usec = 0;
01095         static struct timeval * read_timeout_ptr;
01096         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01097 
01098         FD_ZERO(&read_fds);
01099         FD_SET(inotify_fd, &read_fds);
01100         rc = select(inotify_fd + 1, &read_fds,
01101                     NULL, NULL, read_timeout_ptr);
01102         if ( rc < 0 ) {
01103                 // error
01104                 error = errno;
01105                 return NULL;
01106         }
01107         else if ( rc == 0 ) {
01108                 // timeout
01109                 return NULL;
01110         }
01111 
01112         // wait until we have enough bytes to read
01113         do {
01114                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01115         } while ( !rc &&
01116                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01117 
01118         if ( rc == -1 ) {
01119                 error = errno;
01120                 return NULL;
01121         }
01122 
01123         this_bytes = read(inotify_fd, &event[0] + bytes,
01124                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01125         if ( this_bytes < 0 ) {
01126                 error = errno;
01127                 return NULL;
01128         }
01129         if ( this_bytes == 0 ) {
01130                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01131                                 "events occurred at once.\n");
01132                 return NULL;
01133         }
01134         if ( this_bytes < (int)bytes_to_read ) {
01135                 fprintf(stderr, "Warning, too few bytes read from inotify!\n" );
01136         }
01137         bytes += this_bytes;
01138 
01139         ret = &event[0];
01140         first_byte = sizeof(struct inotify_event) + ret->len;
01141         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01142                                          "almost certainly screw up." );
01143         if ( first_byte == bytes ) {
01144                 first_byte = 0;
01145         }
01146         if ( collect_stats ) {
01147                 record_stats( ret );
01148         }
01149         return ret;
01150 }
01151 
01177 int inotifytools_watch_recursively( char const * path, int events ) {
01178         return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01179 }
01180 
01213 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01214                                                  char const ** exclude_list ) {
01215         niceassert( init, "inotifytools_initialize not called yet" );
01216 
01217         DIR * dir;
01218         char * my_path;
01219         error = 0;
01220         dir = opendir( path );
01221         if ( !dir ) {
01222                 // If not a directory, don't need to do anything special
01223                 if ( errno == ENOTDIR ) {
01224                         return inotifytools_watch_file( path, events );
01225                 }
01226                 else {
01227                         error = errno;
01228                         return 0;
01229                 }
01230         }
01231 
01232         if ( path[strlen(path)-1] != '/' ) {
01233                 niceassert( -1 != asprintf( &my_path, "%s/", path ), 0 );
01234         }
01235         else {
01236                 my_path = (char *)path;
01237         }
01238 
01239         static struct dirent * ent;
01240         char * next_file;
01241         static struct stat64 my_stat;
01242         ent = readdir( dir );
01243         // Watch each directory within this directory
01244         while ( ent ) {
01245                 if ( (0 != strcmp( ent->d_name, "." )) &&
01246                      (0 != strcmp( ent->d_name, ".." )) ) {
01247                         niceassert( -1 != asprintf(&next_file,"%s%s", my_path, ent->d_name),
01248                                     0 );
01249                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01250                                 error = errno;
01251                                 free( next_file );
01252                                 if ( errno != EACCES ) {
01253                                         error = errno;
01254                                         if ( my_path != path ) free( my_path );
01255                                         closedir( dir );
01256                                         return 0;
01257                                 }
01258                         }
01259                         else if ( S_ISDIR( my_stat.st_mode ) &&
01260                                   !S_ISLNK( my_stat.st_mode )) {
01261                                 free( next_file );
01262                                 niceassert( -1 != asprintf(&next_file,"%s%s/", my_path,
01263                                                            ent->d_name), 0 );
01264                                 static unsigned int no_watch;
01265                                 static char const ** exclude_entry;
01266 
01267                                 no_watch = 0;
01268                                 for (exclude_entry = exclude_list;
01269                                          exclude_entry && *exclude_entry && !no_watch;
01270                                          ++exclude_entry) {
01271                                         static int exclude_length;
01272 
01273                                         exclude_length = strlen(*exclude_entry);
01274                                         if ((*exclude_entry)[exclude_length-1] == '/') {
01275                                                 --exclude_length;
01276                                         }
01277                                         if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01278                                             !strncmp(*exclude_entry, next_file, exclude_length)) {
01279                                                 // directory found in exclude list
01280                                                 no_watch = 1;
01281                                         }
01282                                 }
01283                                 if (!no_watch) {
01284                                         static int status;
01285                                         status = inotifytools_watch_recursively_with_exclude(
01286                                                       next_file,
01287                                                       events,
01288                                                       exclude_list );
01289                                         // For some errors, we will continue.
01290                                         if ( !status && (EACCES != error) && (ENOENT != error) &&
01291                                              (ELOOP != error) ) {
01292                                                 free( next_file );
01293                                                 if ( my_path != path ) free( my_path );
01294                                                 closedir( dir );
01295                                                 return 0;
01296                                         }
01297                                 } // if !no_watch
01298                                 free( next_file );
01299                         } // if isdir and not islnk
01300                         else {
01301                                 free( next_file );
01302                         }
01303                 }
01304                 ent = readdir( dir );
01305                 error = 0;
01306         }
01307 
01308         if ( my_path != path ) free( my_path );
01309         closedir( dir );
01310 
01311         return inotifytools_watch_file( path, events );
01312 }
01313 
01317 void record_stats( struct inotify_event const * event ) {
01318 
01319         if ( IN_ACCESS & event->mask ) {
01320                 hit_access[event->wd - 1]++;
01321                 num_access++;
01322         }
01323         if ( IN_MODIFY & event->mask ) {
01324                 hit_modify[event->wd - 1]++;
01325                 num_modify++;
01326         }
01327         if ( IN_ATTRIB & event->mask ) {
01328                 hit_attrib[event->wd - 1]++;
01329                 num_attrib++;
01330         }
01331         if ( IN_CLOSE_WRITE & event->mask ) {
01332                 hit_close_write[event->wd - 1]++;
01333                 num_close_write++;
01334         }
01335         if ( IN_CLOSE_NOWRITE & event->mask ) {
01336                 hit_close_nowrite[event->wd - 1]++;
01337                 num_close_nowrite++;
01338         }
01339         if ( IN_OPEN & event->mask ) {
01340                 hit_open[event->wd - 1]++;
01341                 num_open++;
01342         }
01343         if ( IN_MOVED_FROM & event->mask ) {
01344                 hit_moved_from[event->wd - 1]++;;
01345                 num_moved_from++;
01346         }
01347         if ( IN_MOVED_TO & event->mask ) {
01348                 hit_moved_to[event->wd - 1]++;
01349                 num_moved_to++;
01350         }
01351         if ( IN_CREATE & event->mask ) {
01352                 hit_create[event->wd - 1]++;
01353                 num_create++;
01354         }
01355         if ( IN_DELETE & event->mask ) {
01356                 hit_delete[event->wd - 1]++;
01357                 num_delete++;
01358         }
01359         if ( IN_DELETE_SELF & event->mask ) {
01360                 hit_delete_self[event->wd - 1]++;
01361                 num_delete_self++;
01362         }
01363         if ( IN_UNMOUNT & event->mask ) {
01364                 hit_unmount[event->wd - 1]++;
01365                 num_unmount++;
01366         }
01367         if ( IN_MOVE_SELF & event->mask ) {
01368                 hit_move_self[event->wd - 1]++;
01369                 num_move_self++;
01370         }
01371 
01372         hit_total[event->wd - 1]++;
01373         num_total++;
01374 
01375 }
01376 
01392 int inotifytools_get_stat_by_wd( int wd, int event ) {
01393         niceassert( collect_stats, "stats requested but stats collection not "
01394                                    "enabled" );
01395 
01396         if ( wd < 0 ) return -1;
01397 
01398         if ( IN_ACCESS == event )
01399                 return hit_access[wd - 1];
01400         if ( IN_MODIFY == event )
01401                 return hit_modify[wd - 1];
01402         if ( IN_ATTRIB == event )
01403                 return hit_attrib[wd - 1];
01404         if ( IN_CLOSE_WRITE == event )
01405                 return hit_close_write[wd - 1];
01406         if ( IN_CLOSE_NOWRITE == event )
01407                 return hit_close_nowrite[wd - 1];
01408         if ( IN_OPEN == event )
01409                 return hit_open[wd - 1];
01410         if ( IN_MOVED_FROM == event )
01411                 return hit_moved_from[wd - 1];;
01412         if ( IN_MOVED_TO == event )
01413                 return hit_moved_to[wd - 1];
01414         if ( IN_CREATE == event )
01415                 return hit_create[wd - 1];
01416         if ( IN_DELETE == event )
01417                 return hit_delete[wd - 1];
01418         if ( IN_DELETE_SELF == event )
01419                 return hit_delete_self[wd - 1];
01420         if ( IN_UNMOUNT == event )
01421                 return hit_unmount[wd - 1];
01422         if ( IN_MOVE_SELF == event )
01423                 return hit_move_self[wd - 1];
01424 
01425         if ( 0 == event )
01426                 return hit_total[wd - 1];
01427 
01428         return -1;
01429 }
01430 
01444 int inotifytools_get_stat_total( int event ) {
01445         niceassert( collect_stats, "stats requested but stats collection not "
01446                                    "enabled" );
01447         if ( IN_ACCESS == event )
01448                 return num_access;
01449         if ( IN_MODIFY == event )
01450                 return num_modify;
01451         if ( IN_ATTRIB == event )
01452                 return num_attrib;
01453         if ( IN_CLOSE_WRITE == event )
01454                 return num_close_write;
01455         if ( IN_CLOSE_NOWRITE == event )
01456                 return num_close_nowrite;
01457         if ( IN_OPEN == event )
01458                 return num_open;
01459         if ( IN_MOVED_FROM == event )
01460                 return num_moved_from;
01461         if ( IN_MOVED_TO == event )
01462                 return num_moved_to;
01463         if ( IN_CREATE == event )
01464                 return num_create;
01465         if ( IN_DELETE == event )
01466                 return num_delete;
01467         if ( IN_DELETE_SELF == event )
01468                 return num_delete_self;
01469         if ( IN_UNMOUNT == event )
01470                 return num_unmount;
01471         if ( IN_MOVE_SELF == event )
01472                 return num_move_self;
01473 
01474         if ( 0 == event )
01475                 return num_total;
01476 
01477         return -1;
01478 }
01479 
01499 int inotifytools_get_stat_by_filename( char const * filename,
01500                                                 int event ) {
01501         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01502                filename ), event );
01503 }
01504 
01515 int inotifytools_error() {
01516         return error;
01517 }
01518 
01522 int isdir( char const * path ) {
01523         static struct stat64 my_stat;
01524 
01525         if ( -1 == lstat64( path, &my_stat ) ) {
01526                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01527                 return 0;
01528         }
01529 
01530         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01531 }
01532 
01533 
01540 int inotifytools_get_num_watches() {
01541         return num_watches;
01542 }
01543 
01584 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01585         return inotifytools_fprintf( stdout, event, fmt );
01586 }
01587 
01629 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01630         static char out[MAX_STRLEN+1];
01631         static int ret;
01632         ret = inotifytools_sprintf( out, event, fmt );
01633         if ( -1 != ret ) fprintf( file, "%s", out );
01634         return ret;
01635 }
01636 
01687 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01688         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01689 }
01690 
01691 
01738 int inotifytools_snprintf( char * out, int size,
01739                            struct inotify_event* event, char* fmt ) {
01740         static char * filename, * eventname, * eventstr;
01741         static unsigned int i, ind;
01742         static char ch1;
01743         static char timestr[MAX_STRLEN];
01744         static time_t now;
01745 
01746 
01747         if ( event->len > 0 ) {
01748                 eventname = event->name;
01749         }
01750         else {
01751                 eventname = NULL;
01752         }
01753 
01754 
01755         filename = inotifytools_filename_from_wd( event->wd );
01756 
01757         if ( !fmt || 0 == strlen(fmt) ) {
01758                 error = EINVAL;
01759                 return -1;
01760         }
01761         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01762                 error = EMSGSIZE;
01763                 return -1;
01764         }
01765 
01766         ind = 0;
01767         for ( i = 0; i < strlen(fmt) &&
01768                      (int)ind < size - 1; ++i ) {
01769                 if ( fmt[i] != '%' ) {
01770                         out[ind++] = fmt[i];
01771                         continue;
01772                 }
01773 
01774                 if ( i == strlen(fmt) - 1 ) {
01775                         // last character is %, invalid
01776                         error = EINVAL;
01777                         return ind;
01778                 }
01779 
01780                 ch1 = fmt[i+1];
01781 
01782                 if ( ch1 == '%' ) {
01783                         out[ind++] = '%';
01784                         ++i;
01785                         continue;
01786                 }
01787 
01788                 if ( ch1 == 'w' ) {
01789                         if ( filename ) {
01790                                 strncpy( &out[ind], filename, MAX_STRLEN - ind );
01791                                 ind += strlen(filename);
01792                         }
01793                         ++i;
01794                         continue;
01795                 }
01796 
01797                 if ( ch1 == 'f' ) {
01798                         if ( eventname ) {
01799                                 strncpy( &out[ind], eventname, MAX_STRLEN - ind );
01800                                 ind += strlen(eventname);
01801                         }
01802                         ++i;
01803                         continue;
01804                 }
01805 
01806                 if ( ch1 == 'e' ) {
01807                         eventstr = inotifytools_event_to_str( event->mask );
01808                         strncpy( &out[ind], eventstr, MAX_STRLEN - ind );
01809                         ind += strlen(eventstr);
01810                         ++i;
01811                         continue;
01812                 }
01813 
01814                 if ( ch1 == 'T' ) {
01815 
01816                         if ( timefmt ) {
01817 
01818                                 now = time(0);
01819                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01820                                                     localtime( &now ) ) ) {
01821 
01822                                         // time format probably invalid
01823                                         error = EINVAL;
01824                                         return ind;
01825                                 }
01826                         }
01827                         else {
01828                                 timestr[0] = 0;
01829                         }
01830 
01831                         strncpy( &out[ind], timestr, MAX_STRLEN - ind );
01832                         ind += strlen(timestr);
01833                         ++i;
01834                         continue;
01835                 }
01836 
01837                 // Check if next char in fmt is e
01838                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01839                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01840                         strncpy( &out[ind], eventstr, MAX_STRLEN - ind );
01841                         ind += strlen(eventstr);
01842                         i += 2;
01843                         continue;
01844                 }
01845 
01846                 // OK, this wasn't a special format character, just output it as normal
01847                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01848                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01849                 ++i;
01850         }
01851         out[ind] = 0;
01852 
01853         return ind - 1;
01854 }
01855 
01865 void inotifytools_set_printf_timefmt( char * fmt ) {
01866         timefmt = fmt;
01867 }
01868 
01877 int inotifytools_get_max_queued_events() {
01878         int ret;
01879         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01880         return ret;
01881 }
01882 
01892 int inotifytools_get_max_user_instances() {
01893         int ret;
01894         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01895         return ret;
01896 }
01897 
01907 int inotifytools_get_max_user_watches() {
01908         int ret;
01909         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01910         return ret;
01911 }

Generated on Sun Dec 17 07:12:55 2006 for libinotifytools by  doxygen 1.5.1