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 int isdir( char const * path );
00154 void record_stats( struct inotify_event const * event );
00155 int onestr_to_event(char const * event);
00156
00174 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00175 #cond, mesg)
00176
00177 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00178
00196 void _niceassert( long cond, int line, char const * file, char const * condstr,
00197 char const * mesg ) {
00198 if ( cond ) return;
00199
00200 if ( mesg ) {
00201 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00202 condstr, mesg );
00203 }
00204 else {
00205 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00206 }
00207 }
00208
00218 char * chrtostr(char ch) {
00219 static char str[2] = { '\0', '\0' };
00220 str[0] = ch;
00221 return str;
00222 }
00223
00227 int read_num_from_file( char * filename, int * num ) {
00228 FILE * file = fopen( filename, "r" );
00229 if ( !file ) {
00230 error = errno;
00231 return 0;
00232 }
00233
00234 if ( EOF == fscanf( file, "%d", num ) ) {
00235 error = errno;
00236 return 0;
00237 }
00238
00239 niceassert( 0 == fclose( file ), 0 );
00240
00241 return 1;
00242 }
00243
00244 int wd_compare(const void *d1, const void *d2, const void *config) {
00245 if (!d1 || !d2) return d1 - d2;
00246 return ((watch*)d1)->wd - ((watch*)d2)->wd;
00247 }
00248
00249 int filename_compare(const void *d1, const void *d2, const void *config) {
00250 if (!d1 || !d2) return d1 - d2;
00251 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
00252 }
00253
00257 watch *watch_from_wd( int wd ) {
00258 watch w;
00259 w.wd = wd;
00260 return (watch*)rbfind(&w, tree_wd);
00261 }
00262
00266 watch *watch_from_filename( char const *filename ) {
00267 watch w;
00268 w.filename = (char*)filename;
00269 return (watch*)rbfind(&w, tree_filename);
00270 }
00271
00281 int inotifytools_initialize() {
00282 if (init) return 1;
00283
00284 error = 0;
00285
00286 inotify_fd = inotify_init();
00287 if (inotify_fd < 0) {
00288 error = inotify_fd;
00289 return 0;
00290 }
00291
00292 collect_stats = 0;
00293 init = 1;
00294 tree_wd = rbinit(wd_compare, 0);
00295 tree_filename = rbinit(filename_compare, 0);
00296 timefmt = 0;
00297
00298 return 1;
00299 }
00300
00304 void destroy_watch(watch *w) {
00305 if (w->filename) free(w->filename);
00306 free(w);
00307 }
00308
00312 void cleanup_tree(const void *nodep,
00313 const VISIT which,
00314 const int depth, void* arg) {
00315 if (which != endorder && which != leaf) return;
00316 watch *w = (watch*)nodep;
00317 destroy_watch(w);
00318 }
00319
00326 void inotifytools_cleanup() {
00327 if (!init) return;
00328
00329 init = 0;
00330 close(inotify_fd);
00331 collect_stats = 0;
00332 error = 0;
00333 timefmt = 0;
00334
00335 if (regex) {
00336 regfree(regex);
00337 free(regex);
00338 regex = 0;
00339 }
00340
00341 rbwalk(tree_wd, cleanup_tree, 0);
00342 rbdestroy(tree_wd); tree_wd = 0;
00343 rbdestroy(tree_filename); tree_filename = 0;
00344 }
00345
00349 void empty_stats(const void *nodep,
00350 const VISIT which,
00351 const int depth, void *arg) {
00352 if (which != endorder && which != leaf) return;
00353 watch *w = (watch*)nodep;
00354 w->hit_access = 0;
00355 w->hit_modify = 0;
00356 w->hit_attrib = 0;
00357 w->hit_close_nowrite = 0;
00358 w->hit_close_write = 0;
00359 w->hit_open = 0;
00360 w->hit_move_self = 0;
00361 w->hit_moved_from = 0;
00362 w->hit_moved_to = 0;
00363 w->hit_create = 0;
00364 w->hit_delete = 0;
00365 w->hit_delete_self = 0;
00366 w->hit_unmount = 0;
00367 w->hit_total = 0;
00368 }
00369
00373 void replace_filename(const void *nodep,
00374 const VISIT which,
00375 const int depth, void *arg) {
00376 if (which != endorder && which != leaf) return;
00377 watch *w = (watch*)nodep;
00378 char *old_name = ((char**)arg)[0];
00379 char *new_name = ((char**)arg)[1];
00380 int old_len = *((int*)&((char**)arg)[2]);
00381 char *name;
00382 if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
00383 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
00384 if (!strcmp( w->filename, new_name )) {
00385 free(name);
00386 } else {
00387 rbdelete(w, tree_filename);
00388 free( w->filename );
00389 w->filename = name;
00390 rbsearch(w, tree_filename);
00391 }
00392 }
00393 }
00394
00398 void get_num(const void *nodep,
00399 const VISIT which,
00400 const int depth, void *arg) {
00401 if (which != endorder && which != leaf) return;
00402 ++(*((int*)arg));
00403 }
00404
00405
00418 void inotifytools_initialize_stats() {
00419 niceassert( init, "inotifytools_initialize not called yet" );
00420
00421
00422 if (collect_stats) {
00423 rbwalk(tree_wd, empty_stats, 0);
00424 }
00425
00426 num_access = 0;
00427 num_modify = 0;
00428 num_attrib = 0;
00429 num_close_nowrite = 0;
00430 num_close_write = 0;
00431 num_open = 0;
00432 num_move_self = 0;
00433 num_moved_from = 0;
00434 num_moved_to = 0;
00435 num_create = 0;
00436 num_delete = 0;
00437 num_delete_self = 0;
00438 num_unmount = 0;
00439 num_total = 0;
00440
00441 collect_stats = 1;
00442 }
00443
00471 int inotifytools_str_to_event_sep(char const * event, char sep) {
00472 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00473 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00474 return -1;
00475 }
00476
00477 int ret, ret1, len;
00478 char * event1, * event2;
00479 char eventstr[4096];
00480 ret = 0;
00481
00482 if ( !event || !event[0] ) return 0;
00483
00484 event1 = (char *)event;
00485 event2 = strchr( event1, sep );
00486 while ( event1 && event1[0] ) {
00487 if ( event2 ) {
00488 len = event2 - event1;
00489 niceassert( len < 4096, "malformed event string (very long)" );
00490 }
00491 else {
00492 len = strlen(event1);
00493 }
00494 if ( len > 4095 ) len = 4095;
00495 strncpy( eventstr, event1, len );
00496 eventstr[len] = 0;
00497
00498 ret1 = onestr_to_event( eventstr );
00499 if ( 0 == ret1 || -1 == ret1 ) {
00500 ret = ret1;
00501 break;
00502 }
00503 ret |= ret1;
00504
00505 event1 = event2;
00506 if ( event1 && event1[0] ) {
00507
00508 ++event1;
00509
00510 if ( !event1[0] ) return 0;
00511 event2 = strchr( event1, sep );
00512 }
00513 }
00514
00515 return ret;
00516 }
00517
00541 int inotifytools_str_to_event(char const * event) {
00542 return inotifytools_str_to_event_sep( event, ',' );
00543 }
00544
00556 int onestr_to_event(char const * event)
00557 {
00558 static int ret;
00559 ret = -1;
00560
00561 if ( !event || !event[0] )
00562 ret = 0;
00563 else if ( 0 == strcasecmp(event, "ACCESS") )
00564 ret = IN_ACCESS;
00565 else if ( 0 == strcasecmp(event, "MODIFY") )
00566 ret = IN_MODIFY;
00567 else if ( 0 == strcasecmp(event, "ATTRIB") )
00568 ret = IN_ATTRIB;
00569 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00570 ret = IN_CLOSE_WRITE;
00571 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00572 ret = IN_CLOSE_NOWRITE;
00573 else if ( 0 == strcasecmp(event, "OPEN") )
00574 ret = IN_OPEN;
00575 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00576 ret = IN_MOVED_FROM;
00577 else if ( 0 == strcasecmp(event, "MOVED_TO") )
00578 ret = IN_MOVED_TO;
00579 else if ( 0 == strcasecmp(event, "CREATE") )
00580 ret = IN_CREATE;
00581 else if ( 0 == strcasecmp(event, "DELETE") )
00582 ret = IN_DELETE;
00583 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00584 ret = IN_DELETE_SELF;
00585 else if ( 0 == strcasecmp(event, "UNMOUNT") )
00586 ret = IN_UNMOUNT;
00587 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00588 ret = IN_Q_OVERFLOW;
00589 else if ( 0 == strcasecmp(event, "IGNORED") )
00590 ret = IN_IGNORED;
00591 else if ( 0 == strcasecmp(event, "CLOSE") )
00592 ret = IN_CLOSE;
00593 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00594 ret = IN_MOVE_SELF;
00595 else if ( 0 == strcasecmp(event, "MOVE") )
00596 ret = IN_MOVE;
00597 else if ( 0 == strcasecmp(event, "ISDIR") )
00598 ret = IN_ISDIR;
00599 else if ( 0 == strcasecmp(event, "ONESHOT") )
00600 ret = IN_ONESHOT;
00601 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00602 ret = IN_ALL_EVENTS;
00603
00604 return ret;
00605 }
00606
00628 char * inotifytools_event_to_str(int events) {
00629 return inotifytools_event_to_str_sep(events, ',');
00630 }
00631
00656 char * inotifytools_event_to_str_sep(int events, char sep)
00657 {
00658 static char ret[1024];
00659 ret[0] = '\0';
00660 ret[1] = '\0';
00661
00662 if ( IN_ACCESS & events ) {
00663 strcat( ret, chrtostr(sep) );
00664 strcat( ret, "ACCESS" );
00665 }
00666 if ( IN_MODIFY & events ) {
00667 strcat( ret, chrtostr(sep) );
00668 strcat( ret, "MODIFY" );
00669 }
00670 if ( IN_ATTRIB & events ) {
00671 strcat( ret, chrtostr(sep) );
00672 strcat( ret, "ATTRIB" );
00673 }
00674 if ( IN_CLOSE_WRITE & events ) {
00675 strcat( ret, chrtostr(sep) );
00676 strcat( ret, "CLOSE_WRITE" );
00677 }
00678 if ( IN_CLOSE_NOWRITE & events ) {
00679 strcat( ret, chrtostr(sep) );
00680 strcat( ret, "CLOSE_NOWRITE" );
00681 }
00682 if ( IN_OPEN & events ) {
00683 strcat( ret, chrtostr(sep) );
00684 strcat( ret, "OPEN" );
00685 }
00686 if ( IN_MOVED_FROM & events ) {
00687 strcat( ret, chrtostr(sep) );
00688 strcat( ret, "MOVED_FROM" );
00689 }
00690 if ( IN_MOVED_TO & events ) {
00691 strcat( ret, chrtostr(sep) );
00692 strcat( ret, "MOVED_TO" );
00693 }
00694 if ( IN_CREATE & events ) {
00695 strcat( ret, chrtostr(sep) );
00696 strcat( ret, "CREATE" );
00697 }
00698 if ( IN_DELETE & events ) {
00699 strcat( ret, chrtostr(sep) );
00700 strcat( ret, "DELETE" );
00701 }
00702 if ( IN_DELETE_SELF & events ) {
00703 strcat( ret, chrtostr(sep) );
00704 strcat( ret, "DELETE_SELF" );
00705 }
00706 if ( IN_UNMOUNT & events ) {
00707 strcat( ret, chrtostr(sep) );
00708 strcat( ret, "UNMOUNT" );
00709 }
00710 if ( IN_Q_OVERFLOW & events ) {
00711 strcat( ret, chrtostr(sep) );
00712 strcat( ret, "Q_OVERFLOW" );
00713 }
00714 if ( IN_IGNORED & events ) {
00715 strcat( ret, chrtostr(sep) );
00716 strcat( ret, "IGNORED" );
00717 }
00718 if ( IN_CLOSE & events ) {
00719 strcat( ret, chrtostr(sep) );
00720 strcat( ret, "CLOSE" );
00721 }
00722 if ( IN_MOVE_SELF & events ) {
00723 strcat( ret, chrtostr(sep) );
00724 strcat( ret, "MOVE_SELF" );
00725 }
00726 if ( IN_ISDIR & events ) {
00727 strcat( ret, chrtostr(sep) );
00728 strcat( ret, "ISDIR" );
00729 }
00730 if ( IN_ONESHOT & events ) {
00731 strcat( ret, chrtostr(sep) );
00732 strcat( ret, "ONESHOT" );
00733 }
00734
00735
00736 if (ret[0] == '\0') {
00737 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00738 }
00739
00740 return &ret[1];
00741 }
00742
00763 char * inotifytools_filename_from_wd( int wd ) {
00764 niceassert( init, "inotifytools_initialize not called yet" );
00765 watch *w = watch_from_wd(wd);
00766 if (!w)
00767 return NULL;
00768
00769 return w->filename;
00770 }
00771
00786 int inotifytools_wd_from_filename( char const * filename ) {
00787 niceassert( init, "inotifytools_initialize not called yet" );
00788 watch *w = watch_from_filename(filename);
00789 if (!w) return -1;
00790 return w->wd;
00791 }
00792
00807 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00808 niceassert( init, "inotifytools_initialize not called yet" );
00809 watch *w = watch_from_wd(wd);
00810 if (!w) return;
00811 if (w->filename) free(w->filename);
00812 w->filename = strdup(filename);
00813 }
00814
00829 void inotifytools_set_filename_by_filename( char const * oldname,
00830 char const * newname ) {
00831 watch *w = watch_from_filename(oldname);
00832 if (!w) return;
00833 if (w->filename) free(w->filename);
00834 w->filename = strdup(newname);
00835 }
00836
00859 void inotifytools_replace_filename( char const * oldname,
00860 char const * newname ) {
00861 if ( !oldname || !newname ) return;
00862 char *names[2+sizeof(int)/sizeof(char*)];
00863 names[0] = (char*)oldname;
00864 names[1] = (char*)newname;
00865 *((int*)&names[2]) = strlen(oldname);
00866 rbwalk(tree_filename, replace_filename, (void*)names);
00867 }
00868
00872 int remove_inotify_watch(watch *w) {
00873 error = 0;
00874 int status = inotify_rm_watch( inotify_fd, w->wd );
00875 if ( status < 0 ) {
00876 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
00877 strerror(status) );
00878 error = status;
00879 return 0;
00880 }
00881 return 1;
00882 }
00883
00887 watch *create_watch(int wd, char *filename) {
00888 if ( wd <= 0 || !filename) return 0;
00889
00890 watch *w = (watch*)calloc(1, sizeof(watch));
00891 w->wd = wd;
00892 w->filename = strdup(filename);
00893 rbsearch(w, tree_wd);
00894 rbsearch(w, tree_filename);
00895 return w;
00896 }
00897
00910 int inotifytools_remove_watch_by_wd( int wd ) {
00911 niceassert( init, "inotifytools_initialize not called yet" );
00912 watch *w = watch_from_wd(wd);
00913 if (!w) return 1;
00914
00915 if (!remove_inotify_watch(w)) return 0;
00916 rbdelete(w, tree_wd);
00917 rbdelete(w, tree_filename);
00918 destroy_watch(w);
00919 return 1;
00920 }
00921
00933 int inotifytools_remove_watch_by_filename( char const * filename ) {
00934 niceassert( init, "inotifytools_initialize not called yet" );
00935 watch *w = watch_from_filename(filename);
00936 if (!w) return 1;
00937
00938 if (!remove_inotify_watch(w)) return 0;
00939 rbdelete(w, tree_wd);
00940 rbdelete(w, tree_filename);
00941 destroy_watch(w);
00942 return 1;
00943 }
00944
00956 int inotifytools_watch_file( char const * filename, int events ) {
00957 static char const * filenames[2];
00958 filenames[0] = filename;
00959 filenames[1] = NULL;
00960 return inotifytools_watch_files( filenames, events );
00961 }
00962
00978 int inotifytools_watch_files( char const * filenames[], int events ) {
00979 niceassert( init, "inotifytools_initialize not called yet" );
00980 error = 0;
00981
00982 static int i;
00983 for ( i = 0; filenames[i]; ++i ) {
00984 static int wd;
00985 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00986 if ( wd < 0 ) {
00987 if ( wd == -1 ) {
00988 error = errno;
00989 return 0;
00990 }
00991 else {
00992 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00993 "(expected -1 or >0 )", filenames[i], wd );
00994
00995 return 0;
00996 }
00997 }
00998
00999 char *filename;
01000
01001 if ( !isdir(filenames[i])
01002 || filenames[i][strlen(filenames[i])-1] == '/') {
01003 filename = strdup(filenames[i]);
01004 }
01005 else {
01006 nasprintf( &filename, "%s/", filenames[i] );
01007 }
01008 create_watch(wd, filename);
01009 free(filename);
01010 }
01011
01012 return 1;
01013 }
01014
01041 struct inotify_event * inotifytools_next_event( int timeout ) {
01042 return inotifytools_next_events( timeout, 1 );
01043 }
01044
01045
01095 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01096 niceassert( init, "inotifytools_initialize not called yet" );
01097 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01098
01099 if ( num_events < 1 ) return NULL;
01100
01101 static struct inotify_event event[MAX_EVENTS];
01102 static struct inotify_event * ret;
01103 static int first_byte = 0;
01104 static ssize_t bytes;
01105 static jmp_buf jmp;
01106 static char match_name[MAX_STRLEN];
01107
01108 #define RETURN(A) {\
01109 if (regex) {\
01110 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01111 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01112 longjmp(jmp,0);\
01113 }\
01114 }\
01115 if ( collect_stats ) {\
01116 record_stats( A );\
01117 }\
01118 return A;\
01119 }
01120
01121 setjmp(jmp);
01122
01123 error = 0;
01124
01125
01126 if ( first_byte != 0
01127 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01128
01129 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01130 first_byte += sizeof(struct inotify_event) + ret->len;
01131
01132
01133
01134 if ( first_byte == bytes ) {
01135 first_byte = 0;
01136 }
01137 else if ( first_byte > bytes ) {
01138
01139
01140
01141
01142
01143
01144 niceassert( (long)((char *)&event[0] +
01145 sizeof(struct inotify_event) +
01146 event[0].len) <= (long)ret,
01147 "extremely unlucky user, death imminent" );
01148
01149 bytes = (char *)&event[0] + bytes - (char *)ret;
01150 memcpy( &event[0], ret, bytes );
01151 return inotifytools_next_events( timeout, num_events );
01152 }
01153 RETURN(ret);
01154
01155 }
01156
01157 else if ( first_byte == 0 ) {
01158 bytes = 0;
01159 }
01160
01161
01162 static ssize_t this_bytes;
01163 static unsigned int bytes_to_read;
01164 static int rc;
01165 static fd_set read_fds;
01166
01167 static struct timeval read_timeout;
01168 read_timeout.tv_sec = timeout;
01169 read_timeout.tv_usec = 0;
01170 static struct timeval * read_timeout_ptr;
01171 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01172
01173 FD_ZERO(&read_fds);
01174 FD_SET(inotify_fd, &read_fds);
01175 rc = select(inotify_fd + 1, &read_fds,
01176 NULL, NULL, read_timeout_ptr);
01177 if ( rc < 0 ) {
01178
01179 error = errno;
01180 return NULL;
01181 }
01182 else if ( rc == 0 ) {
01183
01184 return NULL;
01185 }
01186
01187
01188 do {
01189 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01190 } while ( !rc &&
01191 bytes_to_read < sizeof(struct inotify_event)*num_events );
01192
01193 if ( rc == -1 ) {
01194 error = errno;
01195 return NULL;
01196 }
01197
01198 this_bytes = read(inotify_fd, &event[0] + bytes,
01199 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01200 if ( this_bytes < 0 ) {
01201 error = errno;
01202 return NULL;
01203 }
01204 if ( this_bytes == 0 ) {
01205 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
01206 "events occurred at once.\n");
01207 return NULL;
01208 }
01209 bytes += this_bytes;
01210
01211 ret = &event[0];
01212 first_byte = sizeof(struct inotify_event) + ret->len;
01213 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01214 "almost certainly screw up." );
01215 if ( first_byte == bytes ) {
01216 first_byte = 0;
01217 }
01218
01219 RETURN(ret);
01220
01221 #undef RETURN
01222 }
01223
01249 int inotifytools_watch_recursively( char const * path, int events ) {
01250 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01251 }
01252
01285 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01286 char const ** exclude_list ) {
01287 niceassert( init, "inotifytools_initialize not called yet" );
01288
01289 DIR * dir;
01290 char * my_path;
01291 error = 0;
01292 dir = opendir( path );
01293 if ( !dir ) {
01294
01295 if ( errno == ENOTDIR ) {
01296 return inotifytools_watch_file( path, events );
01297 }
01298 else {
01299 error = errno;
01300 return 0;
01301 }
01302 }
01303
01304 if ( path[strlen(path)-1] != '/' ) {
01305 nasprintf( &my_path, "%s/", path );
01306 }
01307 else {
01308 my_path = (char *)path;
01309 }
01310
01311 static struct dirent * ent;
01312 char * next_file;
01313 static struct stat64 my_stat;
01314 ent = readdir( dir );
01315
01316 while ( ent ) {
01317 if ( (0 != strcmp( ent->d_name, "." )) &&
01318 (0 != strcmp( ent->d_name, ".." )) ) {
01319 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01320 if ( -1 == lstat64( next_file, &my_stat ) ) {
01321 error = errno;
01322 free( next_file );
01323 if ( errno != EACCES ) {
01324 error = errno;
01325 if ( my_path != path ) free( my_path );
01326 closedir( dir );
01327 return 0;
01328 }
01329 }
01330 else if ( S_ISDIR( my_stat.st_mode ) &&
01331 !S_ISLNK( my_stat.st_mode )) {
01332 free( next_file );
01333 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01334 static unsigned int no_watch;
01335 static char const ** exclude_entry;
01336
01337 no_watch = 0;
01338 for (exclude_entry = exclude_list;
01339 exclude_entry && *exclude_entry && !no_watch;
01340 ++exclude_entry) {
01341 static int exclude_length;
01342
01343 exclude_length = strlen(*exclude_entry);
01344 if ((*exclude_entry)[exclude_length-1] == '/') {
01345 --exclude_length;
01346 }
01347 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01348 !strncmp(*exclude_entry, next_file, exclude_length)) {
01349
01350 no_watch = 1;
01351 }
01352 }
01353 if (!no_watch) {
01354 static int status;
01355 status = inotifytools_watch_recursively_with_exclude(
01356 next_file,
01357 events,
01358 exclude_list );
01359
01360 if ( !status && (EACCES != error) && (ENOENT != error) &&
01361 (ELOOP != error) ) {
01362 free( next_file );
01363 if ( my_path != path ) free( my_path );
01364 closedir( dir );
01365 return 0;
01366 }
01367 }
01368 free( next_file );
01369 }
01370 else {
01371 free( next_file );
01372 }
01373 }
01374 ent = readdir( dir );
01375 error = 0;
01376 }
01377
01378 closedir( dir );
01379
01380 int ret = inotifytools_watch_file( my_path, events );
01381 if ( my_path != path ) free( my_path );
01382 return ret;
01383 }
01384
01388 void record_stats( struct inotify_event const * event ) {
01389 if (!event) return;
01390 watch *w = watch_from_wd(event->wd);
01391 if (!w) return;
01392 if ( IN_ACCESS & event->mask ) {
01393 ++w->hit_access;
01394 ++num_access;
01395 }
01396 if ( IN_MODIFY & event->mask ) {
01397 ++w->hit_modify;
01398 ++num_modify;
01399 }
01400 if ( IN_ATTRIB & event->mask ) {
01401 ++w->hit_attrib;
01402 ++num_attrib;
01403 }
01404 if ( IN_CLOSE_WRITE & event->mask ) {
01405 ++w->hit_close_write;
01406 ++num_close_write;
01407 }
01408 if ( IN_CLOSE_NOWRITE & event->mask ) {
01409 ++w->hit_close_nowrite;
01410 ++num_close_nowrite;
01411 }
01412 if ( IN_OPEN & event->mask ) {
01413 ++w->hit_open;
01414 ++num_open;
01415 }
01416 if ( IN_MOVED_FROM & event->mask ) {
01417 ++w->hit_moved_from;
01418 ++num_moved_from;
01419 }
01420 if ( IN_MOVED_TO & event->mask ) {
01421 ++w->hit_moved_to;
01422 ++num_moved_to;
01423 }
01424 if ( IN_CREATE & event->mask ) {
01425 ++w->hit_create;
01426 ++num_create;
01427 }
01428 if ( IN_DELETE & event->mask ) {
01429 ++w->hit_delete;
01430 ++num_delete;
01431 }
01432 if ( IN_DELETE_SELF & event->mask ) {
01433 ++w->hit_delete_self;
01434 ++num_delete_self;
01435 }
01436 if ( IN_UNMOUNT & event->mask ) {
01437 ++w->hit_unmount;
01438 ++num_unmount;
01439 }
01440 if ( IN_MOVE_SELF & event->mask ) {
01441 ++w->hit_move_self;
01442 ++num_move_self;
01443 }
01444
01445 ++w->hit_total;
01446 ++num_total;
01447
01448 }
01449
01450 int *stat_ptr(watch *w, int event)
01451 {
01452 if ( IN_ACCESS == event )
01453 return &w->hit_access;
01454 if ( IN_MODIFY == event )
01455 return &w->hit_modify;
01456 if ( IN_ATTRIB == event )
01457 return &w->hit_attrib;
01458 if ( IN_CLOSE_WRITE == event )
01459 return &w->hit_close_write;
01460 if ( IN_CLOSE_NOWRITE == event )
01461 return &w->hit_close_nowrite;
01462 if ( IN_OPEN == event )
01463 return &w->hit_open;
01464 if ( IN_MOVED_FROM == event )
01465 return &w->hit_moved_from;
01466 if ( IN_MOVED_TO == event )
01467 return &w->hit_moved_to;
01468 if ( IN_CREATE == event )
01469 return &w->hit_create;
01470 if ( IN_DELETE == event )
01471 return &w->hit_delete;
01472 if ( IN_DELETE_SELF == event )
01473 return &w->hit_delete_self;
01474 if ( IN_UNMOUNT == event )
01475 return &w->hit_unmount;
01476 if ( IN_MOVE_SELF == event )
01477 return &w->hit_move_self;
01478 if ( 0 == event )
01479 return &w->hit_total;
01480 return 0;
01481 }
01482
01498 int inotifytools_get_stat_by_wd( int wd, int event ) {
01499 if (!collect_stats) return -1;
01500
01501 watch *w = watch_from_wd(wd);
01502 if (!w) return -1;
01503 int *i = stat_ptr(w, event);
01504 if (!i) return -1;
01505 return *i;
01506 }
01507
01521 int inotifytools_get_stat_total( int event ) {
01522 if (!collect_stats) return -1;
01523 if ( IN_ACCESS == event )
01524 return num_access;
01525 if ( IN_MODIFY == event )
01526 return num_modify;
01527 if ( IN_ATTRIB == event )
01528 return num_attrib;
01529 if ( IN_CLOSE_WRITE == event )
01530 return num_close_write;
01531 if ( IN_CLOSE_NOWRITE == event )
01532 return num_close_nowrite;
01533 if ( IN_OPEN == event )
01534 return num_open;
01535 if ( IN_MOVED_FROM == event )
01536 return num_moved_from;
01537 if ( IN_MOVED_TO == event )
01538 return num_moved_to;
01539 if ( IN_CREATE == event )
01540 return num_create;
01541 if ( IN_DELETE == event )
01542 return num_delete;
01543 if ( IN_DELETE_SELF == event )
01544 return num_delete_self;
01545 if ( IN_UNMOUNT == event )
01546 return num_unmount;
01547 if ( IN_MOVE_SELF == event )
01548 return num_move_self;
01549
01550 if ( 0 == event )
01551 return num_total;
01552
01553 return -1;
01554 }
01555
01575 int inotifytools_get_stat_by_filename( char const * filename,
01576 int event ) {
01577 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01578 filename ), event );
01579 }
01580
01591 int inotifytools_error() {
01592 return error;
01593 }
01594
01598 int isdir( char const * path ) {
01599 static struct stat64 my_stat;
01600
01601 if ( -1 == lstat64( path, &my_stat ) ) {
01602 if (errno == ENOENT) return 0;
01603 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01604 return 0;
01605 }
01606
01607 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01608 }
01609
01610
01617 int inotifytools_get_num_watches() {
01618 int ret = 0;
01619 rbwalk(tree_filename, get_num, (void*)&ret);
01620 return ret;
01621 }
01622
01663 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01664 return inotifytools_fprintf( stdout, event, fmt );
01665 }
01666
01708 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01709 static char out[MAX_STRLEN+1];
01710 static int ret;
01711 ret = inotifytools_sprintf( out, event, fmt );
01712 if ( -1 != ret ) fprintf( file, "%s", out );
01713 return ret;
01714 }
01715
01766 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01767 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01768 }
01769
01770
01817 int inotifytools_snprintf( char * out, int size,
01818 struct inotify_event* event, char* fmt ) {
01819 static char * filename, * eventname, * eventstr;
01820 static unsigned int i, ind;
01821 static char ch1;
01822 static char timestr[MAX_STRLEN];
01823 static time_t now;
01824
01825
01826 if ( event->len > 0 ) {
01827 eventname = event->name;
01828 }
01829 else {
01830 eventname = NULL;
01831 }
01832
01833
01834 filename = inotifytools_filename_from_wd( event->wd );
01835
01836 if ( !fmt || 0 == strlen(fmt) ) {
01837 error = EINVAL;
01838 return -1;
01839 }
01840 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01841 error = EMSGSIZE;
01842 return -1;
01843 }
01844
01845 ind = 0;
01846 for ( i = 0; i < strlen(fmt) &&
01847 (int)ind < size - 1; ++i ) {
01848 if ( fmt[i] != '%' ) {
01849 out[ind++] = fmt[i];
01850 continue;
01851 }
01852
01853 if ( i == strlen(fmt) - 1 ) {
01854
01855 error = EINVAL;
01856 return ind;
01857 }
01858
01859 ch1 = fmt[i+1];
01860
01861 if ( ch1 == '%' ) {
01862 out[ind++] = '%';
01863 ++i;
01864 continue;
01865 }
01866
01867 if ( ch1 == 'w' ) {
01868 if ( filename ) {
01869 strncpy( &out[ind], filename, size - ind );
01870 ind += strlen(filename);
01871 }
01872 ++i;
01873 continue;
01874 }
01875
01876 if ( ch1 == 'f' ) {
01877 if ( eventname ) {
01878 strncpy( &out[ind], eventname, size - ind );
01879 ind += strlen(eventname);
01880 }
01881 ++i;
01882 continue;
01883 }
01884
01885 if ( ch1 == 'e' ) {
01886 eventstr = inotifytools_event_to_str( event->mask );
01887 strncpy( &out[ind], eventstr, size - ind );
01888 ind += strlen(eventstr);
01889 ++i;
01890 continue;
01891 }
01892
01893 if ( ch1 == 'T' ) {
01894
01895 if ( timefmt ) {
01896
01897 now = time(0);
01898 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01899 localtime( &now ) ) ) {
01900
01901
01902 error = EINVAL;
01903 return ind;
01904 }
01905 }
01906 else {
01907 timestr[0] = 0;
01908 }
01909
01910 strncpy( &out[ind], timestr, size - ind );
01911 ind += strlen(timestr);
01912 ++i;
01913 continue;
01914 }
01915
01916
01917 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01918 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01919 strncpy( &out[ind], eventstr, size - ind );
01920 ind += strlen(eventstr);
01921 i += 2;
01922 continue;
01923 }
01924
01925
01926 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01927 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01928 ++i;
01929 }
01930 out[ind] = 0;
01931
01932 return ind - 1;
01933 }
01934
01944 void inotifytools_set_printf_timefmt( char * fmt ) {
01945 timefmt = fmt;
01946 }
01947
01956 int inotifytools_get_max_queued_events() {
01957 int ret;
01958 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01959 return ret;
01960 }
01961
01971 int inotifytools_get_max_user_instances() {
01972 int ret;
01973 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01974 return ret;
01975 }
01976
01986 int inotifytools_get_max_user_watches() {
01987 int ret;
01988 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01989 return ret;
01990 }
01991
02003 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
02004 if (!pattern) {
02005 if (regex) {
02006 regfree(regex);
02007 free(regex);
02008 regex = 0;
02009 }
02010 return 1;
02011 }
02012
02013 if (regex) { regfree(regex); }
02014 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
02015
02016 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
02017 if (0 == ret) return 1;
02018
02019 regfree(regex);
02020 free(regex);
02021 regex = 0;
02022 error = EINVAL;
02023 return 0;
02024 }
02025
02026 int event_compare(const void *p1, const void *p2, const void *config)
02027 {
02028 if (!p1 || !p2) return p1 - p2;
02029 char asc = 1;
02030 int sort_event = (int)config;
02031 if (sort_event == -1) {
02032 sort_event = 0;
02033 asc = 0;
02034 } else if (sort_event < 0) {
02035 sort_event = -sort_event;
02036 asc = 0;
02037 }
02038 int *i1 = stat_ptr((watch*)p1, sort_event);
02039 int *i2 = stat_ptr((watch*)p2, sort_event);
02040 if (0 == *i1 - *i2) {
02041 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02042 }
02043 if (asc)
02044 return *i1 - *i2;
02045 else
02046 return *i2 - *i1;
02047 }
02048
02049 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02050 {
02051 struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02052 RBLIST *all = rbopenlist(tree_wd);
02053 void const *p = rbreadlist(all);
02054 while (p) {
02055 void const *r = rbsearch(p, ret);
02056 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02057 p = rbreadlist(all);
02058 }
02059 rbcloselist(all);
02060 return ret;
02061 }