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 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 } // if ( wd == -1 ) 00991 else { 00992 fprintf( stderr, "Failed to watch %s: returned wd was %d " 00993 "(expected -1 or >0 )", filenames[i], wd ); 00994 // no appropriate value for error 00995 return 0; 00996 } // else 00997 } // if ( wd < 0 ) 00998 00999 char *filename; 01000 // Always end filename with / if it is a directory 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 } // for 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 // first_byte is index into event buffer 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 // if the pointer to the next event exactly hits end of bytes read, 01133 // that's good. next time we're called, we'll read. 01134 if ( first_byte == bytes ) { 01135 first_byte = 0; 01136 } 01137 else if ( first_byte > bytes ) { 01138 // oh... no. this can't be happening. An incomplete event. 01139 // Copy what we currently have into first element, call self to 01140 // read remainder. 01141 // oh, and they BETTER NOT overlap. 01142 // Boy I hope this code works. 01143 // But I think this can never happen due to how inotify is written. 01144 niceassert( (long)((char *)&event[0] + 01145 sizeof(struct inotify_event) + 01146 event[0].len) <= (long)ret, 01147 "extremely unlucky user, death imminent" ); 01148 // how much of the event do we have? 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 // error 01179 error = errno; 01180 return NULL; 01181 } 01182 else if ( rc == 0 ) { 01183 // timeout 01184 return NULL; 01185 } 01186 01187 // wait until we have enough bytes to read 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 // If not a directory, don't need to do anything special 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 // Watch each directory within this directory 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 // directory found in exclude list 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 // For some errors, we will continue. 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 } // if !no_watch 01368 free( next_file ); 01369 } // if isdir and not islnk 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 // last character is %, invalid 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 // time format probably invalid 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 // Check if next char in fmt is e 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 // OK, this wasn't a special format character, just output it as normal 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 }