00001
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
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
00380 if ( !collect_stats ) {
00381 allocatestatmem( 0, watches_buf_size );
00382 }
00383
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
00483 ++event1;
00484
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
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 }
00944 else {
00945 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00946 "(expected -1 or >0 )", filenames[i], wd );
00947
00948 return 0;
00949 }
00950 }
00951
00952
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 }
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
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
01087
01088 if ( first_byte == bytes ) {
01089 first_byte = 0;
01090 }
01091 else if ( first_byte > bytes ) {
01092
01093
01094
01095
01096
01097
01098 niceassert( (long)((char *)&event[0] +
01099 sizeof(struct inotify_event) +
01100 event[0].len) <= (long)ret,
01101 "extremely unlucky user, death imminent" );
01102
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
01136 error = errno;
01137 return NULL;
01138 }
01139 else if ( rc == 0 ) {
01140
01141 return NULL;
01142 }
01143
01144
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
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
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
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
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 }
01328 free( next_file );
01329 }
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
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
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
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
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 }