15 #include "../../config.h" 17 #include "inotifytools_p.h" 24 #include <sys/select.h> 25 #include <sys/types.h> 27 #include <sys/ioctl.h> 34 #include "inotifytools/inotify.h" 122 #define MAX_EVENTS 4096 123 #define MAX_STRLEN 4096 124 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/" 125 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches" 126 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches" 127 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances" 129 static int inotify_fd;
130 static unsigned num_access;
131 static unsigned num_modify;
132 static unsigned num_attrib;
133 static unsigned num_close_nowrite;
134 static unsigned num_close_write;
135 static unsigned num_open;
136 static unsigned num_move_self;
137 static unsigned num_moved_to;
138 static unsigned num_moved_from;
139 static unsigned num_create;
140 static unsigned num_delete;
141 static unsigned num_delete_self;
142 static unsigned num_unmount;
143 static unsigned num_total;
144 static int collect_stats = 0;
146 struct rbtree *tree_wd = 0;
147 struct rbtree *tree_filename = 0;
148 static int error = 0;
150 static char* timefmt = 0;
151 static regex_t* regex = 0;
153 int isdir(
char const * path );
154 void record_stats(
struct inotify_event
const * event );
155 int onestr_to_event(
char const * event);
174 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \ 177 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory") 196 void _niceassert(
long cond,
int line,
char const * file,
char const * condstr,
197 char const * mesg ) {
201 fprintf(stderr,
"%s:%d assertion ( %s ) failed: %s\n", file, line,
205 fprintf(stderr,
"%s:%d assertion ( %s ) failed.\n", file, line, condstr);
218 char * chrtostr(
char ch) {
219 static char str[2] = {
'\0',
'\0' };
227 int read_num_from_file(
char * filename,
int * num ) {
228 FILE * file = fopen( filename,
"r" );
234 if ( EOF == fscanf( file,
"%d", num ) ) {
239 niceassert( 0 == fclose( file ), 0 );
244 int wd_compare(
const void *d1,
const void *d2,
const void *config) {
245 if (!d1 || !d2)
return d1 - d2;
246 return ((watch*)d1)->wd - ((watch*)d2)->wd;
249 int filename_compare(
const void *d1,
const void *d2,
const void *config) {
250 if (!d1 || !d2)
return d1 - d2;
251 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
257 watch *watch_from_wd(
int wd ) {
260 return (watch*)rbfind(&w, tree_wd);
266 watch *watch_from_filename(
char const *filename ) {
268 w.filename = (
char*)filename;
269 return (watch*)rbfind(&w, tree_filename);
286 inotify_fd = inotify_init();
287 if (inotify_fd < 0) {
294 tree_wd = rbinit(wd_compare, 0);
295 tree_filename = rbinit(filename_compare, 0);
304 void destroy_watch(watch *w) {
305 if (w->filename) free(w->filename);
312 void cleanup_tree(
const void *nodep,
314 const int depth,
void* arg) {
315 if (which != endorder && which != leaf)
return;
316 watch *w = (watch*)nodep;
341 rbwalk(tree_wd, cleanup_tree, 0);
342 rbdestroy(tree_wd); tree_wd = 0;
343 rbdestroy(tree_filename); tree_filename = 0;
349 void empty_stats(
const void *nodep,
351 const int depth,
void *arg) {
352 if (which != endorder && which != leaf)
return;
353 watch *w = (watch*)nodep;
357 w->hit_close_nowrite = 0;
358 w->hit_close_write = 0;
360 w->hit_move_self = 0;
361 w->hit_moved_from = 0;
365 w->hit_delete_self = 0;
373 void replace_filename(
const void *nodep,
375 const int depth,
void *arg) {
376 if (which != endorder && which != leaf)
return;
377 watch *w = (watch*)nodep;
378 char *old_name = ((
char**)arg)[0];
379 char *new_name = ((
char**)arg)[1];
380 int old_len = *((
int*)&((
char**)arg)[2]);
382 if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
383 nasprintf( &name,
"%s%s", new_name, &(w->filename[old_len]) );
384 if (!strcmp( w->filename, new_name )) {
387 rbdelete(w, tree_filename);
390 rbsearch(w, tree_filename);
398 void get_num(
const void *nodep,
400 const int depth,
void *arg) {
401 if (which != endorder && which != leaf)
return;
419 niceassert( init,
"inotifytools_initialize not called yet" );
423 rbwalk(tree_wd, empty_stats, 0);
429 num_close_nowrite = 0;
472 if ( strchr(
"_" "abcdefghijklmnopqrstuvwxyz" 473 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
478 char * event1, * event2;
482 if ( !event || !event[0] )
return 0;
484 event1 = (
char *)event;
485 event2 = strchr( event1, sep );
486 while ( event1 && event1[0] ) {
488 len = event2 - event1;
489 niceassert( len < 4096,
"malformed event string (very long)" );
492 len = strlen(event1);
494 if ( len > 4095 ) len = 4095;
495 strncpy( eventstr, event1, len );
498 ret1 = onestr_to_event( eventstr );
499 if ( 0 == ret1 || -1 == ret1 ) {
506 if ( event1 && event1[0] ) {
510 if ( !event1[0] )
return 0;
511 event2 = strchr( event1, sep );
556 int onestr_to_event(
char const * event)
561 if ( !event || !event[0] )
563 else if ( 0 == strcasecmp(event,
"ACCESS") )
565 else if ( 0 == strcasecmp(event,
"MODIFY") )
567 else if ( 0 == strcasecmp(event,
"ATTRIB") )
569 else if ( 0 == strcasecmp(event,
"CLOSE_WRITE") )
570 ret = IN_CLOSE_WRITE;
571 else if ( 0 == strcasecmp(event,
"CLOSE_NOWRITE") )
572 ret = IN_CLOSE_NOWRITE;
573 else if ( 0 == strcasecmp(event,
"OPEN") )
575 else if ( 0 == strcasecmp(event,
"MOVED_FROM") )
577 else if ( 0 == strcasecmp(event,
"MOVED_TO") )
579 else if ( 0 == strcasecmp(event,
"CREATE") )
581 else if ( 0 == strcasecmp(event,
"DELETE") )
583 else if ( 0 == strcasecmp(event,
"DELETE_SELF") )
584 ret = IN_DELETE_SELF;
585 else if ( 0 == strcasecmp(event,
"UNMOUNT") )
587 else if ( 0 == strcasecmp(event,
"Q_OVERFLOW") )
589 else if ( 0 == strcasecmp(event,
"IGNORED") )
591 else if ( 0 == strcasecmp(event,
"CLOSE") )
593 else if ( 0 == strcasecmp(event,
"MOVE_SELF") )
595 else if ( 0 == strcasecmp(event,
"MOVE") )
597 else if ( 0 == strcasecmp(event,
"ISDIR") )
599 else if ( 0 == strcasecmp(event,
"ONESHOT") )
601 else if ( 0 == strcasecmp(event,
"ALL_EVENTS") )
658 static char ret[1024];
662 if ( IN_ACCESS & events ) {
663 strcat( ret, chrtostr(sep) );
664 strcat( ret,
"ACCESS" );
666 if ( IN_MODIFY & events ) {
667 strcat( ret, chrtostr(sep) );
668 strcat( ret,
"MODIFY" );
670 if ( IN_ATTRIB & events ) {
671 strcat( ret, chrtostr(sep) );
672 strcat( ret,
"ATTRIB" );
674 if ( IN_CLOSE_WRITE & events ) {
675 strcat( ret, chrtostr(sep) );
676 strcat( ret,
"CLOSE_WRITE" );
678 if ( IN_CLOSE_NOWRITE & events ) {
679 strcat( ret, chrtostr(sep) );
680 strcat( ret,
"CLOSE_NOWRITE" );
682 if ( IN_OPEN & events ) {
683 strcat( ret, chrtostr(sep) );
684 strcat( ret,
"OPEN" );
686 if ( IN_MOVED_FROM & events ) {
687 strcat( ret, chrtostr(sep) );
688 strcat( ret,
"MOVED_FROM" );
690 if ( IN_MOVED_TO & events ) {
691 strcat( ret, chrtostr(sep) );
692 strcat( ret,
"MOVED_TO" );
694 if ( IN_CREATE & events ) {
695 strcat( ret, chrtostr(sep) );
696 strcat( ret,
"CREATE" );
698 if ( IN_DELETE & events ) {
699 strcat( ret, chrtostr(sep) );
700 strcat( ret,
"DELETE" );
702 if ( IN_DELETE_SELF & events ) {
703 strcat( ret, chrtostr(sep) );
704 strcat( ret,
"DELETE_SELF" );
706 if ( IN_UNMOUNT & events ) {
707 strcat( ret, chrtostr(sep) );
708 strcat( ret,
"UNMOUNT" );
710 if ( IN_Q_OVERFLOW & events ) {
711 strcat( ret, chrtostr(sep) );
712 strcat( ret,
"Q_OVERFLOW" );
714 if ( IN_IGNORED & events ) {
715 strcat( ret, chrtostr(sep) );
716 strcat( ret,
"IGNORED" );
718 if ( IN_CLOSE & events ) {
719 strcat( ret, chrtostr(sep) );
720 strcat( ret,
"CLOSE" );
722 if ( IN_MOVE_SELF & events ) {
723 strcat( ret, chrtostr(sep) );
724 strcat( ret,
"MOVE_SELF" );
726 if ( IN_ISDIR & events ) {
727 strcat( ret, chrtostr(sep) );
728 strcat( ret,
"ISDIR" );
730 if ( IN_ONESHOT & events ) {
731 strcat( ret, chrtostr(sep) );
732 strcat( ret,
"ONESHOT" );
736 if (ret[0] ==
'\0') {
737 niceassert( -1 != sprintf( ret,
"%c0x%08x", sep, events ), 0 );
764 niceassert( init,
"inotifytools_initialize not called yet" );
765 watch *w = watch_from_wd(wd);
787 niceassert( init,
"inotifytools_initialize not called yet" );
788 watch *w = watch_from_filename(filename);
808 niceassert( init,
"inotifytools_initialize not called yet" );
809 watch *w = watch_from_wd(wd);
811 if (w->filename) free(w->filename);
812 w->filename = strdup(filename);
830 char const * newname ) {
831 watch *w = watch_from_filename(oldname);
833 if (w->filename) free(w->filename);
834 w->filename = strdup(newname);
860 char const * newname ) {
861 if ( !oldname || !newname )
return;
862 char *names[2+
sizeof(int)/
sizeof(
char*)];
863 names[0] = (
char*)oldname;
864 names[1] = (
char*)newname;
865 *((
int*)&names[2]) = strlen(oldname);
866 rbwalk(tree_filename, replace_filename, (
void*)names);
872 int remove_inotify_watch(watch *w) {
874 int status = inotify_rm_watch( inotify_fd, w->wd );
876 fprintf(stderr,
"Failed to remove watch on %s: %s\n", w->filename,
887 watch *create_watch(
int wd,
char *filename) {
888 if ( wd <= 0 || !filename)
return 0;
890 watch *w = (watch*)calloc(1,
sizeof(watch));
892 w->filename = strdup(filename);
893 rbsearch(w, tree_wd);
894 rbsearch(w, tree_filename);
911 niceassert( init,
"inotifytools_initialize not called yet" );
912 watch *w = watch_from_wd(wd);
915 if (!remove_inotify_watch(w))
return 0;
916 rbdelete(w, tree_wd);
917 rbdelete(w, tree_filename);
934 niceassert( init,
"inotifytools_initialize not called yet" );
935 watch *w = watch_from_filename(filename);
938 if (!remove_inotify_watch(w))
return 0;
939 rbdelete(w, tree_wd);
940 rbdelete(w, tree_filename);
957 static char const * filenames[2];
958 filenames[0] = filename;
979 niceassert( init,
"inotifytools_initialize not called yet" );
983 for ( i = 0; filenames[i]; ++i ) {
985 wd = inotify_add_watch( inotify_fd, filenames[i], events );
992 fprintf( stderr,
"Failed to watch %s: returned wd was %d " 993 "(expected -1 or >0 )", filenames[i], wd );
1001 if ( !isdir(filenames[i])
1002 || filenames[i][strlen(filenames[i])-1] ==
'/') {
1003 filename = strdup(filenames[i]);
1006 nasprintf( &filename,
"%s/", filenames[i] );
1008 create_watch(wd, filename);
1096 niceassert( init,
"inotifytools_initialize not called yet" );
1097 niceassert( num_events <= MAX_EVENTS,
"too many events requested" );
1099 if ( num_events < 1 )
return NULL;
1101 static struct inotify_event event[MAX_EVENTS];
1102 static struct inotify_event * ret;
1103 static int first_byte = 0;
1104 static ssize_t bytes;
1106 static char match_name[MAX_STRLEN];
1108 #define RETURN(A) {\ 1110 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\ 1111 if (0 == regexec(regex, match_name, 0, 0, 0)) {\ 1115 if ( collect_stats ) {\ 1126 if ( first_byte != 0
1127 && first_byte <= (
int)(bytes -
sizeof(
struct inotify_event)) ) {
1129 ret = (
struct inotify_event *)((
char *)&
event[0] + first_byte);
1130 first_byte +=
sizeof(
struct inotify_event) + ret->len;
1134 if ( first_byte == bytes ) {
1137 else if ( first_byte > bytes ) {
1144 niceassert( (
long)((
char *)&event[0] +
1145 sizeof(
struct inotify_event) +
1146 event[0].len) <= (
long)ret,
1147 "extremely unlucky user, death imminent" );
1149 bytes = (
char *)&event[0] + bytes - (
char *)ret;
1150 memcpy( &event[0], ret, bytes );
1157 else if ( first_byte == 0 ) {
1162 static ssize_t this_bytes;
1163 static unsigned int bytes_to_read;
1165 static fd_set read_fds;
1167 static struct timeval read_timeout;
1168 read_timeout.tv_sec = timeout;
1169 read_timeout.tv_usec = 0;
1170 static struct timeval * read_timeout_ptr;
1171 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
1174 FD_SET(inotify_fd, &read_fds);
1175 rc = select(inotify_fd + 1, &read_fds,
1176 NULL, NULL, read_timeout_ptr);
1182 else if ( rc == 0 ) {
1189 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1191 bytes_to_read <
sizeof(
struct inotify_event)*num_events );
1198 this_bytes = read(inotify_fd, &event[0] + bytes,
1199 sizeof(
struct inotify_event)*MAX_EVENTS - bytes);
1200 if ( this_bytes < 0 ) {
1204 if ( this_bytes == 0 ) {
1205 fprintf(stderr,
"Inotify reported end-of-file. Possibly too many " 1206 "events occurred at once.\n");
1209 bytes += this_bytes;
1212 first_byte =
sizeof(
struct inotify_event) + ret->len;
1213 niceassert( first_byte <= bytes,
"ridiculously long filename, things will " 1214 "almost certainly screw up." );
1215 if ( first_byte == bytes ) {
1286 char const ** exclude_list ) {
1287 niceassert( init,
"inotifytools_initialize not called yet" );
1292 dir = opendir( path );
1295 if ( errno == ENOTDIR ) {
1304 if ( path[strlen(path)-1] !=
'/' ) {
1305 nasprintf( &my_path,
"%s/", path );
1308 my_path = (
char *)path;
1311 static struct dirent * ent;
1313 static struct stat64 my_stat;
1314 ent = readdir( dir );
1317 if ( (0 != strcmp( ent->d_name,
"." )) &&
1318 (0 != strcmp( ent->d_name,
".." )) ) {
1319 nasprintf(&next_file,
"%s%s", my_path, ent->d_name);
1320 if ( -1 == lstat64( next_file, &my_stat ) ) {
1323 if ( errno != EACCES ) {
1325 if ( my_path != path ) free( my_path );
1330 else if ( S_ISDIR( my_stat.st_mode ) &&
1331 !S_ISLNK( my_stat.st_mode )) {
1333 nasprintf(&next_file,
"%s%s/", my_path, ent->d_name);
1334 static unsigned int no_watch;
1335 static char const ** exclude_entry;
1338 for (exclude_entry = exclude_list;
1339 exclude_entry && *exclude_entry && !no_watch;
1341 static int exclude_length;
1343 exclude_length = strlen(*exclude_entry);
1344 if ((*exclude_entry)[exclude_length-1] ==
'/') {
1347 if ( strlen(next_file) == (
unsigned)(exclude_length + 1) &&
1348 !strncmp(*exclude_entry, next_file, exclude_length)) {
1360 if ( !status && (EACCES != error) && (ENOENT != error) &&
1361 (ELOOP != error) ) {
1363 if ( my_path != path ) free( my_path );
1374 ent = readdir( dir );
1381 if ( my_path != path ) free( my_path );
1388 void record_stats(
struct inotify_event
const * event ) {
1390 watch *w = watch_from_wd(event->wd);
1392 if ( IN_ACCESS & event->mask ) {
1396 if ( IN_MODIFY & event->mask ) {
1400 if ( IN_ATTRIB & event->mask ) {
1404 if ( IN_CLOSE_WRITE & event->mask ) {
1405 ++w->hit_close_write;
1408 if ( IN_CLOSE_NOWRITE & event->mask ) {
1409 ++w->hit_close_nowrite;
1410 ++num_close_nowrite;
1412 if ( IN_OPEN & event->mask ) {
1416 if ( IN_MOVED_FROM & event->mask ) {
1417 ++w->hit_moved_from;
1420 if ( IN_MOVED_TO & event->mask ) {
1424 if ( IN_CREATE & event->mask ) {
1428 if ( IN_DELETE & event->mask ) {
1432 if ( IN_DELETE_SELF & event->mask ) {
1433 ++w->hit_delete_self;
1436 if ( IN_UNMOUNT & event->mask ) {
1440 if ( IN_MOVE_SELF & event->mask ) {
1450 int *stat_ptr(watch *w,
int event)
1452 if ( IN_ACCESS == event )
1453 return &w->hit_access;
1454 if ( IN_MODIFY == event )
1455 return &w->hit_modify;
1456 if ( IN_ATTRIB == event )
1457 return &w->hit_attrib;
1458 if ( IN_CLOSE_WRITE == event )
1459 return &w->hit_close_write;
1460 if ( IN_CLOSE_NOWRITE == event )
1461 return &w->hit_close_nowrite;
1462 if ( IN_OPEN == event )
1463 return &w->hit_open;
1464 if ( IN_MOVED_FROM == event )
1465 return &w->hit_moved_from;
1466 if ( IN_MOVED_TO == event )
1467 return &w->hit_moved_to;
1468 if ( IN_CREATE == event )
1469 return &w->hit_create;
1470 if ( IN_DELETE == event )
1471 return &w->hit_delete;
1472 if ( IN_DELETE_SELF == event )
1473 return &w->hit_delete_self;
1474 if ( IN_UNMOUNT == event )
1475 return &w->hit_unmount;
1476 if ( IN_MOVE_SELF == event )
1477 return &w->hit_move_self;
1479 return &w->hit_total;
1499 if (!collect_stats)
return -1;
1501 watch *w = watch_from_wd(wd);
1503 int *i = stat_ptr(w, event);
1522 if (!collect_stats)
return -1;
1523 if ( IN_ACCESS == event )
1525 if ( IN_MODIFY == event )
1527 if ( IN_ATTRIB == event )
1529 if ( IN_CLOSE_WRITE == event )
1530 return num_close_write;
1531 if ( IN_CLOSE_NOWRITE == event )
1532 return num_close_nowrite;
1533 if ( IN_OPEN == event )
1535 if ( IN_MOVED_FROM == event )
1536 return num_moved_from;
1537 if ( IN_MOVED_TO == event )
1538 return num_moved_to;
1539 if ( IN_CREATE == event )
1541 if ( IN_DELETE == event )
1543 if ( IN_DELETE_SELF == event )
1544 return num_delete_self;
1545 if ( IN_UNMOUNT == event )
1547 if ( IN_MOVE_SELF == event )
1548 return num_move_self;
1578 filename ), event );
1598 int isdir(
char const * path ) {
1599 static struct stat64 my_stat;
1601 if ( -1 == lstat64( path, &my_stat ) ) {
1602 if (errno == ENOENT)
return 0;
1603 fprintf(stderr,
"Stat failed on %s: %s\n", path, strerror(errno));
1607 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1619 rbwalk(tree_filename, get_num, (
void*)&ret);
1709 static char out[MAX_STRLEN+1];
1712 if ( -1 != ret ) fprintf( file,
"%s", out );
1818 struct inotify_event* event,
char* fmt ) {
1819 static char * filename, * eventname, * eventstr;
1820 static unsigned int i, ind;
1822 static char timestr[MAX_STRLEN];
1826 if ( event->len > 0 ) {
1827 eventname =
event->name;
1836 if ( !fmt || 0 == strlen(fmt) ) {
1840 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1846 for ( i = 0; i < strlen(fmt) &&
1847 (int)ind < size - 1; ++i ) {
1848 if ( fmt[i] !=
'%' ) {
1849 out[ind++] = fmt[i];
1853 if ( i == strlen(fmt) - 1 ) {
1869 strncpy( &out[ind], filename, size - ind );
1870 ind += strlen(filename);
1878 strncpy( &out[ind], eventname, size - ind );
1879 ind += strlen(eventname);
1887 strncpy( &out[ind], eventstr, size - ind );
1888 ind += strlen(eventstr);
1898 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1899 localtime( &now ) ) ) {
1910 strncpy( &out[ind], timestr, size - ind );
1911 ind += strlen(timestr);
1917 if ( i < strlen(fmt) - 2 && fmt[i+2] ==
'e' ) {
1919 strncpy( &out[ind], eventstr, size - ind );
1920 ind += strlen(eventstr);
1926 if ( ind < MAX_STRLEN ) out[ind++] =
'%';
1927 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1958 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) )
return -1;
1973 if ( !read_num_from_file( INSTANCES_PATH, &ret ) )
return -1;
1988 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) )
return -1;
2013 if (regex) { regfree(regex); }
2014 else { regex = (regex_t *)malloc(
sizeof(regex_t)); }
2016 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2017 if (0 == ret)
return 1;
2026 int event_compare(
const void *p1,
const void *p2,
const void *config)
2028 if (!p1 || !p2)
return p1 - p2;
2030 int sort_event = (int)config;
2031 if (sort_event == -1) {
2034 }
else if (sort_event < 0) {
2035 sort_event = -sort_event;
2038 int *i1 = stat_ptr((watch*)p1, sort_event);
2039 int *i2 = stat_ptr((watch*)p2, sort_event);
2040 if (0 == *i1 - *i2) {
2041 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2049 struct rbtree *inotifytools_wd_sorted_by_event(
int sort_event)
2051 struct rbtree *ret = rbinit(event_compare, (
void*)sort_event);
2052 RBLIST *all = rbopenlist(tree_wd);
2053 void const *p = rbreadlist(all);
2055 void const *r = rbsearch(p, ret);
2056 niceassert((
int)(r == p),
"Couldn't insert watch into new tree");
2057 p = rbreadlist(all);