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
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
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
00375 if ( !collect_stats ) {
00376 allocatestatmem( 0, watches_buf_size );
00377 }
00378
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
00478 ++event1;
00479
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
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 }
00939 else {
00940 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00941 "(expected -1 or >0 )", filenames[i], wd );
00942
00943 return 0;
00944 }
00945 }
00946
00947
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 }
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
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
01055
01056 if ( first_byte == bytes ) {
01057 first_byte = 0;
01058 }
01059 else if ( first_byte > bytes ) {
01060
01061
01062
01063
01064
01065
01066 niceassert( (int)((char *)&event[0] +
01067 sizeof(struct inotify_event) +
01068 event[0].len) <= (int)ret,
01069 "extremely unlucky user, death imminent" );
01070
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
01104 error = errno;
01105 return NULL;
01106 }
01107 else if ( rc == 0 ) {
01108
01109 return NULL;
01110 }
01111
01112
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
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
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
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
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 }
01298 free( next_file );
01299 }
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
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
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
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
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 }