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