libinotifytools
|
00001 // kate: replace-tabs off; space-indent off; 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 // Try to initialise inotify 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 // if already collecting stats, reset stats 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 // jump over 'sep' character 00508 ++event1; 00509 // if last character was 'sep'... 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 // Maybe we didn't match any... ? 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 } 00896 00909 int inotifytools_remove_watch_by_wd( int wd ) { 00910 niceassert( init, "inotifytools_initialize not called yet" ); 00911 watch *w = watch_from_wd(wd); 00912 if (!w) return 1; 00913 00914 if (!remove_inotify_watch(w)) return 0; 00915 rbdelete(w, tree_wd); 00916 rbdelete(w, tree_filename); 00917 destroy_watch(w); 00918 return 1; 00919 } 00920 00932 int inotifytools_remove_watch_by_filename( char const * filename ) { 00933 niceassert( init, "inotifytools_initialize not called yet" ); 00934 watch *w = watch_from_filename(filename); 00935 if (!w) return 1; 00936 00937 if (!remove_inotify_watch(w)) return 0; 00938 rbdelete(w, tree_wd); 00939 rbdelete(w, tree_filename); 00940 destroy_watch(w); 00941 return 1; 00942 } 00943 00955 int inotifytools_watch_file( char const * filename, int events ) { 00956 static char const * filenames[2]; 00957 filenames[0] = filename; 00958 filenames[1] = NULL; 00959 return inotifytools_watch_files( filenames, events ); 00960 } 00961 00977 int inotifytools_watch_files( char const * filenames[], int events ) { 00978 niceassert( init, "inotifytools_initialize not called yet" ); 00979 error = 0; 00980 00981 static int i; 00982 for ( i = 0; filenames[i]; ++i ) { 00983 static int wd; 00984 wd = inotify_add_watch( inotify_fd, filenames[i], events ); 00985 if ( wd < 0 ) { 00986 if ( wd == -1 ) { 00987 error = errno; 00988 return 0; 00989 } // if ( wd == -1 ) 00990 else { 00991 fprintf( stderr, "Failed to watch %s: returned wd was %d " 00992 "(expected -1 or >0 )", filenames[i], wd ); 00993 // no appropriate value for error 00994 return 0; 00995 } // else 00996 } // if ( wd < 0 ) 00997 00998 char *filename; 00999 // Always end filename with / if it is a directory 01000 if ( !isdir(filenames[i]) 01001 || filenames[i][strlen(filenames[i])-1] == '/') { 01002 filename = strdup(filenames[i]); 01003 } 01004 else { 01005 nasprintf( &filename, "%s/", filenames[i] ); 01006 } 01007 create_watch(wd, filename); 01008 free(filename); 01009 } // for 01010 01011 return 1; 01012 } 01013 01040 struct inotify_event * inotifytools_next_event( int timeout ) { 01041 return inotifytools_next_events( timeout, 1 ); 01042 } 01043 01044 01094 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) { 01095 niceassert( init, "inotifytools_initialize not called yet" ); 01096 niceassert( num_events <= MAX_EVENTS, "too many events requested" ); 01097 01098 if ( num_events < 1 ) return NULL; 01099 01100 static struct inotify_event event[MAX_EVENTS]; 01101 static struct inotify_event * ret; 01102 static int first_byte = 0; 01103 static ssize_t bytes; 01104 static jmp_buf jmp; 01105 static char match_name[MAX_STRLEN]; 01106 01107 #define RETURN(A) {\ 01108 if (regex) {\ 01109 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\ 01110 if (0 == regexec(regex, match_name, 0, 0, 0)) {\ 01111 longjmp(jmp,0);\ 01112 }\ 01113 }\ 01114 if ( collect_stats ) {\ 01115 record_stats( A );\ 01116 }\ 01117 return A;\ 01118 } 01119 01120 setjmp(jmp); 01121 01122 error = 0; 01123 01124 // first_byte is index into event buffer 01125 if ( first_byte != 0 01126 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) { 01127 01128 ret = (struct inotify_event *)((char *)&event[0] + first_byte); 01129 first_byte += sizeof(struct inotify_event) + ret->len; 01130 01131 // if the pointer to the next event exactly hits end of bytes read, 01132 // that's good. next time we're called, we'll read. 01133 if ( first_byte == bytes ) { 01134 first_byte = 0; 01135 } 01136 else if ( first_byte > bytes ) { 01137 // oh... no. this can't be happening. An incomplete event. 01138 // Copy what we currently have into first element, call self to 01139 // read remainder. 01140 // oh, and they BETTER NOT overlap. 01141 // Boy I hope this code works. 01142 // But I think this can never happen due to how inotify is written. 01143 niceassert( (long)((char *)&event[0] + 01144 sizeof(struct inotify_event) + 01145 event[0].len) <= (long)ret, 01146 "extremely unlucky user, death imminent" ); 01147 // how much of the event do we have? 01148 bytes = (char *)&event[0] + bytes - (char *)ret; 01149 memcpy( &event[0], ret, bytes ); 01150 return inotifytools_next_events( timeout, num_events ); 01151 } 01152 RETURN(ret); 01153 01154 } 01155 01156 else if ( first_byte == 0 ) { 01157 bytes = 0; 01158 } 01159 01160 01161 static ssize_t this_bytes; 01162 static unsigned int bytes_to_read; 01163 static int rc; 01164 static fd_set read_fds; 01165 01166 static struct timeval read_timeout; 01167 read_timeout.tv_sec = timeout; 01168 read_timeout.tv_usec = 0; 01169 static struct timeval * read_timeout_ptr; 01170 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout ); 01171 01172 FD_ZERO(&read_fds); 01173 FD_SET(inotify_fd, &read_fds); 01174 rc = select(inotify_fd + 1, &read_fds, 01175 NULL, NULL, read_timeout_ptr); 01176 if ( rc < 0 ) { 01177 // error 01178 error = errno; 01179 return NULL; 01180 } 01181 else if ( rc == 0 ) { 01182 // timeout 01183 return NULL; 01184 } 01185 01186 // wait until we have enough bytes to read 01187 do { 01188 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read ); 01189 } while ( !rc && 01190 bytes_to_read < sizeof(struct inotify_event)*num_events ); 01191 01192 if ( rc == -1 ) { 01193 error = errno; 01194 return NULL; 01195 } 01196 01197 this_bytes = read(inotify_fd, &event[0] + bytes, 01198 sizeof(struct inotify_event)*MAX_EVENTS - bytes); 01199 if ( this_bytes < 0 ) { 01200 error = errno; 01201 return NULL; 01202 } 01203 if ( this_bytes == 0 ) { 01204 fprintf(stderr, "Inotify reported end-of-file. Possibly too many " 01205 "events occurred at once.\n"); 01206 return NULL; 01207 } 01208 bytes += this_bytes; 01209 01210 ret = &event[0]; 01211 first_byte = sizeof(struct inotify_event) + ret->len; 01212 niceassert( first_byte <= bytes, "ridiculously long filename, things will " 01213 "almost certainly screw up." ); 01214 if ( first_byte == bytes ) { 01215 first_byte = 0; 01216 } 01217 01218 RETURN(ret); 01219 01220 #undef RETURN 01221 } 01222 01248 int inotifytools_watch_recursively( char const * path, int events ) { 01249 return inotifytools_watch_recursively_with_exclude( path, events, 0 ); 01250 } 01251 01284 int inotifytools_watch_recursively_with_exclude( char const * path, int events, 01285 char const ** exclude_list ) { 01286 niceassert( init, "inotifytools_initialize not called yet" ); 01287 01288 DIR * dir; 01289 char * my_path; 01290 error = 0; 01291 dir = opendir( path ); 01292 if ( !dir ) { 01293 // If not a directory, don't need to do anything special 01294 if ( errno == ENOTDIR ) { 01295 return inotifytools_watch_file( path, events ); 01296 } 01297 else { 01298 error = errno; 01299 return 0; 01300 } 01301 } 01302 01303 if ( path[strlen(path)-1] != '/' ) { 01304 nasprintf( &my_path, "%s/", path ); 01305 } 01306 else { 01307 my_path = (char *)path; 01308 } 01309 01310 static struct dirent * ent; 01311 char * next_file; 01312 static struct stat64 my_stat; 01313 ent = readdir( dir ); 01314 // Watch each directory within this directory 01315 while ( ent ) { 01316 if ( (0 != strcmp( ent->d_name, "." )) && 01317 (0 != strcmp( ent->d_name, ".." )) ) { 01318 nasprintf(&next_file,"%s%s", my_path, ent->d_name); 01319 if ( -1 == lstat64( next_file, &my_stat ) ) { 01320 error = errno; 01321 free( next_file ); 01322 if ( errno != EACCES ) { 01323 error = errno; 01324 if ( my_path != path ) free( my_path ); 01325 closedir( dir ); 01326 return 0; 01327 } 01328 } 01329 else if ( S_ISDIR( my_stat.st_mode ) && 01330 !S_ISLNK( my_stat.st_mode )) { 01331 free( next_file ); 01332 nasprintf(&next_file,"%s%s/", my_path, ent->d_name); 01333 static unsigned int no_watch; 01334 static char const ** exclude_entry; 01335 01336 no_watch = 0; 01337 for (exclude_entry = exclude_list; 01338 exclude_entry && *exclude_entry && !no_watch; 01339 ++exclude_entry) { 01340 static int exclude_length; 01341 01342 exclude_length = strlen(*exclude_entry); 01343 if ((*exclude_entry)[exclude_length-1] == '/') { 01344 --exclude_length; 01345 } 01346 if ( strlen(next_file) == (unsigned)(exclude_length + 1) && 01347 !strncmp(*exclude_entry, next_file, exclude_length)) { 01348 // directory found in exclude list 01349 no_watch = 1; 01350 } 01351 } 01352 if (!no_watch) { 01353 static int status; 01354 status = inotifytools_watch_recursively_with_exclude( 01355 next_file, 01356 events, 01357 exclude_list ); 01358 // For some errors, we will continue. 01359 if ( !status && (EACCES != error) && (ENOENT != error) && 01360 (ELOOP != error) ) { 01361 free( next_file ); 01362 if ( my_path != path ) free( my_path ); 01363 closedir( dir ); 01364 return 0; 01365 } 01366 } // if !no_watch 01367 free( next_file ); 01368 } // if isdir and not islnk 01369 else { 01370 free( next_file ); 01371 } 01372 } 01373 ent = readdir( dir ); 01374 error = 0; 01375 } 01376 01377 closedir( dir ); 01378 01379 int ret = inotifytools_watch_file( my_path, events ); 01380 if ( my_path != path ) free( my_path ); 01381 return ret; 01382 } 01383 01387 void record_stats( struct inotify_event const * event ) { 01388 if (!event) return; 01389 watch *w = watch_from_wd(event->wd); 01390 if (!w) return; 01391 if ( IN_ACCESS & event->mask ) { 01392 ++w->hit_access; 01393 ++num_access; 01394 } 01395 if ( IN_MODIFY & event->mask ) { 01396 ++w->hit_modify; 01397 ++num_modify; 01398 } 01399 if ( IN_ATTRIB & event->mask ) { 01400 ++w->hit_attrib; 01401 ++num_attrib; 01402 } 01403 if ( IN_CLOSE_WRITE & event->mask ) { 01404 ++w->hit_close_write; 01405 ++num_close_write; 01406 } 01407 if ( IN_CLOSE_NOWRITE & event->mask ) { 01408 ++w->hit_close_nowrite; 01409 ++num_close_nowrite; 01410 } 01411 if ( IN_OPEN & event->mask ) { 01412 ++w->hit_open; 01413 ++num_open; 01414 } 01415 if ( IN_MOVED_FROM & event->mask ) { 01416 ++w->hit_moved_from; 01417 ++num_moved_from; 01418 } 01419 if ( IN_MOVED_TO & event->mask ) { 01420 ++w->hit_moved_to; 01421 ++num_moved_to; 01422 } 01423 if ( IN_CREATE & event->mask ) { 01424 ++w->hit_create; 01425 ++num_create; 01426 } 01427 if ( IN_DELETE & event->mask ) { 01428 ++w->hit_delete; 01429 ++num_delete; 01430 } 01431 if ( IN_DELETE_SELF & event->mask ) { 01432 ++w->hit_delete_self; 01433 ++num_delete_self; 01434 } 01435 if ( IN_UNMOUNT & event->mask ) { 01436 ++w->hit_unmount; 01437 ++num_unmount; 01438 } 01439 if ( IN_MOVE_SELF & event->mask ) { 01440 ++w->hit_move_self; 01441 ++num_move_self; 01442 } 01443 01444 ++w->hit_total; 01445 ++num_total; 01446 01447 } 01448 01449 int *stat_ptr(watch *w, int event) 01450 { 01451 if ( IN_ACCESS == event ) 01452 return &w->hit_access; 01453 if ( IN_MODIFY == event ) 01454 return &w->hit_modify; 01455 if ( IN_ATTRIB == event ) 01456 return &w->hit_attrib; 01457 if ( IN_CLOSE_WRITE == event ) 01458 return &w->hit_close_write; 01459 if ( IN_CLOSE_NOWRITE == event ) 01460 return &w->hit_close_nowrite; 01461 if ( IN_OPEN == event ) 01462 return &w->hit_open; 01463 if ( IN_MOVED_FROM == event ) 01464 return &w->hit_moved_from; 01465 if ( IN_MOVED_TO == event ) 01466 return &w->hit_moved_to; 01467 if ( IN_CREATE == event ) 01468 return &w->hit_create; 01469 if ( IN_DELETE == event ) 01470 return &w->hit_delete; 01471 if ( IN_DELETE_SELF == event ) 01472 return &w->hit_delete_self; 01473 if ( IN_UNMOUNT == event ) 01474 return &w->hit_unmount; 01475 if ( IN_MOVE_SELF == event ) 01476 return &w->hit_move_self; 01477 if ( 0 == event ) 01478 return &w->hit_total; 01479 return 0; 01480 } 01481 01497 int inotifytools_get_stat_by_wd( int wd, int event ) { 01498 if (!collect_stats) return -1; 01499 01500 watch *w = watch_from_wd(wd); 01501 if (!w) return -1; 01502 int *i = stat_ptr(w, event); 01503 if (!i) return -1; 01504 return *i; 01505 } 01506 01520 int inotifytools_get_stat_total( int event ) { 01521 if (!collect_stats) return -1; 01522 if ( IN_ACCESS == event ) 01523 return num_access; 01524 if ( IN_MODIFY == event ) 01525 return num_modify; 01526 if ( IN_ATTRIB == event ) 01527 return num_attrib; 01528 if ( IN_CLOSE_WRITE == event ) 01529 return num_close_write; 01530 if ( IN_CLOSE_NOWRITE == event ) 01531 return num_close_nowrite; 01532 if ( IN_OPEN == event ) 01533 return num_open; 01534 if ( IN_MOVED_FROM == event ) 01535 return num_moved_from; 01536 if ( IN_MOVED_TO == event ) 01537 return num_moved_to; 01538 if ( IN_CREATE == event ) 01539 return num_create; 01540 if ( IN_DELETE == event ) 01541 return num_delete; 01542 if ( IN_DELETE_SELF == event ) 01543 return num_delete_self; 01544 if ( IN_UNMOUNT == event ) 01545 return num_unmount; 01546 if ( IN_MOVE_SELF == event ) 01547 return num_move_self; 01548 01549 if ( 0 == event ) 01550 return num_total; 01551 01552 return -1; 01553 } 01554 01574 int inotifytools_get_stat_by_filename( char const * filename, 01575 int event ) { 01576 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename( 01577 filename ), event ); 01578 } 01579 01590 int inotifytools_error() { 01591 return error; 01592 } 01593 01597 int isdir( char const * path ) { 01598 static struct stat64 my_stat; 01599 01600 if ( -1 == lstat64( path, &my_stat ) ) { 01601 if (errno == ENOENT) return 0; 01602 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno)); 01603 return 0; 01604 } 01605 01606 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode ); 01607 } 01608 01609 01616 int inotifytools_get_num_watches() { 01617 int ret = 0; 01618 rbwalk(tree_filename, get_num, (void*)&ret); 01619 return ret; 01620 } 01621 01662 int inotifytools_printf( struct inotify_event* event, char* fmt ) { 01663 return inotifytools_fprintf( stdout, event, fmt ); 01664 } 01665 01707 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) { 01708 static char out[MAX_STRLEN+1]; 01709 static int ret; 01710 ret = inotifytools_sprintf( out, event, fmt ); 01711 if ( -1 != ret ) fprintf( file, "%s", out ); 01712 return ret; 01713 } 01714 01765 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) { 01766 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt ); 01767 } 01768 01769 01816 int inotifytools_snprintf( char * out, int size, 01817 struct inotify_event* event, char* fmt ) { 01818 static char * filename, * eventname, * eventstr; 01819 static unsigned int i, ind; 01820 static char ch1; 01821 static char timestr[MAX_STRLEN]; 01822 static time_t now; 01823 01824 01825 if ( event->len > 0 ) { 01826 eventname = event->name; 01827 } 01828 else { 01829 eventname = NULL; 01830 } 01831 01832 01833 filename = inotifytools_filename_from_wd( event->wd ); 01834 01835 if ( !fmt || 0 == strlen(fmt) ) { 01836 error = EINVAL; 01837 return -1; 01838 } 01839 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) { 01840 error = EMSGSIZE; 01841 return -1; 01842 } 01843 01844 ind = 0; 01845 for ( i = 0; i < strlen(fmt) && 01846 (int)ind < size - 1; ++i ) { 01847 if ( fmt[i] != '%' ) { 01848 out[ind++] = fmt[i]; 01849 continue; 01850 } 01851 01852 if ( i == strlen(fmt) - 1 ) { 01853 // last character is %, invalid 01854 error = EINVAL; 01855 return ind; 01856 } 01857 01858 ch1 = fmt[i+1]; 01859 01860 if ( ch1 == '%' ) { 01861 out[ind++] = '%'; 01862 ++i; 01863 continue; 01864 } 01865 01866 if ( ch1 == 'w' ) { 01867 if ( filename ) { 01868 strncpy( &out[ind], filename, size - ind ); 01869 ind += strlen(filename); 01870 } 01871 ++i; 01872 continue; 01873 } 01874 01875 if ( ch1 == 'f' ) { 01876 if ( eventname ) { 01877 strncpy( &out[ind], eventname, size - ind ); 01878 ind += strlen(eventname); 01879 } 01880 ++i; 01881 continue; 01882 } 01883 01884 if ( ch1 == 'e' ) { 01885 eventstr = inotifytools_event_to_str( event->mask ); 01886 strncpy( &out[ind], eventstr, size - ind ); 01887 ind += strlen(eventstr); 01888 ++i; 01889 continue; 01890 } 01891 01892 if ( ch1 == 'T' ) { 01893 01894 if ( timefmt ) { 01895 01896 now = time(0); 01897 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt, 01898 localtime( &now ) ) ) { 01899 01900 // time format probably invalid 01901 error = EINVAL; 01902 return ind; 01903 } 01904 } 01905 else { 01906 timestr[0] = 0; 01907 } 01908 01909 strncpy( &out[ind], timestr, size - ind ); 01910 ind += strlen(timestr); 01911 ++i; 01912 continue; 01913 } 01914 01915 // Check if next char in fmt is e 01916 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) { 01917 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 ); 01918 strncpy( &out[ind], eventstr, size - ind ); 01919 ind += strlen(eventstr); 01920 i += 2; 01921 continue; 01922 } 01923 01924 // OK, this wasn't a special format character, just output it as normal 01925 if ( ind < MAX_STRLEN ) out[ind++] = '%'; 01926 if ( ind < MAX_STRLEN ) out[ind++] = ch1; 01927 ++i; 01928 } 01929 out[ind] = 0; 01930 01931 return ind - 1; 01932 } 01933 01943 void inotifytools_set_printf_timefmt( char * fmt ) { 01944 timefmt = fmt; 01945 } 01946 01955 int inotifytools_get_max_queued_events() { 01956 int ret; 01957 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1; 01958 return ret; 01959 } 01960 01970 int inotifytools_get_max_user_instances() { 01971 int ret; 01972 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1; 01973 return ret; 01974 } 01975 01985 int inotifytools_get_max_user_watches() { 01986 int ret; 01987 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1; 01988 return ret; 01989 } 01990 02002 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) { 02003 if (!pattern) { 02004 if (regex) { 02005 regfree(regex); 02006 free(regex); 02007 regex = 0; 02008 } 02009 return 1; 02010 } 02011 02012 if (regex) { regfree(regex); } 02013 else { regex = (regex_t *)malloc(sizeof(regex_t)); } 02014 02015 int ret = regcomp(regex, pattern, flags | REG_NOSUB); 02016 if (0 == ret) return 1; 02017 02018 regfree(regex); 02019 free(regex); 02020 regex = 0; 02021 error = EINVAL; 02022 return 0; 02023 } 02024 02025 int event_compare(const void *p1, const void *p2, const void *config) 02026 { 02027 if (!p1 || !p2) return p1 - p2; 02028 char asc = 1; 02029 int sort_event = (int)config; 02030 if (sort_event == -1) { 02031 sort_event = 0; 02032 asc = 0; 02033 } else if (sort_event < 0) { 02034 sort_event = -sort_event; 02035 asc = 0; 02036 } 02037 int *i1 = stat_ptr((watch*)p1, sort_event); 02038 int *i2 = stat_ptr((watch*)p2, sort_event); 02039 if (0 == *i1 - *i2) { 02040 return ((watch*)p1)->wd - ((watch*)p2)->wd; 02041 } 02042 if (asc) 02043 return *i1 - *i2; 02044 else 02045 return *i2 - *i1; 02046 } 02047 02048 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event) 02049 { 02050 struct rbtree *ret = rbinit(event_compare, (void*)sort_event); 02051 RBLIST *all = rbopenlist(tree_wd); 02052 void const *p = rbreadlist(all); 02053 while (p) { 02054 void const *r = rbsearch(p, ret); 02055 niceassert((int)(r == p), "Couldn't insert watch into new tree"); 02056 p = rbreadlist(all); 02057 } 02058 rbcloselist(all); 02059 return ret; 02060 }