00001
00002
00015 #include "../../config.h"
00016 #include "inotifytools/inotifytools.h"
00017 #include "inotifytools_p.h"
00018
00019 #include <string.h>
00020 #include <strings.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <errno.h>
00024 #include <sys/select.h>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/ioctl.h>
00028 #include <unistd.h>
00029 #include <dirent.h>
00030 #include <time.h>
00031 #include <regex.h>
00032 #include <setjmp.h>
00033
00034 #include "inotifytools/inotify.h"
00035
00126 #define MAX_EVENTS 4096
00127 #define MAX_STRLEN 4096
00128 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00129 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00130 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
00131 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
00132
00133 static int inotify_fd;
00134 static unsigned num_access;
00135 static unsigned num_modify;
00136 static unsigned num_attrib;
00137 static unsigned num_close_nowrite;
00138 static unsigned num_close_write;
00139 static unsigned num_open;
00140 static unsigned num_move_self;
00141 static unsigned num_moved_to;
00142 static unsigned num_moved_from;
00143 static unsigned num_create;
00144 static unsigned num_delete;
00145 static unsigned num_delete_self;
00146 static unsigned num_unmount;
00147 static unsigned num_total;
00148 static int collect_stats = 0;
00149
00150 struct rbtree *tree_wd = 0;
00151 struct rbtree *tree_filename = 0;
00152 static int error = 0;
00153 static int init = 0;
00154 static char* timefmt = 0;
00155 static regex_t* regex = 0;
00156
00157 int isdir( char const * path );
00158 void record_stats( struct inotify_event const * event );
00159 int onestr_to_event(char const * event);
00160
00178 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00179 #cond, mesg)
00180
00181 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00182
00200 void _niceassert( long cond, int line, char const * file, char const * condstr,
00201 char const * mesg ) {
00202 if ( cond ) return;
00203
00204 if ( mesg ) {
00205 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00206 condstr, mesg );
00207 }
00208 else {
00209 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00210 }
00211 }
00212
00222 char * chrtostr(char ch) {
00223 static char str[2] = { '\0', '\0' };
00224 str[0] = ch;
00225 return str;
00226 }
00227
00231 int read_num_from_file( char * filename, int * num ) {
00232 FILE * file = fopen( filename, "r" );
00233 if ( !file ) {
00234 error = errno;
00235 return 0;
00236 }
00237
00238 if ( EOF == fscanf( file, "%d", num ) ) {
00239 error = errno;
00240 return 0;
00241 }
00242
00243 niceassert( 0 == fclose( file ), 0 );
00244
00245 return 1;
00246 }
00247
00248 int wd_compare(const void *d1, const void *d2, const void *config) {
00249 if (!d1 || !d2) return d1 - d2;
00250 return ((watch*)d1)->wd - ((watch*)d2)->wd;
00251 }
00252
00253 int filename_compare(const void *d1, const void *d2, const void *config) {
00254 if (!d1 || !d2) return d1 - d2;
00255 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
00256 }
00257
00261 watch *watch_from_wd( int wd ) {
00262 watch w;
00263 w.wd = wd;
00264 return (watch*)rbfind(&w, tree_wd);
00265 }
00266
00270 watch *watch_from_filename( char const *filename ) {
00271 watch w;
00272 w.filename = (char*)filename;
00273 return (watch*)rbfind(&w, tree_filename);
00274 }
00275
00285 int inotifytools_initialize() {
00286 if (init) return 1;
00287
00288 error = 0;
00289
00290 inotify_fd = inotify_init();
00291 if (inotify_fd < 0) {
00292 error = inotify_fd;
00293 return 0;
00294 }
00295
00296 collect_stats = 0;
00297 init = 1;
00298 tree_wd = rbinit(wd_compare, 0);
00299 tree_filename = rbinit(filename_compare, 0);
00300
00301 return 1;
00302 }
00303
00307 void empty_stats(const void *nodep,
00308 const VISIT which,
00309 const int depth, void *arg) {
00310 if (which != endorder && which != leaf) return;
00311 watch *w = (watch*)nodep;
00312 w->hit_access = 0;
00313 w->hit_modify = 0;
00314 w->hit_attrib = 0;
00315 w->hit_close_nowrite = 0;
00316 w->hit_close_write = 0;
00317 w->hit_open = 0;
00318 w->hit_move_self = 0;
00319 w->hit_moved_from = 0;
00320 w->hit_moved_to = 0;
00321 w->hit_create = 0;
00322 w->hit_delete = 0;
00323 w->hit_delete_self = 0;
00324 w->hit_unmount = 0;
00325 w->hit_total = 0;
00326 }
00327
00331 void replace_filename(const void *nodep,
00332 const VISIT which,
00333 const int depth, void *arg) {
00334 if (which != endorder && which != leaf) return;
00335 watch *w = (watch*)nodep;
00336 char *old_name = ((char**)arg)[0];
00337 char *new_name = ((char**)arg)[1];
00338 int old_len = *((int*)&((char**)arg)[2]);
00339 char *name;
00340 if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
00341 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
00342 if (!strcmp( w->filename, new_name )) {
00343 free(name);
00344 } else {
00345 rbdelete(w, tree_filename);
00346 free( w->filename );
00347 w->filename = name;
00348 rbsearch(w, tree_filename);
00349 }
00350 }
00351 }
00352
00356 void get_num(const void *nodep,
00357 const VISIT which,
00358 const int depth, void *arg) {
00359 if (which != endorder && which != leaf) return;
00360 ++(*((int*)arg));
00361 }
00362
00363
00376 void inotifytools_initialize_stats() {
00377 niceassert( init, "inotifytools_initialize not called yet" );
00378
00379
00380 if (collect_stats) {
00381 rbwalk(tree_wd, empty_stats, 0);
00382 }
00383
00384 num_access = 0;
00385 num_modify = 0;
00386 num_attrib = 0;
00387 num_close_nowrite = 0;
00388 num_close_write = 0;
00389 num_open = 0;
00390 num_move_self = 0;
00391 num_moved_from = 0;
00392 num_moved_to = 0;
00393 num_create = 0;
00394 num_delete = 0;
00395 num_delete_self = 0;
00396 num_unmount = 0;
00397 num_total = 0;
00398
00399 collect_stats = 1;
00400 }
00401
00429 int inotifytools_str_to_event_sep(char const * event, char sep) {
00430 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00431 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00432 return -1;
00433 }
00434
00435 int ret, ret1, len;
00436 char * event1, * event2;
00437 char eventstr[4096];
00438 ret = 0;
00439
00440 if ( !event || !event[0] ) return 0;
00441
00442 event1 = (char *)event;
00443 event2 = strchr( event1, sep );
00444 while ( event1 && event1[0] ) {
00445 if ( event2 ) {
00446 len = event2 - event1;
00447 niceassert( len < 4096, "malformed event string (very long)" );
00448 }
00449 else {
00450 len = strlen(event1);
00451 }
00452 if ( len > 4095 ) len = 4095;
00453 strncpy( eventstr, event1, len );
00454 eventstr[len] = 0;
00455
00456 ret1 = onestr_to_event( eventstr );
00457 if ( 0 == ret1 || -1 == ret1 ) {
00458 ret = ret1;
00459 break;
00460 }
00461 ret |= ret1;
00462
00463 event1 = event2;
00464 if ( event1 && event1[0] ) {
00465
00466 ++event1;
00467
00468 if ( !event1[0] ) return 0;
00469 event2 = strchr( event1, sep );
00470 }
00471 }
00472
00473 return ret;
00474 }
00475
00499 int inotifytools_str_to_event(char const * event) {
00500 return inotifytools_str_to_event_sep( event, ',' );
00501 }
00502
00514 int onestr_to_event(char const * event)
00515 {
00516 static int ret;
00517 ret = -1;
00518
00519 if ( !event || !event[0] )
00520 ret = 0;
00521 else if ( 0 == strcasecmp(event, "ACCESS") )
00522 ret = IN_ACCESS;
00523 else if ( 0 == strcasecmp(event, "MODIFY") )
00524 ret = IN_MODIFY;
00525 else if ( 0 == strcasecmp(event, "ATTRIB") )
00526 ret = IN_ATTRIB;
00527 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00528 ret = IN_CLOSE_WRITE;
00529 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00530 ret = IN_CLOSE_NOWRITE;
00531 else if ( 0 == strcasecmp(event, "OPEN") )
00532 ret = IN_OPEN;
00533 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00534 ret = IN_MOVED_FROM;
00535 else if ( 0 == strcasecmp(event, "MOVED_TO") )
00536 ret = IN_MOVED_TO;
00537 else if ( 0 == strcasecmp(event, "CREATE") )
00538 ret = IN_CREATE;
00539 else if ( 0 == strcasecmp(event, "DELETE") )
00540 ret = IN_DELETE;
00541 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00542 ret = IN_DELETE_SELF;
00543 else if ( 0 == strcasecmp(event, "UNMOUNT") )
00544 ret = IN_UNMOUNT;
00545 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00546 ret = IN_Q_OVERFLOW;
00547 else if ( 0 == strcasecmp(event, "IGNORED") )
00548 ret = IN_IGNORED;
00549 else if ( 0 == strcasecmp(event, "CLOSE") )
00550 ret = IN_CLOSE;
00551 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00552 ret = IN_MOVE_SELF;
00553 else if ( 0 == strcasecmp(event, "MOVE") )
00554 ret = IN_MOVE;
00555 else if ( 0 == strcasecmp(event, "ISDIR") )
00556 ret = IN_ISDIR;
00557 else if ( 0 == strcasecmp(event, "ONESHOT") )
00558 ret = IN_ONESHOT;
00559 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00560 ret = IN_ALL_EVENTS;
00561
00562 return ret;
00563 }
00564
00586 char * inotifytools_event_to_str(int events) {
00587 return inotifytools_event_to_str_sep(events, ',');
00588 }
00589
00614 char * inotifytools_event_to_str_sep(int events, char sep)
00615 {
00616 static char ret[1024];
00617 ret[0] = '\0';
00618 ret[1] = '\0';
00619
00620 if ( IN_ACCESS & events ) {
00621 strcat( ret, chrtostr(sep) );
00622 strcat( ret, "ACCESS" );
00623 }
00624 if ( IN_MODIFY & events ) {
00625 strcat( ret, chrtostr(sep) );
00626 strcat( ret, "MODIFY" );
00627 }
00628 if ( IN_ATTRIB & events ) {
00629 strcat( ret, chrtostr(sep) );
00630 strcat( ret, "ATTRIB" );
00631 }
00632 if ( IN_CLOSE_WRITE & events ) {
00633 strcat( ret, chrtostr(sep) );
00634 strcat( ret, "CLOSE_WRITE" );
00635 }
00636 if ( IN_CLOSE_NOWRITE & events ) {
00637 strcat( ret, chrtostr(sep) );
00638 strcat( ret, "CLOSE_NOWRITE" );
00639 }
00640 if ( IN_OPEN & events ) {
00641 strcat( ret, chrtostr(sep) );
00642 strcat( ret, "OPEN" );
00643 }
00644 if ( IN_MOVED_FROM & events ) {
00645 strcat( ret, chrtostr(sep) );
00646 strcat( ret, "MOVED_FROM" );
00647 }
00648 if ( IN_MOVED_TO & events ) {
00649 strcat( ret, chrtostr(sep) );
00650 strcat( ret, "MOVED_TO" );
00651 }
00652 if ( IN_CREATE & events ) {
00653 strcat( ret, chrtostr(sep) );
00654 strcat( ret, "CREATE" );
00655 }
00656 if ( IN_DELETE & events ) {
00657 strcat( ret, chrtostr(sep) );
00658 strcat( ret, "DELETE" );
00659 }
00660 if ( IN_DELETE_SELF & events ) {
00661 strcat( ret, chrtostr(sep) );
00662 strcat( ret, "DELETE_SELF" );
00663 }
00664 if ( IN_UNMOUNT & events ) {
00665 strcat( ret, chrtostr(sep) );
00666 strcat( ret, "UNMOUNT" );
00667 }
00668 if ( IN_Q_OVERFLOW & events ) {
00669 strcat( ret, chrtostr(sep) );
00670 strcat( ret, "Q_OVERFLOW" );
00671 }
00672 if ( IN_IGNORED & events ) {
00673 strcat( ret, chrtostr(sep) );
00674 strcat( ret, "IGNORED" );
00675 }
00676 if ( IN_CLOSE & events ) {
00677 strcat( ret, chrtostr(sep) );
00678 strcat( ret, "CLOSE" );
00679 }
00680 if ( IN_MOVE_SELF & events ) {
00681 strcat( ret, chrtostr(sep) );
00682 strcat( ret, "MOVE_SELF" );
00683 }
00684 if ( IN_ISDIR & events ) {
00685 strcat( ret, chrtostr(sep) );
00686 strcat( ret, "ISDIR" );
00687 }
00688 if ( IN_ONESHOT & events ) {
00689 strcat( ret, chrtostr(sep) );
00690 strcat( ret, "ONESHOT" );
00691 }
00692
00693
00694 if (ret[0] == '\0') {
00695 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00696 }
00697
00698 return &ret[1];
00699 }
00700
00721 char * inotifytools_filename_from_wd( int wd ) {
00722 niceassert( init, "inotifytools_initialize not called yet" );
00723 watch *w = watch_from_wd(wd);
00724 if (!w) return 0;
00725 return w->filename;
00726 }
00727
00742 int inotifytools_wd_from_filename( char const * filename ) {
00743 niceassert( init, "inotifytools_initialize not called yet" );
00744 watch *w = watch_from_filename(filename);
00745 if (!w) return -1;
00746 return w->wd;
00747 }
00748
00763 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00764 niceassert( init, "inotifytools_initialize not called yet" );
00765 watch *w = watch_from_wd(wd);
00766 if (!w) return;
00767 if (w->filename) free(w->filename);
00768 w->filename = strdup(filename);
00769 }
00770
00785 void inotifytools_set_filename_by_filename( char const * oldname,
00786 char const * newname ) {
00787 watch *w = watch_from_filename(oldname);
00788 if (!w) return;
00789 if (w->filename) free(w->filename);
00790 w->filename = strdup(newname);
00791 }
00792
00815 void inotifytools_replace_filename( char const * oldname,
00816 char const * newname ) {
00817 if ( !oldname || !newname ) return;
00818 char *names[2+sizeof(int)/sizeof(char*)];
00819 names[0] = (char*)oldname;
00820 names[1] = (char*)newname;
00821 *((int*)&names[2]) = strlen(oldname);
00822 rbwalk(tree_filename, replace_filename, (void*)names);
00823 }
00824
00828 int remove_inotify_watch(watch *w) {
00829 error = 0;
00830 int status = inotify_rm_watch( inotify_fd, w->wd );
00831 if ( status < 0 ) {
00832 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
00833 strerror(status) );
00834 error = status;
00835 return 0;
00836 }
00837 return 1;
00838 }
00839
00843 void destroy_watch(watch *w) {
00844 if (w->filename) free(w->filename);
00845 free(w);
00846 }
00847
00851 watch *create_watch(int wd, char *filename) {
00852 if ( wd <= 0 || !filename) return 0;
00853
00854 watch *w = (watch*)calloc(1, sizeof(watch));
00855 w->wd = wd;
00856 w->filename = strdup(filename);
00857 rbsearch(w, tree_wd);
00858 rbsearch(w, tree_filename);
00859 }
00860
00873 int inotifytools_remove_watch_by_wd( int wd ) {
00874 niceassert( init, "inotifytools_initialize not called yet" );
00875 watch *w = watch_from_wd(wd);
00876 if (!w) return 1;
00877
00878 if (!remove_inotify_watch(w)) return 0;
00879 rbdelete(w, tree_wd);
00880 rbdelete(w, tree_filename);
00881 destroy_watch(w);
00882 return 1;
00883 }
00884
00896 int inotifytools_remove_watch_by_filename( char const * filename ) {
00897 niceassert( init, "inotifytools_initialize not called yet" );
00898 watch *w = watch_from_filename(filename);
00899 if (!w) return 1;
00900
00901 if (!remove_inotify_watch(w)) return 0;
00902 rbdelete(w, tree_wd);
00903 rbdelete(w, tree_filename);
00904 destroy_watch(w);
00905 return 1;
00906 }
00907
00919 int inotifytools_watch_file( char const * filename, int events ) {
00920 static char const * filenames[2];
00921 filenames[0] = filename;
00922 filenames[1] = NULL;
00923 return inotifytools_watch_files( filenames, events );
00924 }
00925
00941 int inotifytools_watch_files( char const * filenames[], int events ) {
00942 niceassert( init, "inotifytools_initialize not called yet" );
00943 error = 0;
00944
00945 static int i;
00946 for ( i = 0; filenames[i]; ++i ) {
00947 static int wd;
00948 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00949 if ( wd < 0 ) {
00950 if ( wd == -1 ) {
00951 error = errno;
00952 return 0;
00953 }
00954 else {
00955 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00956 "(expected -1 or >0 )", filenames[i], wd );
00957
00958 return 0;
00959 }
00960 }
00961
00962 char *filename;
00963
00964 if ( !isdir(filenames[i])
00965 || filenames[i][strlen(filenames[i])-1] == '/') {
00966 filename = strdup(filenames[i]);
00967 }
00968 else {
00969 nasprintf( &filename, "%s/", filenames[i] );
00970 }
00971 create_watch(wd, filename);
00972 free(filename);
00973 }
00974
00975 return 1;
00976 }
00977
01004 struct inotify_event * inotifytools_next_event( int timeout ) {
01005 return inotifytools_next_events( timeout, 1 );
01006 }
01007
01008
01058 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01059 niceassert( init, "inotifytools_initialize not called yet" );
01060 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01061
01062 if ( num_events < 1 ) return NULL;
01063
01064 static struct inotify_event event[MAX_EVENTS];
01065 static struct inotify_event * ret;
01066 static int first_byte = 0;
01067 static ssize_t bytes;
01068 static jmp_buf jmp;
01069 static char match_name[MAX_STRLEN];
01070
01071 #define RETURN(A) {\
01072 if (regex) {\
01073 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01074 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01075 longjmp(jmp,0);\
01076 }\
01077 }\
01078 if ( collect_stats ) {\
01079 record_stats( A );\
01080 }\
01081 return A;\
01082 }
01083
01084 setjmp(jmp);
01085
01086 error = 0;
01087
01088
01089 if ( first_byte != 0
01090 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01091
01092 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01093 first_byte += sizeof(struct inotify_event) + ret->len;
01094
01095
01096
01097 if ( first_byte == bytes ) {
01098 first_byte = 0;
01099 }
01100 else if ( first_byte > bytes ) {
01101
01102
01103
01104
01105
01106
01107 niceassert( (long)((char *)&event[0] +
01108 sizeof(struct inotify_event) +
01109 event[0].len) <= (long)ret,
01110 "extremely unlucky user, death imminent" );
01111
01112 bytes = (char *)&event[0] + bytes - (char *)ret;
01113 memcpy( &event[0], ret, bytes );
01114 return inotifytools_next_events( timeout, num_events );
01115 }
01116 RETURN(ret);
01117
01118 }
01119
01120 else if ( first_byte == 0 ) {
01121 bytes = 0;
01122 }
01123
01124
01125 static ssize_t this_bytes;
01126 static unsigned int bytes_to_read;
01127 static int rc;
01128 static fd_set read_fds;
01129
01130 static struct timeval read_timeout;
01131 read_timeout.tv_sec = timeout;
01132 read_timeout.tv_usec = 0;
01133 static struct timeval * read_timeout_ptr;
01134 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01135
01136 FD_ZERO(&read_fds);
01137 FD_SET(inotify_fd, &read_fds);
01138 rc = select(inotify_fd + 1, &read_fds,
01139 NULL, NULL, read_timeout_ptr);
01140 if ( rc < 0 ) {
01141
01142 error = errno;
01143 return NULL;
01144 }
01145 else if ( rc == 0 ) {
01146
01147 return NULL;
01148 }
01149
01150
01151 do {
01152 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01153 } while ( !rc &&
01154 bytes_to_read < sizeof(struct inotify_event)*num_events );
01155
01156 if ( rc == -1 ) {
01157 error = errno;
01158 return NULL;
01159 }
01160
01161 this_bytes = read(inotify_fd, &event[0] + bytes,
01162 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01163 if ( this_bytes < 0 ) {
01164 error = errno;
01165 return NULL;
01166 }
01167 if ( this_bytes == 0 ) {
01168 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
01169 "events occurred at once.\n");
01170 return NULL;
01171 }
01172 bytes += this_bytes;
01173
01174 ret = &event[0];
01175 first_byte = sizeof(struct inotify_event) + ret->len;
01176 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01177 "almost certainly screw up." );
01178 if ( first_byte == bytes ) {
01179 first_byte = 0;
01180 }
01181
01182 RETURN(ret);
01183
01184 #undef RETURN
01185 }
01186
01212 int inotifytools_watch_recursively( char const * path, int events ) {
01213 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01214 }
01215
01248 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01249 char const ** exclude_list ) {
01250 niceassert( init, "inotifytools_initialize not called yet" );
01251
01252 DIR * dir;
01253 char * my_path;
01254 error = 0;
01255 dir = opendir( path );
01256 if ( !dir ) {
01257
01258 if ( errno == ENOTDIR ) {
01259 return inotifytools_watch_file( path, events );
01260 }
01261 else {
01262 error = errno;
01263 return 0;
01264 }
01265 }
01266
01267 if ( path[strlen(path)-1] != '/' ) {
01268 nasprintf( &my_path, "%s/", path );
01269 }
01270 else {
01271 my_path = (char *)path;
01272 }
01273
01274 static struct dirent * ent;
01275 char * next_file;
01276 static struct stat64 my_stat;
01277 ent = readdir( dir );
01278
01279 while ( ent ) {
01280 if ( (0 != strcmp( ent->d_name, "." )) &&
01281 (0 != strcmp( ent->d_name, ".." )) ) {
01282 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01283 if ( -1 == lstat64( next_file, &my_stat ) ) {
01284 error = errno;
01285 free( next_file );
01286 if ( errno != EACCES ) {
01287 error = errno;
01288 if ( my_path != path ) free( my_path );
01289 closedir( dir );
01290 return 0;
01291 }
01292 }
01293 else if ( S_ISDIR( my_stat.st_mode ) &&
01294 !S_ISLNK( my_stat.st_mode )) {
01295 free( next_file );
01296 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01297 static unsigned int no_watch;
01298 static char const ** exclude_entry;
01299
01300 no_watch = 0;
01301 for (exclude_entry = exclude_list;
01302 exclude_entry && *exclude_entry && !no_watch;
01303 ++exclude_entry) {
01304 static int exclude_length;
01305
01306 exclude_length = strlen(*exclude_entry);
01307 if ((*exclude_entry)[exclude_length-1] == '/') {
01308 --exclude_length;
01309 }
01310 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01311 !strncmp(*exclude_entry, next_file, exclude_length)) {
01312
01313 no_watch = 1;
01314 }
01315 }
01316 if (!no_watch) {
01317 static int status;
01318 status = inotifytools_watch_recursively_with_exclude(
01319 next_file,
01320 events,
01321 exclude_list );
01322
01323 if ( !status && (EACCES != error) && (ENOENT != error) &&
01324 (ELOOP != error) ) {
01325 free( next_file );
01326 if ( my_path != path ) free( my_path );
01327 closedir( dir );
01328 return 0;
01329 }
01330 }
01331 free( next_file );
01332 }
01333 else {
01334 free( next_file );
01335 }
01336 }
01337 ent = readdir( dir );
01338 error = 0;
01339 }
01340
01341 if ( my_path != path ) free( my_path );
01342 closedir( dir );
01343
01344 return inotifytools_watch_file( path, events );
01345 }
01346
01350 void record_stats( struct inotify_event const * event ) {
01351 if (!event) return;
01352 watch *w = watch_from_wd(event->wd);
01353 if (!w) return;
01354 if ( IN_ACCESS & event->mask ) {
01355 ++w->hit_access;
01356 ++num_access;
01357 }
01358 if ( IN_MODIFY & event->mask ) {
01359 ++w->hit_modify;
01360 ++num_modify;
01361 }
01362 if ( IN_ATTRIB & event->mask ) {
01363 ++w->hit_attrib;
01364 ++num_attrib;
01365 }
01366 if ( IN_CLOSE_WRITE & event->mask ) {
01367 ++w->hit_close_write;
01368 ++num_close_write;
01369 }
01370 if ( IN_CLOSE_NOWRITE & event->mask ) {
01371 ++w->hit_close_nowrite;
01372 ++num_close_nowrite;
01373 }
01374 if ( IN_OPEN & event->mask ) {
01375 ++w->hit_open;
01376 ++num_open;
01377 }
01378 if ( IN_MOVED_FROM & event->mask ) {
01379 ++w->hit_moved_from;
01380 ++num_moved_from;
01381 }
01382 if ( IN_MOVED_TO & event->mask ) {
01383 ++w->hit_moved_to;
01384 ++num_moved_to;
01385 }
01386 if ( IN_CREATE & event->mask ) {
01387 ++w->hit_create;
01388 ++num_create;
01389 }
01390 if ( IN_DELETE & event->mask ) {
01391 ++w->hit_delete;
01392 ++num_delete;
01393 }
01394 if ( IN_DELETE_SELF & event->mask ) {
01395 ++w->hit_delete_self;
01396 ++num_delete_self;
01397 }
01398 if ( IN_UNMOUNT & event->mask ) {
01399 ++w->hit_unmount;
01400 ++num_unmount;
01401 }
01402 if ( IN_MOVE_SELF & event->mask ) {
01403 ++w->hit_move_self;
01404 ++num_move_self;
01405 }
01406
01407 ++w->hit_total;
01408 ++num_total;
01409
01410 }
01411
01412 int *stat_ptr(watch *w, int event)
01413 {
01414 if ( IN_ACCESS == event )
01415 return &w->hit_access;
01416 if ( IN_MODIFY == event )
01417 return &w->hit_modify;
01418 if ( IN_ATTRIB == event )
01419 return &w->hit_attrib;
01420 if ( IN_CLOSE_WRITE == event )
01421 return &w->hit_close_write;
01422 if ( IN_CLOSE_NOWRITE == event )
01423 return &w->hit_close_nowrite;
01424 if ( IN_OPEN == event )
01425 return &w->hit_open;
01426 if ( IN_MOVED_FROM == event )
01427 return &w->hit_moved_from;
01428 if ( IN_MOVED_TO == event )
01429 return &w->hit_moved_to;
01430 if ( IN_CREATE == event )
01431 return &w->hit_create;
01432 if ( IN_DELETE == event )
01433 return &w->hit_delete;
01434 if ( IN_DELETE_SELF == event )
01435 return &w->hit_delete_self;
01436 if ( IN_UNMOUNT == event )
01437 return &w->hit_unmount;
01438 if ( IN_MOVE_SELF == event )
01439 return &w->hit_move_self;
01440 if ( 0 == event )
01441 return &w->hit_total;
01442 return 0;
01443 }
01444
01460 int inotifytools_get_stat_by_wd( int wd, int event ) {
01461 if (!collect_stats) return -1;
01462
01463 watch *w = watch_from_wd(wd);
01464 if (!w) return -1;
01465 int *i = stat_ptr(w, event);
01466 if (!i) return -1;
01467 return *i;
01468 }
01469
01483 int inotifytools_get_stat_total( int event ) {
01484 if (!collect_stats) return -1;
01485 if ( IN_ACCESS == event )
01486 return num_access;
01487 if ( IN_MODIFY == event )
01488 return num_modify;
01489 if ( IN_ATTRIB == event )
01490 return num_attrib;
01491 if ( IN_CLOSE_WRITE == event )
01492 return num_close_write;
01493 if ( IN_CLOSE_NOWRITE == event )
01494 return num_close_nowrite;
01495 if ( IN_OPEN == event )
01496 return num_open;
01497 if ( IN_MOVED_FROM == event )
01498 return num_moved_from;
01499 if ( IN_MOVED_TO == event )
01500 return num_moved_to;
01501 if ( IN_CREATE == event )
01502 return num_create;
01503 if ( IN_DELETE == event )
01504 return num_delete;
01505 if ( IN_DELETE_SELF == event )
01506 return num_delete_self;
01507 if ( IN_UNMOUNT == event )
01508 return num_unmount;
01509 if ( IN_MOVE_SELF == event )
01510 return num_move_self;
01511
01512 if ( 0 == event )
01513 return num_total;
01514
01515 return -1;
01516 }
01517
01537 int inotifytools_get_stat_by_filename( char const * filename,
01538 int event ) {
01539 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01540 filename ), event );
01541 }
01542
01553 int inotifytools_error() {
01554 return error;
01555 }
01556
01560 int isdir( char const * path ) {
01561 static struct stat64 my_stat;
01562
01563 if ( -1 == lstat64( path, &my_stat ) ) {
01564 if (errno == ENOENT) return 0;
01565 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01566 return 0;
01567 }
01568
01569 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01570 }
01571
01572
01579 int inotifytools_get_num_watches() {
01580 int ret = 0;
01581 rbwalk(tree_filename, get_num, (void*)&ret);
01582 return ret;
01583 }
01584
01625 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01626 return inotifytools_fprintf( stdout, event, fmt );
01627 }
01628
01670 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01671 static char out[MAX_STRLEN+1];
01672 static int ret;
01673 ret = inotifytools_sprintf( out, event, fmt );
01674 if ( -1 != ret ) fprintf( file, "%s", out );
01675 return ret;
01676 }
01677
01728 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01729 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01730 }
01731
01732
01779 int inotifytools_snprintf( char * out, int size,
01780 struct inotify_event* event, char* fmt ) {
01781 static char * filename, * eventname, * eventstr;
01782 static unsigned int i, ind;
01783 static char ch1;
01784 static char timestr[MAX_STRLEN];
01785 static time_t now;
01786
01787
01788 if ( event->len > 0 ) {
01789 eventname = event->name;
01790 }
01791 else {
01792 eventname = NULL;
01793 }
01794
01795
01796 filename = inotifytools_filename_from_wd( event->wd );
01797
01798 if ( !fmt || 0 == strlen(fmt) ) {
01799 error = EINVAL;
01800 return -1;
01801 }
01802 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01803 error = EMSGSIZE;
01804 return -1;
01805 }
01806
01807 ind = 0;
01808 for ( i = 0; i < strlen(fmt) &&
01809 (int)ind < size - 1; ++i ) {
01810 if ( fmt[i] != '%' ) {
01811 out[ind++] = fmt[i];
01812 continue;
01813 }
01814
01815 if ( i == strlen(fmt) - 1 ) {
01816
01817 error = EINVAL;
01818 return ind;
01819 }
01820
01821 ch1 = fmt[i+1];
01822
01823 if ( ch1 == '%' ) {
01824 out[ind++] = '%';
01825 ++i;
01826 continue;
01827 }
01828
01829 if ( ch1 == 'w' ) {
01830 if ( filename ) {
01831 strncpy( &out[ind], filename, size - ind );
01832 ind += strlen(filename);
01833 }
01834 ++i;
01835 continue;
01836 }
01837
01838 if ( ch1 == 'f' ) {
01839 if ( eventname ) {
01840 strncpy( &out[ind], eventname, size - ind );
01841 ind += strlen(eventname);
01842 }
01843 ++i;
01844 continue;
01845 }
01846
01847 if ( ch1 == 'e' ) {
01848 eventstr = inotifytools_event_to_str( event->mask );
01849 strncpy( &out[ind], eventstr, size - ind );
01850 ind += strlen(eventstr);
01851 ++i;
01852 continue;
01853 }
01854
01855 if ( ch1 == 'T' ) {
01856
01857 if ( timefmt ) {
01858
01859 now = time(0);
01860 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01861 localtime( &now ) ) ) {
01862
01863
01864 error = EINVAL;
01865 return ind;
01866 }
01867 }
01868 else {
01869 timestr[0] = 0;
01870 }
01871
01872 strncpy( &out[ind], timestr, size - ind );
01873 ind += strlen(timestr);
01874 ++i;
01875 continue;
01876 }
01877
01878
01879 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01880 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01881 strncpy( &out[ind], eventstr, size - ind );
01882 ind += strlen(eventstr);
01883 i += 2;
01884 continue;
01885 }
01886
01887
01888 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01889 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01890 ++i;
01891 }
01892 out[ind] = 0;
01893
01894 return ind - 1;
01895 }
01896
01906 void inotifytools_set_printf_timefmt( char * fmt ) {
01907 timefmt = fmt;
01908 }
01909
01918 int inotifytools_get_max_queued_events() {
01919 int ret;
01920 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01921 return ret;
01922 }
01923
01933 int inotifytools_get_max_user_instances() {
01934 int ret;
01935 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01936 return ret;
01937 }
01938
01948 int inotifytools_get_max_user_watches() {
01949 int ret;
01950 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01951 return ret;
01952 }
01953
01965 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
01966 if (!pattern) {
01967 if (regex) {
01968 regfree(regex);
01969 free(regex);
01970 regex = 0;
01971 }
01972 return 1;
01973 }
01974
01975 if (regex) { regfree(regex); }
01976 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
01977
01978 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
01979 if (0 == ret) return 1;
01980
01981 regfree(regex);
01982 free(regex);
01983 regex = 0;
01984 error = EINVAL;
01985 return 0;
01986 }
01987
01988 int event_compare(const void *p1, const void *p2, const void *config)
01989 {
01990 if (!p1 || !p2) return p1 - p2;
01991 char asc = 1;
01992 int sort_event = (int)config;
01993 if (sort_event == -1) {
01994 sort_event = 0;
01995 asc = 0;
01996 } else if (sort_event < 0) {
01997 sort_event = -sort_event;
01998 asc = 0;
01999 }
02000 int *i1 = stat_ptr((watch*)p1, sort_event);
02001 int *i2 = stat_ptr((watch*)p2, sort_event);
02002 if (0 == *i1 - *i2) {
02003 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02004 }
02005 if (asc)
02006 return *i1 - *i2;
02007 else
02008 return *i2 - *i1;
02009 }
02010
02011 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02012 {
02013 struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02014 RBLIST *all = rbopenlist(tree_wd);
02015 void const *p = rbreadlist(all);
02016 while (p) {
02017 void const *r = rbsearch(p, ret);
02018 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02019 p = rbreadlist(all);
02020 }
02021 rbcloselist(all);
02022 return ret;
02023 }