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