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
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 }
00852 else {
00853 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00854 "(expected -1 or >0 )", filenames[i], wd );
00855
00856 return 0;
00857 }
00858 }
00859
00860
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 }
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
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
00968
00969 if ( first_byte == bytes ) {
00970 first_byte = 0;
00971 }
00972 else if ( first_byte > bytes ) {
00973
00974
00975
00976
00977
00978 niceassert( (unsigned)((char *)&event[0] +
00979 sizeof(struct inotify_event) +
00980 event[0].len) <= (unsigned)ret,
00981 "extremely unlucky user, death imminent" );
00982
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
01016 error = errno;
01017 return NULL;
01018 }
01019 else if ( rc == 0 ) {
01020
01021 return NULL;
01022 }
01023
01024
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
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
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
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
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
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
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
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 }