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 
00731 char * inotifytools_filename_from_wd( int wd ) {
00732         if ( wd <= 0 ) return 0;
00733         niceassert( init, "inotifytools_initialize not called yet" );
00734         return watches[wd-1];
00735 }
00736 
00748 int inotifytools_wd_from_filename( char const * filename ) {
00749         niceassert( filename, 0 );
00750         niceassert( init, "inotifytools_initialize not called yet" );
00751         static int i;
00752         for ( i = 0; i < WATCHES_SIZE; ++i ) {
00753                 if ( watches[i] && 0 == strcmp( filename, watches[i] ) ) {
00754                         return i+1;
00755                 }
00756         }
00757         return -1;
00758 }
00759 
00768 int inotifytools_remove_watch_by_filename( char const * filename ) {
00769         return inotifytools_remove_watch_by_wd( inotifytools_wd_from_filename(
00770                filename ) );
00771 }
00772 
00784 int inotifytools_remove_watch_by_wd( int wd ) {
00785         niceassert( wd > 0, 0 );
00786         niceassert( watches[wd - 1], "No watch exists for requested wd" );
00787         niceassert( init, "inotifytools_initialize not called yet" );
00788         error = 0;
00789         static int status;
00790         status = inotify_rm_watch( inotify_fd, wd );
00791         if ( status < 0 ) {
00792                 fprintf(stderr, "Failed to remove watch on %s: %s\n", watches[wd - 1],
00793                         strerror(status) );
00794                 error = status;
00795                 return 0;
00796         }
00797         free( watches[wd - 1] );
00798         watches[wd - 1] = NULL;
00799         num_watches--;
00800         if ( (int)num_watches < (int)watches_buf_size - (int)WATCHES_SIZE - 1 ) {
00801                 resize_buffers( -WATCHES_SIZE );
00802         }
00803         return 1;
00804 }
00805 
00817 int inotifytools_watch_file( char const * filename, int events ) {
00818         static char const * filenames[2];
00819         filenames[0] = filename;
00820         filenames[1] = NULL;
00821         return inotifytools_watch_files( filenames, events );
00822 }
00823 
00839 int inotifytools_watch_files( char const * filenames[], int events ) {
00840         niceassert( init, "inotifytools_initialize not called yet" );
00841         error = 0;
00842 
00843         static int i;
00844         for ( i = 0; filenames[i]; ++i ) {
00845                 static int wd;
00846                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00847                 if ( wd < 0 ) {
00848                         if ( wd == -1 ) {
00849                                 error = errno;
00850                                 return 0;
00851                         } // if ( wd == -1 )
00852                         else {
00853                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00854                                          "(expected -1 or >0 )", filenames[i], wd );
00855                                 // no appropriate value for error
00856                                 return 0;
00857                         } // else
00858                 } // if ( wd < 0 )
00859 
00860                 // Always end filename with / if it is a directory
00861                 if ( !isdir(filenames[i])
00862                      || filenames[i][strlen(filenames[i])-1] == '/') {
00863                         watches[wd - 1] = strdup(filenames[i]);
00864                 }
00865                 else {
00866                         niceassert(-1!=asprintf( &watches[wd - 1], "%s/", filenames[i] ),0);
00867                 }
00868                 num_watches++;
00869                 if ( num_watches == watches_buf_size ) {
00870                         resize_buffers( WATCHES_SIZE );
00871                 }
00872         } // for
00873 
00874         return 1;
00875 }
00876 
00898 struct inotify_event * inotifytools_next_event( int timeout ) {
00899         return inotifytools_next_events( timeout, 1 );
00900 }
00901 
00902 
00947 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
00948         niceassert( init, "inotifytools_initialize not called yet" );
00949         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
00950 
00951         if ( num_events < 1 ) return NULL;
00952 
00953         static struct inotify_event event[MAX_EVENTS];
00954         static struct inotify_event * ret;
00955         static int first_byte = 0;
00956         static ssize_t bytes;
00957 
00958         error = 0;
00959 
00960         // first_byte is index into event buffer
00961         if ( first_byte != 0
00962           && first_byte <= bytes - sizeof(struct inotify_event) ) {
00963 
00964                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
00965                 first_byte += sizeof(struct inotify_event) + ret->len;
00966 
00967                 // if the pointer to the next event exactly hits end of bytes read,
00968                 // that's good.  next time we're called, we'll read.
00969                 if ( first_byte == bytes ) {
00970                         first_byte = 0;
00971                 }
00972                 else if ( first_byte > bytes ) {
00973                         // oh... no.  this can't be happening.  An incomplete event.
00974                         // Copy what we currently have into first element, call self to
00975                         // read remainder.
00976                         // oh, and they BETTER NOT overlap.
00977                         // Boy I hope this code works.
00978                         niceassert( (unsigned)((char *)&event[0] +
00979                                     sizeof(struct inotify_event) +
00980                                     event[0].len) <= (unsigned)ret,
00981                                     "extremely unlucky user, death imminent" );
00982                         // how much of the event do we have?
00983                         bytes = (char *)&event[0] + bytes - (char *)ret;
00984                         memcpy( &event[0], ret, bytes );
00985                         return inotifytools_next_events( timeout, num_events );
00986                 }
00987                 if ( collect_stats ) {
00988                         record_stats( ret );
00989                 }
00990                 return ret;
00991 
00992         }
00993 
00994         else if ( first_byte == 0 ) {
00995                 bytes = 0;
00996         }
00997 
00998 
00999         static ssize_t this_bytes;
01000         static int bytes_to_read;
01001         static int rc;
01002         static fd_set read_fds;
01003 
01004         static struct timeval read_timeout;
01005         read_timeout.tv_sec = timeout;
01006         read_timeout.tv_usec = 0;
01007         static struct timeval * read_timeout_ptr;
01008         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01009 
01010         FD_ZERO(&read_fds);
01011         FD_SET(inotify_fd, &read_fds);
01012         rc = select(inotify_fd + 1, &read_fds,
01013                     NULL, NULL, read_timeout_ptr);
01014         if ( rc < 0 ) {
01015                 // error
01016                 error = errno;
01017                 return NULL;
01018         }
01019         else if ( rc == 0 ) {
01020                 // timeout
01021                 return NULL;
01022         }
01023 
01024         // wait until we have enough bytes to read
01025         do {
01026                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01027         } while ( !rc &&
01028                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01029 
01030         if ( rc == -1 ) {
01031                 error = errno;
01032                 return NULL;
01033         }
01034 
01035         this_bytes = read(inotify_fd, &event[0] + bytes,
01036                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01037         if ( this_bytes < 0 ) {
01038                 error = errno;
01039                 return NULL;
01040         }
01041         if ( this_bytes == 0 ) {
01042                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01043                                 "events occurred at once.\n");
01044                 return NULL;
01045         }
01046         if ( this_bytes < bytes_to_read ) {
01047                 fprintf(stderr, "Warning, too few bytes read from inotify!\n" );
01048         }
01049         bytes += this_bytes;
01050 
01051         ret = &event[0];
01052         first_byte = sizeof(struct inotify_event) + ret->len;
01053         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01054                                          "almost certainly screw up." );
01055         if ( first_byte == bytes ) {
01056                 first_byte = 0;
01057         }
01058         if ( collect_stats ) {
01059                 record_stats( ret );
01060         }
01061         return ret;
01062 }
01063 
01089 int inotifytools_watch_recursively( char const * path, int events ) {
01090         niceassert( init, "inotifytools_initialize not called yet" );
01091 
01092         DIR * dir;
01093         char * my_path;
01094         error = 0;
01095         dir = opendir( path );
01096         if ( !dir ) {
01097                 // If not a directory, don't need to do anything special
01098                 if ( errno == ENOTDIR ) {
01099                         return inotifytools_watch_file( path, events );
01100                 }
01101                 else {
01102                         error = errno;
01103                         return 0;
01104                 }
01105         }
01106 
01107         if ( path[strlen(path)-1] != '/' ) {
01108                 niceassert( -1 != asprintf( &my_path, "%s/", path ), 0 );
01109         }
01110         else {
01111                 my_path = (char *)path;
01112         }
01113 
01114         static struct dirent * ent;
01115         char * next_file;
01116         static struct stat64 my_stat;
01117         ent = readdir( dir );
01118         // Watch each directory within this directory
01119         while ( ent ) {
01120                 if ( (0 != strcmp( ent->d_name, "." )) &&
01121                      (0 != strcmp( ent->d_name, ".." )) ) {
01122                         niceassert( -1 != asprintf(&next_file,"%s%s", my_path, ent->d_name),
01123                                     0 );
01124                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01125                                 error = errno;
01126                                 free( next_file );
01127                                 if ( errno != EACCES ) {
01128                                         error = errno;
01129                                         if ( my_path != path ) free( my_path );
01130                                         closedir( dir );
01131                                         return 0;
01132                                 }
01133                         }
01134                         else if ( S_ISDIR( my_stat.st_mode ) &&
01135                                   !S_ISLNK( my_stat.st_mode )) {
01136                                 free( next_file );
01137                                 niceassert( -1 != asprintf(&next_file,"%s%s/", my_path,
01138                                                            ent->d_name), 0 );
01139                                 static int status;
01140                                 status = inotifytools_watch_recursively( next_file, events );
01141                                 // For some errors, we will continue.
01142                                 if ( !status && (EACCES != error) && (ENOENT != error) &&
01143                                      (ELOOP != error) ) {
01144                                         free( next_file );
01145                                         if ( my_path != path ) free( my_path );
01146                                         closedir( dir );
01147                                         return 0;
01148                                 }
01149                                 free( next_file );
01150                         }
01151                         else {
01152                                 free( next_file );
01153                         }
01154                 }
01155                 ent = readdir( dir );
01156                 error = 0;
01157         }
01158 
01159         if ( my_path != path ) free( my_path );
01160         closedir( dir );
01161 
01162         return inotifytools_watch_file( path, events );
01163 }
01164 
01168 void record_stats( struct inotify_event const * event ) {
01169 
01170         if ( IN_ACCESS & event->mask ) {
01171                 hit_access[event->wd - 1]++;
01172                 num_access++;
01173         }
01174         if ( IN_MODIFY & event->mask ) {
01175                 hit_modify[event->wd - 1]++;
01176                 num_modify++;
01177         }
01178         if ( IN_ATTRIB & event->mask ) {
01179                 hit_attrib[event->wd - 1]++;
01180                 num_attrib++;
01181         }
01182         if ( IN_CLOSE_WRITE & event->mask ) {
01183                 hit_close_write[event->wd - 1]++;
01184                 num_close_write++;
01185         }
01186         if ( IN_CLOSE_NOWRITE & event->mask ) {
01187                 hit_close_nowrite[event->wd - 1]++;
01188                 num_close_nowrite++;
01189         }
01190         if ( IN_OPEN & event->mask ) {
01191                 hit_open[event->wd - 1]++;
01192                 num_open++;
01193         }
01194         if ( IN_MOVED_FROM & event->mask ) {
01195                 hit_moved_from[event->wd - 1]++;;
01196                 num_moved_from++;
01197         }
01198         if ( IN_MOVED_TO & event->mask ) {
01199                 hit_moved_to[event->wd - 1]++;
01200                 num_moved_to++;
01201         }
01202         if ( IN_CREATE & event->mask ) {
01203                 hit_create[event->wd - 1]++;
01204                 num_create++;
01205         }
01206         if ( IN_DELETE & event->mask ) {
01207                 hit_delete[event->wd - 1]++;
01208                 num_delete++;
01209         }
01210         if ( IN_DELETE_SELF & event->mask ) {
01211                 hit_delete_self[event->wd - 1]++;
01212                 num_delete_self++;
01213         }
01214         if ( IN_UNMOUNT & event->mask ) {
01215                 hit_unmount[event->wd - 1]++;
01216                 num_unmount++;
01217         }
01218         if ( IN_MOVE_SELF & event->mask ) {
01219                 hit_move_self[event->wd - 1]++;
01220                 num_move_self++;
01221         }
01222 
01223         hit_total[event->wd - 1]++;
01224         num_total++;
01225 
01226 }
01227 
01243 int inotifytools_get_stat_by_wd( int wd, int event ) {
01244         niceassert( collect_stats, "stats requested but stats collection not "
01245                                    "enabled" );
01246 
01247         if ( wd < 0 ) return -1;
01248 
01249         if ( IN_ACCESS == event )
01250                 return hit_access[wd - 1];
01251         if ( IN_MODIFY == event )
01252                 return hit_modify[wd - 1];
01253         if ( IN_ATTRIB == event )
01254                 return hit_attrib[wd - 1];
01255         if ( IN_CLOSE_WRITE == event )
01256                 return hit_close_write[wd - 1];
01257         if ( IN_CLOSE_NOWRITE == event )
01258                 return hit_close_nowrite[wd - 1];
01259         if ( IN_OPEN == event )
01260                 return hit_open[wd - 1];
01261         if ( IN_MOVED_FROM == event )
01262                 return hit_moved_from[wd - 1];;
01263         if ( IN_MOVED_TO == event )
01264                 return hit_moved_to[wd - 1];
01265         if ( IN_CREATE == event )
01266                 return hit_create[wd - 1];
01267         if ( IN_DELETE == event )
01268                 return hit_delete[wd - 1];
01269         if ( IN_DELETE_SELF == event )
01270                 return hit_delete_self[wd - 1];
01271         if ( IN_UNMOUNT == event )
01272                 return hit_unmount[wd - 1];
01273         if ( IN_MOVE_SELF == event )
01274                 return hit_move_self[wd - 1];
01275 
01276         if ( 0 == event )
01277                 return hit_total[wd - 1];
01278 
01279         return -1;
01280 }
01281 
01295 int inotifytools_get_stat_total( int event ) {
01296         niceassert( collect_stats, "stats requested but stats collection not "
01297                                    "enabled" );
01298         if ( IN_ACCESS == event )
01299                 return num_access;
01300         if ( IN_MODIFY == event )
01301                 return num_modify;
01302         if ( IN_ATTRIB == event )
01303                 return num_attrib;
01304         if ( IN_CLOSE_WRITE == event )
01305                 return num_close_write;
01306         if ( IN_CLOSE_NOWRITE == event )
01307                 return num_close_nowrite;
01308         if ( IN_OPEN == event )
01309                 return num_open;
01310         if ( IN_MOVED_FROM == event )
01311                 return num_moved_from;
01312         if ( IN_MOVED_TO == event )
01313                 return num_moved_to;
01314         if ( IN_CREATE == event )
01315                 return num_create;
01316         if ( IN_DELETE == event )
01317                 return num_delete;
01318         if ( IN_DELETE_SELF == event )
01319                 return num_delete_self;
01320         if ( IN_UNMOUNT == event )
01321                 return num_unmount;
01322         if ( IN_MOVE_SELF == event )
01323                 return num_move_self;
01324 
01325         if ( 0 == event )
01326                 return num_total;
01327 
01328         return -1;
01329 }
01330 
01347 int inotifytools_get_stat_by_filename( char const * filename,
01348                                                 int event ) {
01349         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01350                filename ), event );
01351 }
01352 
01363 int inotifytools_error() {
01364         return error;
01365 }
01366 
01370 int isdir( char const * path ) {
01371         static struct stat64 my_stat;
01372 
01373         if ( -1 == lstat64( path, &my_stat ) ) {
01374                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01375                 return 0;
01376         }
01377 
01378         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01379 }
01380 
01381 
01388 int inotifytools_get_num_watches() {
01389         return num_watches;
01390 }
01391 
01432 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01433         return inotifytools_fprintf( stdout, event, fmt );
01434 }
01435 
01477 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01478         static char out[MAX_STRLEN+1];
01479         static int ret;
01480         ret = inotifytools_sprintf( out, event, fmt );
01481         if ( -1 != ret ) fprintf( file, "%s", out );
01482         return ret;
01483 }
01484 
01535 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01536         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01537 }
01538 
01539 
01586 int inotifytools_snprintf( char * out, int size,
01587                            struct inotify_event* event, char* fmt ) {
01588         static char * filename, * eventname, * eventstr;
01589         static unsigned int i, ind;
01590         static char ch1;
01591         static char timestr[MAX_STRLEN];
01592         static time_t now;
01593 
01594 
01595         if ( event->len > 0 ) {
01596                 eventname = event->name;
01597         }
01598         else {
01599                 eventname = NULL;
01600         }
01601 
01602 
01603         filename = inotifytools_filename_from_wd( event->wd );
01604 
01605         if ( !fmt || 0 == strlen(fmt) ) {
01606                 error = EINVAL;
01607                 return -1;
01608         }
01609         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01610                 error = EMSGSIZE;
01611                 return -1;
01612         }
01613 
01614         ind = 0;
01615         for ( i = 0; i < strlen(fmt) && ind < size - 1; ++i ) {
01616                 if ( fmt[i] != '%' ) {
01617                         out[ind++] = fmt[i];
01618                         continue;
01619                 }
01620 
01621                 if ( i == strlen(fmt) - 1 ) {
01622                         // last character is %, invalid
01623                         error = EINVAL;
01624                         return ind;
01625                 }
01626 
01627                 ch1 = fmt[i+1];
01628 
01629                 if ( ch1 == '%' ) {
01630                         out[ind++] = '%';
01631                         ++i;
01632                         continue;
01633                 }
01634 
01635                 if ( ch1 == 'w' ) {
01636                         if ( filename ) {
01637                                 strncpy( &out[ind], filename, size - ind );
01638                                 ind += strlen(filename);
01639                         }
01640                         ++i;
01641                         continue;
01642                 }
01643 
01644                 if ( ch1 == 'f' ) {
01645                         if ( eventname ) {
01646                                 strncpy( &out[ind], eventname, size - ind );
01647                                 ind += strlen(eventname);
01648                         }
01649                         ++i;
01650                         continue;
01651                 }
01652 
01653                 if ( ch1 == 'e' ) {
01654                         eventstr = inotifytools_event_to_str( event->mask );
01655                         strncpy( &out[ind], eventstr, size - ind );
01656                         ind += strlen(eventstr);
01657                         ++i;
01658                         continue;
01659                 }
01660 
01661                 if ( ch1 == 'T' ) {
01662 
01663                         if ( timefmt ) {
01664 
01665                                 now = time(0);
01666                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01667                                                     localtime( &now ) ) ) {
01668 
01669                                         // time format probably invalid
01670                                         error = EINVAL;
01671                                         return ind;
01672                                 }
01673                         }
01674                         else {
01675                                 timestr[0] = 0;
01676                         }
01677 
01678                         strncpy( &out[ind], timestr, size - ind );
01679                         ind += strlen(timestr);
01680                         ++i;
01681                         continue;
01682                 }
01683 
01684                 // Check if next char in fmt is e
01685                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01686                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01687                         strncpy( &out[ind], eventstr, size - ind );
01688                         ind += strlen(eventstr);
01689                         i += 2;
01690                         continue;
01691                 }
01692 
01693                 // OK, this wasn't a special format character, just output it as normal
01694                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01695                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01696                 ++i;
01697         }
01698         out[ind] = 0;
01699 
01700         return ind - 1;
01701 }
01702 
01712 void inotifytools_set_printf_timefmt( char * fmt ) {
01713         timefmt = fmt;
01714 }
01715 
01724 int inotifytools_get_max_queued_events() {
01725         int ret;
01726         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01727         return ret;
01728 }
01729 
01739 int inotifytools_get_max_user_instances() {
01740         int ret;
01741         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01742         return ret;
01743 }
01744 
01754 int inotifytools_get_max_user_watches() {
01755         int ret;
01756         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01757         return ret;
01758 }

Generated on Fri Dec 28 00:52:21 2007 for libinotifytools by  doxygen 1.5.1