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
00122 #define MAX_EVENTS 4096
00123 #define MAX_STRLEN 4096
00124 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00125 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00126 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
00127 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
00128
00129 static int inotify_fd;
00130 static unsigned num_access;
00131 static unsigned num_modify;
00132 static unsigned num_attrib;
00133 static unsigned num_close_nowrite;
00134 static unsigned num_close_write;
00135 static unsigned num_open;
00136 static unsigned num_move_self;
00137 static unsigned num_moved_to;
00138 static unsigned num_moved_from;
00139 static unsigned num_create;
00140 static unsigned num_delete;
00141 static unsigned num_delete_self;
00142 static unsigned num_unmount;
00143 static unsigned num_total;
00144 static int collect_stats = 0;
00145
00146 struct rbtree *tree_wd = 0;
00147 struct rbtree *tree_filename = 0;
00148 static int error = 0;
00149 static int init = 0;
00150 static char* timefmt = 0;
00151 static regex_t* regex = 0;
00152
00153 static int invert_regexp = 0;
00154
00155 int isdir( char const * path );
00156 void record_stats( struct inotify_event const * event );
00157 int onestr_to_event(char const * event);
00158
00176 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00177 #cond, mesg)
00178
00179 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00180
00198 void _niceassert( long cond, int line, char const * file, char const * condstr,
00199 char const * mesg ) {
00200 if ( cond ) return;
00201
00202 if ( mesg ) {
00203 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00204 condstr, mesg );
00205 }
00206 else {
00207 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00208 }
00209 }
00210
00220 char * chrtostr(char ch) {
00221 static char str[2] = { '\0', '\0' };
00222 str[0] = ch;
00223 return str;
00224 }
00225
00229 int read_num_from_file( char * filename, int * num ) {
00230 FILE * file = fopen( filename, "r" );
00231 if ( !file ) {
00232 error = errno;
00233 return 0;
00234 }
00235
00236 if ( EOF == fscanf( file, "%d", num ) ) {
00237 error = errno;
00238 return 0;
00239 }
00240
00241 niceassert( 0 == fclose( file ), 0 );
00242
00243 return 1;
00244 }
00245
00246 int wd_compare(const void *d1, const void *d2, const void *config) {
00247 if (!d1 || !d2) return d1 - d2;
00248 return ((watch*)d1)->wd - ((watch*)d2)->wd;
00249 }
00250
00251 int filename_compare(const void *d1, const void *d2, const void *config) {
00252 if (!d1 || !d2) return d1 - d2;
00253 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
00254 }
00255
00259 watch *watch_from_wd( int wd ) {
00260 watch w;
00261 w.wd = wd;
00262 return (watch*)rbfind(&w, tree_wd);
00263 }
00264
00268 watch *watch_from_filename( char const *filename ) {
00269 watch w;
00270 w.filename = (char*)filename;
00271 return (watch*)rbfind(&w, tree_filename);
00272 }
00273
00283 int inotifytools_initialize() {
00284 if (init) return 1;
00285
00286 error = 0;
00287
00288 inotify_fd = inotify_init();
00289 if (inotify_fd < 0) {
00290 error = inotify_fd;
00291 return 0;
00292 }
00293
00294 collect_stats = 0;
00295 init = 1;
00296 tree_wd = rbinit(wd_compare, 0);
00297 tree_filename = rbinit(filename_compare, 0);
00298 timefmt = 0;
00299
00300 return 1;
00301 }
00302
00306 void destroy_watch(watch *w) {
00307 if (w->filename) free(w->filename);
00308 free(w);
00309 }
00310
00314 void cleanup_tree(const void *nodep,
00315 const VISIT which,
00316 const int depth, void* arg) {
00317 if (which != endorder && which != leaf) return;
00318 watch *w = (watch*)nodep;
00319 destroy_watch(w);
00320 }
00321
00328 void inotifytools_cleanup() {
00329 if (!init) return;
00330
00331 init = 0;
00332 close(inotify_fd);
00333 collect_stats = 0;
00334 error = 0;
00335 timefmt = 0;
00336
00337 if (regex) {
00338 regfree(regex);
00339 free(regex);
00340 regex = 0;
00341 }
00342
00343 rbwalk(tree_wd, cleanup_tree, 0);
00344 rbdestroy(tree_wd); tree_wd = 0;
00345 rbdestroy(tree_filename); tree_filename = 0;
00346 }
00347
00351 void empty_stats(const void *nodep,
00352 const VISIT which,
00353 const int depth, void *arg) {
00354 if (which != endorder && which != leaf) return;
00355 watch *w = (watch*)nodep;
00356 w->hit_access = 0;
00357 w->hit_modify = 0;
00358 w->hit_attrib = 0;
00359 w->hit_close_nowrite = 0;
00360 w->hit_close_write = 0;
00361 w->hit_open = 0;
00362 w->hit_move_self = 0;
00363 w->hit_moved_from = 0;
00364 w->hit_moved_to = 0;
00365 w->hit_create = 0;
00366 w->hit_delete = 0;
00367 w->hit_delete_self = 0;
00368 w->hit_unmount = 0;
00369 w->hit_total = 0;
00370 }
00371
00375 void replace_filename(const void *nodep,
00376 const VISIT which,
00377 const int depth, void *arg) {
00378 if (which != endorder && which != leaf) return;
00379 watch *w = (watch*)nodep;
00380 char *old_name = ((char**)arg)[0];
00381 char *new_name = ((char**)arg)[1];
00382 int old_len = *((int*)&((char**)arg)[2]);
00383 char *name;
00384 if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
00385 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
00386 if (!strcmp( w->filename, new_name )) {
00387 free(name);
00388 } else {
00389 rbdelete(w, tree_filename);
00390 free( w->filename );
00391 w->filename = name;
00392 rbsearch(w, tree_filename);
00393 }
00394 }
00395 }
00396
00400 void get_num(const void *nodep,
00401 const VISIT which,
00402 const int depth, void *arg) {
00403 if (which != endorder && which != leaf) return;
00404 ++(*((int*)arg));
00405 }
00406
00407
00420 void inotifytools_initialize_stats() {
00421 niceassert( init, "inotifytools_initialize not called yet" );
00422
00423
00424 if (collect_stats) {
00425 rbwalk(tree_wd, empty_stats, 0);
00426 }
00427
00428 num_access = 0;
00429 num_modify = 0;
00430 num_attrib = 0;
00431 num_close_nowrite = 0;
00432 num_close_write = 0;
00433 num_open = 0;
00434 num_move_self = 0;
00435 num_moved_from = 0;
00436 num_moved_to = 0;
00437 num_create = 0;
00438 num_delete = 0;
00439 num_delete_self = 0;
00440 num_unmount = 0;
00441 num_total = 0;
00442
00443 collect_stats = 1;
00444 }
00445
00473 int inotifytools_str_to_event_sep(char const * event, char sep) {
00474 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00475 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00476 return -1;
00477 }
00478
00479 int ret, ret1, len;
00480 char * event1, * event2;
00481 char eventstr[4096];
00482 ret = 0;
00483
00484 if ( !event || !event[0] ) return 0;
00485
00486 event1 = (char *)event;
00487 event2 = strchr( event1, sep );
00488 while ( event1 && event1[0] ) {
00489 if ( event2 ) {
00490 len = event2 - event1;
00491 niceassert( len < 4096, "malformed event string (very long)" );
00492 }
00493 else {
00494 len = strlen(event1);
00495 }
00496 if ( len > 4095 ) len = 4095;
00497 strncpy( eventstr, event1, len );
00498 eventstr[len] = 0;
00499
00500 ret1 = onestr_to_event( eventstr );
00501 if ( 0 == ret1 || -1 == ret1 ) {
00502 ret = ret1;
00503 break;
00504 }
00505 ret |= ret1;
00506
00507 event1 = event2;
00508 if ( event1 && event1[0] ) {
00509
00510 ++event1;
00511
00512 if ( !event1[0] ) return 0;
00513 event2 = strchr( event1, sep );
00514 }
00515 }
00516
00517 return ret;
00518 }
00519
00543 int inotifytools_str_to_event(char const * event) {
00544 return inotifytools_str_to_event_sep( event, ',' );
00545 }
00546
00558 int onestr_to_event(char const * event)
00559 {
00560 static int ret;
00561 ret = -1;
00562
00563 if ( !event || !event[0] )
00564 ret = 0;
00565 else if ( 0 == strcasecmp(event, "ACCESS") )
00566 ret = IN_ACCESS;
00567 else if ( 0 == strcasecmp(event, "MODIFY") )
00568 ret = IN_MODIFY;
00569 else if ( 0 == strcasecmp(event, "ATTRIB") )
00570 ret = IN_ATTRIB;
00571 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00572 ret = IN_CLOSE_WRITE;
00573 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00574 ret = IN_CLOSE_NOWRITE;
00575 else if ( 0 == strcasecmp(event, "OPEN") )
00576 ret = IN_OPEN;
00577 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00578 ret = IN_MOVED_FROM;
00579 else if ( 0 == strcasecmp(event, "MOVED_TO") )
00580 ret = IN_MOVED_TO;
00581 else if ( 0 == strcasecmp(event, "CREATE") )
00582 ret = IN_CREATE;
00583 else if ( 0 == strcasecmp(event, "DELETE") )
00584 ret = IN_DELETE;
00585 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00586 ret = IN_DELETE_SELF;
00587 else if ( 0 == strcasecmp(event, "UNMOUNT") )
00588 ret = IN_UNMOUNT;
00589 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00590 ret = IN_Q_OVERFLOW;
00591 else if ( 0 == strcasecmp(event, "IGNORED") )
00592 ret = IN_IGNORED;
00593 else if ( 0 == strcasecmp(event, "CLOSE") )
00594 ret = IN_CLOSE;
00595 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00596 ret = IN_MOVE_SELF;
00597 else if ( 0 == strcasecmp(event, "MOVE") )
00598 ret = IN_MOVE;
00599 else if ( 0 == strcasecmp(event, "ISDIR") )
00600 ret = IN_ISDIR;
00601 else if ( 0 == strcasecmp(event, "ONESHOT") )
00602 ret = IN_ONESHOT;
00603 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00604 ret = IN_ALL_EVENTS;
00605
00606 return ret;
00607 }
00608
00630 char * inotifytools_event_to_str(int events) {
00631 return inotifytools_event_to_str_sep(events, ',');
00632 }
00633
00658 char * inotifytools_event_to_str_sep(int events, char sep)
00659 {
00660 static char ret[1024];
00661 ret[0] = '\0';
00662 ret[1] = '\0';
00663
00664 if ( IN_ACCESS & events ) {
00665 strcat( ret, chrtostr(sep) );
00666 strcat( ret, "ACCESS" );
00667 }
00668 if ( IN_MODIFY & events ) {
00669 strcat( ret, chrtostr(sep) );
00670 strcat( ret, "MODIFY" );
00671 }
00672 if ( IN_ATTRIB & events ) {
00673 strcat( ret, chrtostr(sep) );
00674 strcat( ret, "ATTRIB" );
00675 }
00676 if ( IN_CLOSE_WRITE & events ) {
00677 strcat( ret, chrtostr(sep) );
00678 strcat( ret, "CLOSE_WRITE" );
00679 }
00680 if ( IN_CLOSE_NOWRITE & events ) {
00681 strcat( ret, chrtostr(sep) );
00682 strcat( ret, "CLOSE_NOWRITE" );
00683 }
00684 if ( IN_OPEN & events ) {
00685 strcat( ret, chrtostr(sep) );
00686 strcat( ret, "OPEN" );
00687 }
00688 if ( IN_MOVED_FROM & events ) {
00689 strcat( ret, chrtostr(sep) );
00690 strcat( ret, "MOVED_FROM" );
00691 }
00692 if ( IN_MOVED_TO & events ) {
00693 strcat( ret, chrtostr(sep) );
00694 strcat( ret, "MOVED_TO" );
00695 }
00696 if ( IN_CREATE & events ) {
00697 strcat( ret, chrtostr(sep) );
00698 strcat( ret, "CREATE" );
00699 }
00700 if ( IN_DELETE & events ) {
00701 strcat( ret, chrtostr(sep) );
00702 strcat( ret, "DELETE" );
00703 }
00704 if ( IN_DELETE_SELF & events ) {
00705 strcat( ret, chrtostr(sep) );
00706 strcat( ret, "DELETE_SELF" );
00707 }
00708 if ( IN_UNMOUNT & events ) {
00709 strcat( ret, chrtostr(sep) );
00710 strcat( ret, "UNMOUNT" );
00711 }
00712 if ( IN_Q_OVERFLOW & events ) {
00713 strcat( ret, chrtostr(sep) );
00714 strcat( ret, "Q_OVERFLOW" );
00715 }
00716 if ( IN_IGNORED & events ) {
00717 strcat( ret, chrtostr(sep) );
00718 strcat( ret, "IGNORED" );
00719 }
00720 if ( IN_CLOSE & events ) {
00721 strcat( ret, chrtostr(sep) );
00722 strcat( ret, "CLOSE" );
00723 }
00724 if ( IN_MOVE_SELF & events ) {
00725 strcat( ret, chrtostr(sep) );
00726 strcat( ret, "MOVE_SELF" );
00727 }
00728 if ( IN_ISDIR & events ) {
00729 strcat( ret, chrtostr(sep) );
00730 strcat( ret, "ISDIR" );
00731 }
00732 if ( IN_ONESHOT & events ) {
00733 strcat( ret, chrtostr(sep) );
00734 strcat( ret, "ONESHOT" );
00735 }
00736
00737
00738 if (ret[0] == '\0') {
00739 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00740 }
00741
00742 return &ret[1];
00743 }
00744
00765 char * inotifytools_filename_from_wd( int wd ) {
00766 niceassert( init, "inotifytools_initialize not called yet" );
00767 watch *w = watch_from_wd(wd);
00768 if (!w)
00769 return NULL;
00770
00771 return w->filename;
00772 }
00773
00788 int inotifytools_wd_from_filename( char const * filename ) {
00789 niceassert( init, "inotifytools_initialize not called yet" );
00790 watch *w = watch_from_filename(filename);
00791 if (!w) return -1;
00792 return w->wd;
00793 }
00794
00809 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00810 niceassert( init, "inotifytools_initialize not called yet" );
00811 watch *w = watch_from_wd(wd);
00812 if (!w) return;
00813 if (w->filename) free(w->filename);
00814 w->filename = strdup(filename);
00815 }
00816
00831 void inotifytools_set_filename_by_filename( char const * oldname,
00832 char const * newname ) {
00833 watch *w = watch_from_filename(oldname);
00834 if (!w) return;
00835 if (w->filename) free(w->filename);
00836 w->filename = strdup(newname);
00837 }
00838
00861 void inotifytools_replace_filename( char const * oldname,
00862 char const * newname ) {
00863 if ( !oldname || !newname ) return;
00864 char *names[2+sizeof(int)/sizeof(char*)];
00865 names[0] = (char*)oldname;
00866 names[1] = (char*)newname;
00867 *((int*)&names[2]) = strlen(oldname);
00868 rbwalk(tree_filename, replace_filename, (void*)names);
00869 }
00870
00874 int remove_inotify_watch(watch *w) {
00875 error = 0;
00876 int status = inotify_rm_watch( inotify_fd, w->wd );
00877 if ( status < 0 ) {
00878 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
00879 strerror(status) );
00880 error = status;
00881 return 0;
00882 }
00883 return 1;
00884 }
00885
00889 watch *create_watch(int wd, char *filename) {
00890 if ( wd <= 0 || !filename) return 0;
00891
00892 watch *w = (watch*)calloc(1, sizeof(watch));
00893 w->wd = wd;
00894 w->filename = strdup(filename);
00895 rbsearch(w, tree_wd);
00896 rbsearch(w, tree_filename);
00897 }
00898
00911 int inotifytools_remove_watch_by_wd( int wd ) {
00912 niceassert( init, "inotifytools_initialize not called yet" );
00913 watch *w = watch_from_wd(wd);
00914 if (!w) return 1;
00915
00916 if (!remove_inotify_watch(w)) return 0;
00917 rbdelete(w, tree_wd);
00918 rbdelete(w, tree_filename);
00919 destroy_watch(w);
00920 return 1;
00921 }
00922
00934 int inotifytools_remove_watch_by_filename( char const * filename ) {
00935 niceassert( init, "inotifytools_initialize not called yet" );
00936 watch *w = watch_from_filename(filename);
00937 if (!w) return 1;
00938
00939 if (!remove_inotify_watch(w)) return 0;
00940 rbdelete(w, tree_wd);
00941 rbdelete(w, tree_filename);
00942 destroy_watch(w);
00943 return 1;
00944 }
00945
00957 int inotifytools_watch_file( char const * filename, int events ) {
00958 static char const * filenames[2];
00959 filenames[0] = filename;
00960 filenames[1] = NULL;
00961 return inotifytools_watch_files( filenames, events );
00962 }
00963
00979 int inotifytools_watch_files( char const * filenames[], int events ) {
00980 niceassert( init, "inotifytools_initialize not called yet" );
00981 error = 0;
00982
00983 static int i;
00984 for ( i = 0; filenames[i]; ++i ) {
00985 static int wd;
00986 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00987 if ( wd < 0 ) {
00988 if ( wd == -1 ) {
00989 error = errno;
00990 return 0;
00991 }
00992 else {
00993 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00994 "(expected -1 or >0 )", filenames[i], wd );
00995
00996 return 0;
00997 }
00998 }
00999
01000 char *filename;
01001
01002 if ( !isdir(filenames[i])
01003 || filenames[i][strlen(filenames[i])-1] == '/') {
01004 filename = strdup(filenames[i]);
01005 }
01006 else {
01007 nasprintf( &filename, "%s/", filenames[i] );
01008 }
01009 create_watch(wd, filename);
01010 free(filename);
01011 }
01012
01013 return 1;
01014 }
01015
01042 struct inotify_event * inotifytools_next_event( int timeout ) {
01043 return inotifytools_next_events( timeout, 1 );
01044 }
01045
01046
01096 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01097 niceassert( init, "inotifytools_initialize not called yet" );
01098 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01099
01100 if ( num_events < 1 ) return NULL;
01101
01102 static struct inotify_event event[MAX_EVENTS];
01103 static struct inotify_event * ret;
01104 static int first_byte = 0;
01105 static ssize_t bytes;
01106 static jmp_buf jmp;
01107 static char match_name[MAX_STRLEN];
01108
01109 #define RETURN(A) {\
01110 if (regex) {\
01111 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01112 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01113 if (!invert_regexp)\
01114 longjmp(jmp,0);\
01115 } else {\
01116 if (invert_regexp)\
01117 longjmp(jmp,0);\
01118 }\
01119 }\
01120 if ( collect_stats ) {\
01121 record_stats( A );\
01122 }\
01123 return A;\
01124 }
01125
01126 setjmp(jmp);
01127
01128 error = 0;
01129
01130
01131 if ( first_byte != 0
01132 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01133
01134 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01135 first_byte += sizeof(struct inotify_event) + ret->len;
01136
01137
01138
01139 if ( first_byte == bytes ) {
01140 first_byte = 0;
01141 }
01142 else if ( first_byte > bytes ) {
01143
01144
01145
01146
01147
01148
01149 niceassert( (long)((char *)&event[0] +
01150 sizeof(struct inotify_event) +
01151 event[0].len) <= (long)ret,
01152 "extremely unlucky user, death imminent" );
01153
01154 bytes = (char *)&event[0] + bytes - (char *)ret;
01155 memcpy( &event[0], ret, bytes );
01156 return inotifytools_next_events( timeout, num_events );
01157 }
01158 RETURN(ret);
01159
01160 }
01161
01162 else if ( first_byte == 0 ) {
01163 bytes = 0;
01164 }
01165
01166
01167 static ssize_t this_bytes;
01168 static unsigned int bytes_to_read;
01169 static int rc;
01170 static fd_set read_fds;
01171
01172 static struct timeval read_timeout;
01173 read_timeout.tv_sec = timeout;
01174 read_timeout.tv_usec = 0;
01175 static struct timeval * read_timeout_ptr;
01176 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01177
01178 FD_ZERO(&read_fds);
01179 FD_SET(inotify_fd, &read_fds);
01180 rc = select(inotify_fd + 1, &read_fds,
01181 NULL, NULL, read_timeout_ptr);
01182 if ( rc < 0 ) {
01183
01184 error = errno;
01185 return NULL;
01186 }
01187 else if ( rc == 0 ) {
01188
01189 return NULL;
01190 }
01191
01192
01193 do {
01194 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01195 } while ( !rc &&
01196 bytes_to_read < sizeof(struct inotify_event)*num_events );
01197
01198 if ( rc == -1 ) {
01199 error = errno;
01200 return NULL;
01201 }
01202
01203 this_bytes = read(inotify_fd, &event[0] + bytes,
01204 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01205 if ( this_bytes < 0 ) {
01206 error = errno;
01207 return NULL;
01208 }
01209 if ( this_bytes == 0 ) {
01210 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
01211 "events occurred at once.\n");
01212 return NULL;
01213 }
01214 bytes += this_bytes;
01215
01216 ret = &event[0];
01217 first_byte = sizeof(struct inotify_event) + ret->len;
01218 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01219 "almost certainly screw up." );
01220 if ( first_byte == bytes ) {
01221 first_byte = 0;
01222 }
01223
01224 RETURN(ret);
01225
01226 #undef RETURN
01227 }
01228
01254 int inotifytools_watch_recursively( char const * path, int events ) {
01255 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01256 }
01257
01290 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01291 char const ** exclude_list ) {
01292 niceassert( init, "inotifytools_initialize not called yet" );
01293
01294 DIR * dir;
01295 char * my_path;
01296 error = 0;
01297 dir = opendir( path );
01298 if ( !dir ) {
01299
01300 if ( errno == ENOTDIR ) {
01301 return inotifytools_watch_file( path, events );
01302 }
01303 else {
01304 error = errno;
01305 return 0;
01306 }
01307 }
01308
01309 if ( path[strlen(path)-1] != '/' ) {
01310 nasprintf( &my_path, "%s/", path );
01311 }
01312 else {
01313 my_path = (char *)path;
01314 }
01315
01316 static struct dirent * ent;
01317 char * next_file;
01318 static struct stat64 my_stat;
01319 ent = readdir( dir );
01320
01321 while ( ent ) {
01322 if ( (0 != strcmp( ent->d_name, "." )) &&
01323 (0 != strcmp( ent->d_name, ".." )) ) {
01324 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01325 if ( -1 == lstat64( next_file, &my_stat ) ) {
01326 error = errno;
01327 free( next_file );
01328 if ( errno != EACCES ) {
01329 error = errno;
01330 if ( my_path != path ) free( my_path );
01331 closedir( dir );
01332 return 0;
01333 }
01334 }
01335 else if ( S_ISDIR( my_stat.st_mode ) &&
01336 !S_ISLNK( my_stat.st_mode )) {
01337 free( next_file );
01338 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01339 static unsigned int no_watch;
01340 static char const ** exclude_entry;
01341
01342 no_watch = 0;
01343 for (exclude_entry = exclude_list;
01344 exclude_entry && *exclude_entry && !no_watch;
01345 ++exclude_entry) {
01346 static int exclude_length;
01347
01348 exclude_length = strlen(*exclude_entry);
01349 if ((*exclude_entry)[exclude_length-1] == '/') {
01350 --exclude_length;
01351 }
01352 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01353 !strncmp(*exclude_entry, next_file, exclude_length)) {
01354
01355 no_watch = 1;
01356 }
01357 }
01358 if (!no_watch) {
01359 static int status;
01360 status = inotifytools_watch_recursively_with_exclude(
01361 next_file,
01362 events,
01363 exclude_list );
01364
01365 if ( !status && (EACCES != error) && (ENOENT != error) &&
01366 (ELOOP != error) ) {
01367 free( next_file );
01368 if ( my_path != path ) free( my_path );
01369 closedir( dir );
01370 return 0;
01371 }
01372 }
01373 free( next_file );
01374 }
01375 else {
01376 free( next_file );
01377 }
01378 }
01379 ent = readdir( dir );
01380 error = 0;
01381 }
01382
01383 closedir( dir );
01384
01385 int ret = inotifytools_watch_file( my_path, events );
01386 if ( my_path != path ) free( my_path );
01387 return ret;
01388 }
01389
01393 void record_stats( struct inotify_event const * event ) {
01394 if (!event) return;
01395 watch *w = watch_from_wd(event->wd);
01396 if (!w) return;
01397 if ( IN_ACCESS & event->mask ) {
01398 ++w->hit_access;
01399 ++num_access;
01400 }
01401 if ( IN_MODIFY & event->mask ) {
01402 ++w->hit_modify;
01403 ++num_modify;
01404 }
01405 if ( IN_ATTRIB & event->mask ) {
01406 ++w->hit_attrib;
01407 ++num_attrib;
01408 }
01409 if ( IN_CLOSE_WRITE & event->mask ) {
01410 ++w->hit_close_write;
01411 ++num_close_write;
01412 }
01413 if ( IN_CLOSE_NOWRITE & event->mask ) {
01414 ++w->hit_close_nowrite;
01415 ++num_close_nowrite;
01416 }
01417 if ( IN_OPEN & event->mask ) {
01418 ++w->hit_open;
01419 ++num_open;
01420 }
01421 if ( IN_MOVED_FROM & event->mask ) {
01422 ++w->hit_moved_from;
01423 ++num_moved_from;
01424 }
01425 if ( IN_MOVED_TO & event->mask ) {
01426 ++w->hit_moved_to;
01427 ++num_moved_to;
01428 }
01429 if ( IN_CREATE & event->mask ) {
01430 ++w->hit_create;
01431 ++num_create;
01432 }
01433 if ( IN_DELETE & event->mask ) {
01434 ++w->hit_delete;
01435 ++num_delete;
01436 }
01437 if ( IN_DELETE_SELF & event->mask ) {
01438 ++w->hit_delete_self;
01439 ++num_delete_self;
01440 }
01441 if ( IN_UNMOUNT & event->mask ) {
01442 ++w->hit_unmount;
01443 ++num_unmount;
01444 }
01445 if ( IN_MOVE_SELF & event->mask ) {
01446 ++w->hit_move_self;
01447 ++num_move_self;
01448 }
01449
01450 ++w->hit_total;
01451 ++num_total;
01452
01453 }
01454
01455 int *stat_ptr(watch *w, int event)
01456 {
01457 if ( IN_ACCESS == event )
01458 return &w->hit_access;
01459 if ( IN_MODIFY == event )
01460 return &w->hit_modify;
01461 if ( IN_ATTRIB == event )
01462 return &w->hit_attrib;
01463 if ( IN_CLOSE_WRITE == event )
01464 return &w->hit_close_write;
01465 if ( IN_CLOSE_NOWRITE == event )
01466 return &w->hit_close_nowrite;
01467 if ( IN_OPEN == event )
01468 return &w->hit_open;
01469 if ( IN_MOVED_FROM == event )
01470 return &w->hit_moved_from;
01471 if ( IN_MOVED_TO == event )
01472 return &w->hit_moved_to;
01473 if ( IN_CREATE == event )
01474 return &w->hit_create;
01475 if ( IN_DELETE == event )
01476 return &w->hit_delete;
01477 if ( IN_DELETE_SELF == event )
01478 return &w->hit_delete_self;
01479 if ( IN_UNMOUNT == event )
01480 return &w->hit_unmount;
01481 if ( IN_MOVE_SELF == event )
01482 return &w->hit_move_self;
01483 if ( 0 == event )
01484 return &w->hit_total;
01485 return 0;
01486 }
01487
01503 int inotifytools_get_stat_by_wd( int wd, int event ) {
01504 if (!collect_stats) return -1;
01505
01506 watch *w = watch_from_wd(wd);
01507 if (!w) return -1;
01508 int *i = stat_ptr(w, event);
01509 if (!i) return -1;
01510 return *i;
01511 }
01512
01526 int inotifytools_get_stat_total( int event ) {
01527 if (!collect_stats) return -1;
01528 if ( IN_ACCESS == event )
01529 return num_access;
01530 if ( IN_MODIFY == event )
01531 return num_modify;
01532 if ( IN_ATTRIB == event )
01533 return num_attrib;
01534 if ( IN_CLOSE_WRITE == event )
01535 return num_close_write;
01536 if ( IN_CLOSE_NOWRITE == event )
01537 return num_close_nowrite;
01538 if ( IN_OPEN == event )
01539 return num_open;
01540 if ( IN_MOVED_FROM == event )
01541 return num_moved_from;
01542 if ( IN_MOVED_TO == event )
01543 return num_moved_to;
01544 if ( IN_CREATE == event )
01545 return num_create;
01546 if ( IN_DELETE == event )
01547 return num_delete;
01548 if ( IN_DELETE_SELF == event )
01549 return num_delete_self;
01550 if ( IN_UNMOUNT == event )
01551 return num_unmount;
01552 if ( IN_MOVE_SELF == event )
01553 return num_move_self;
01554
01555 if ( 0 == event )
01556 return num_total;
01557
01558 return -1;
01559 }
01560
01580 int inotifytools_get_stat_by_filename( char const * filename,
01581 int event ) {
01582 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01583 filename ), event );
01584 }
01585
01596 int inotifytools_error() {
01597 return error;
01598 }
01599
01603 int isdir( char const * path ) {
01604 static struct stat64 my_stat;
01605
01606 if ( -1 == lstat64( path, &my_stat ) ) {
01607 if (errno == ENOENT) return 0;
01608 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01609 return 0;
01610 }
01611
01612 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01613 }
01614
01615
01622 int inotifytools_get_num_watches() {
01623 int ret = 0;
01624 rbwalk(tree_filename, get_num, (void*)&ret);
01625 return ret;
01626 }
01627
01668 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01669 return inotifytools_fprintf( stdout, event, fmt );
01670 }
01671
01713 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01714 static char out[MAX_STRLEN+1];
01715 static int ret;
01716 ret = inotifytools_sprintf( out, event, fmt );
01717 if ( -1 != ret ) fprintf( file, "%s", out );
01718 return ret;
01719 }
01720
01771 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01772 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01773 }
01774
01775
01822 int inotifytools_snprintf( char * out, int size,
01823 struct inotify_event* event, char* fmt ) {
01824 static char * filename, * eventname, * eventstr;
01825 static unsigned int i, ind;
01826 static char ch1;
01827 static char timestr[MAX_STRLEN];
01828 static time_t now;
01829
01830
01831 if ( event->len > 0 ) {
01832 eventname = event->name;
01833 }
01834 else {
01835 eventname = NULL;
01836 }
01837
01838
01839 filename = inotifytools_filename_from_wd( event->wd );
01840
01841 if ( !fmt || 0 == strlen(fmt) ) {
01842 error = EINVAL;
01843 return -1;
01844 }
01845 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01846 error = EMSGSIZE;
01847 return -1;
01848 }
01849
01850 ind = 0;
01851 for ( i = 0; i < strlen(fmt) &&
01852 (int)ind < size - 1; ++i ) {
01853 if ( fmt[i] != '%' ) {
01854 out[ind++] = fmt[i];
01855 continue;
01856 }
01857
01858 if ( i == strlen(fmt) - 1 ) {
01859
01860 error = EINVAL;
01861 return ind;
01862 }
01863
01864 ch1 = fmt[i+1];
01865
01866 if ( ch1 == '%' ) {
01867 out[ind++] = '%';
01868 ++i;
01869 continue;
01870 }
01871
01872 if ( ch1 == 'w' ) {
01873 if ( filename ) {
01874 strncpy( &out[ind], filename, size - ind );
01875 ind += strlen(filename);
01876 }
01877 ++i;
01878 continue;
01879 }
01880
01881 if ( ch1 == 'f' ) {
01882 if ( eventname ) {
01883 strncpy( &out[ind], eventname, size - ind );
01884 ind += strlen(eventname);
01885 }
01886 ++i;
01887 continue;
01888 }
01889
01890 if ( ch1 == 'e' ) {
01891 eventstr = inotifytools_event_to_str( event->mask );
01892 strncpy( &out[ind], eventstr, size - ind );
01893 ind += strlen(eventstr);
01894 ++i;
01895 continue;
01896 }
01897
01898 if ( ch1 == 'T' ) {
01899
01900 if ( timefmt ) {
01901
01902 now = time(0);
01903 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01904 localtime( &now ) ) ) {
01905
01906
01907 error = EINVAL;
01908 return ind;
01909 }
01910 }
01911 else {
01912 timestr[0] = 0;
01913 }
01914
01915 strncpy( &out[ind], timestr, size - ind );
01916 ind += strlen(timestr);
01917 ++i;
01918 continue;
01919 }
01920
01921
01922 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01923 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01924 strncpy( &out[ind], eventstr, size - ind );
01925 ind += strlen(eventstr);
01926 i += 2;
01927 continue;
01928 }
01929
01930
01931 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01932 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01933 ++i;
01934 }
01935 out[ind] = 0;
01936
01937 return ind - 1;
01938 }
01939
01949 void inotifytools_set_printf_timefmt( char * fmt ) {
01950 timefmt = fmt;
01951 }
01952
01961 int inotifytools_get_max_queued_events() {
01962 int ret;
01963 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01964 return ret;
01965 }
01966
01976 int inotifytools_get_max_user_instances() {
01977 int ret;
01978 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01979 return ret;
01980 }
01981
01991 int inotifytools_get_max_user_watches() {
01992 int ret;
01993 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01994 return ret;
01995 }
01996
02010 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
02011 if (!pattern) {
02012 if (regex) {
02013 regfree(regex);
02014 free(regex);
02015 regex = 0;
02016 }
02017 return 1;
02018 }
02019
02020 if (regex) { regfree(regex); }
02021 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
02022
02023 invert_regexp = invert;
02024 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
02025 if (0 == ret) return 1;
02026
02027 regfree(regex);
02028 free(regex);
02029 regex = 0;
02030 error = EINVAL;
02031 return 0;
02032 }
02033
02045 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
02046 return do_ignore_events_by_regex(pattern, flags, 0);
02047 }
02048
02060 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
02061 return do_ignore_events_by_regex(pattern, flags, 1);
02062 }
02063
02064 int event_compare(const void *p1, const void *p2, const void *config)
02065 {
02066 if (!p1 || !p2) return p1 - p2;
02067 char asc = 1;
02068 int sort_event = (int)config;
02069 if (sort_event == -1) {
02070 sort_event = 0;
02071 asc = 0;
02072 } else if (sort_event < 0) {
02073 sort_event = -sort_event;
02074 asc = 0;
02075 }
02076 int *i1 = stat_ptr((watch*)p1, sort_event);
02077 int *i2 = stat_ptr((watch*)p2, sort_event);
02078 if (0 == *i1 - *i2) {
02079 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02080 }
02081 if (asc)
02082 return *i1 - *i2;
02083 else
02084 return *i2 - *i1;
02085 }
02086
02087 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02088 {
02089 struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02090 RBLIST *all = rbopenlist(tree_wd);
02091 void const *p = rbreadlist(all);
02092 while (p) {
02093 void const *r = rbsearch(p, ret);
02094 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02095 p = rbreadlist(all);
02096 }
02097 rbcloselist(all);
02098 return ret;
02099 }