libinotifytools

inotifytools.c
1 // kate: replace-tabs off; space-indent off;
2
15 #include "../../config.h"
16 #include "inotifytools/inotifytools.h"
17 #include "inotifytools_p.h"
18
19 #include <string.h>
20 #include <strings.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <sys/select.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <time.h>
31 #include <regex.h>
32 #include <setjmp.h>
33
34 #include "inotifytools/inotify.h"
35
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"
128
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;
145
146 struct rbtree *tree_wd = 0;
147 struct rbtree *tree_filename = 0;
148 static int error = 0;
149 static int init = 0;
150 static char* timefmt = 0;
151 static regex_t* regex = 0;
152 /* 0: --exclude[i], 1: --include[i] */
153 static int invert_regexp = 0;
154
155 static int isdir( char const * path );
156 void record_stats( struct inotify_event const * event );
157 int onestr_to_event(char const * event);
158
176 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
177 #cond, mesg)
178
179 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
180
198 static void _niceassert( long cond, int line, char const * file,
199 char const * condstr, char const * mesg ) {
200 if ( cond ) return;
201
202 if ( mesg ) {
203 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
204 condstr, mesg );
205 }
206 else {
207 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
208 }
209 }
210
220 char * chrtostr(char ch) {
221 static char str[2] = { '\0', '\0' };
222 str[0] = ch;
223 return str;
224 }
225
229 int read_num_from_file( char * filename, int * num ) {
230 FILE * file = fopen( filename, "r" );
231 if ( !file ) {
232 error = errno;
233 return 0;
234 }
235
236 if ( EOF == fscanf( file, "%d", num ) ) {
237 error = errno;
238 return 0;
239 }
240
241 niceassert( 0 == fclose( file ), 0 );
242
243 return 1;
244 }
245
246 int wd_compare(const void *d1, const void *d2, const void *config) {
247 if (!d1 || !d2) return d1 - d2;
248 return ((watch*)d1)->wd - ((watch*)d2)->wd;
249 }
250
251 int filename_compare(const void *d1, const void *d2, const void *config) {
252 if (!d1 || !d2) return d1 - d2;
253 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
254 }
255
259 watch *watch_from_wd( int wd ) {
260 watch w;
261 w.wd = wd;
262 return (watch*)rbfind(&w, tree_wd);
263 }
264
268 watch *watch_from_filename( char const *filename ) {
269 watch w;
270 w.filename = (char*)filename;
271 return (watch*)rbfind(&w, tree_filename);
272 }
273
283 int inotifytools_initialize() {
284 if (init) return 1;
285
286 error = 0;
287 // Try to initialise inotify
288 inotify_fd = inotify_init();
289 if (inotify_fd < 0) {
290 error = errno;
291 return 0;
292 }
293
294 collect_stats = 0;
295 init = 1;
296 tree_wd = rbinit(wd_compare, 0);
297 tree_filename = rbinit(filename_compare, 0);
298 timefmt = 0;
299
300 return 1;
301 }
302
306 void destroy_watch(watch *w) {
307 if (w->filename) free(w->filename);
308 free(w);
309 }
310
314 void cleanup_tree(const void *nodep,
315 const VISIT which,
316 const int depth, void* arg) {
317 if (which != endorder && which != leaf) return;
318 watch *w = (watch*)nodep;
319 destroy_watch(w);
320 }
321
328 void inotifytools_cleanup() {
329 if (!init) return;
330
331 init = 0;
332 close(inotify_fd);
333 collect_stats = 0;
334 error = 0;
335 timefmt = 0;
336
337 if (regex) {
338 regfree(regex);
339 free(regex);
340 regex = 0;
341 }
342
343 rbwalk(tree_wd, cleanup_tree, 0);
344 rbdestroy(tree_wd); tree_wd = 0;
345 rbdestroy(tree_filename); tree_filename = 0;
346 }
347
351 void empty_stats(const void *nodep,
352 const VISIT which,
353 const int depth, void *arg) {
354 if (which != endorder && which != leaf) return;
355 watch *w = (watch*)nodep;
356 w->hit_access = 0;
357 w->hit_modify = 0;
358 w->hit_attrib = 0;
359 w->hit_close_nowrite = 0;
360 w->hit_close_write = 0;
361 w->hit_open = 0;
362 w->hit_move_self = 0;
363 w->hit_moved_from = 0;
364 w->hit_moved_to = 0;
365 w->hit_create = 0;
366 w->hit_delete = 0;
367 w->hit_delete_self = 0;
368 w->hit_unmount = 0;
369 w->hit_total = 0;
370 }
371
375 void replace_filename(const void *nodep,
376 const VISIT which,
377 const int depth, void *arg) {
378 if (which != endorder && which != leaf) return;
379 watch *w = (watch*)nodep;
380 char *old_name = ((char**)arg)[0];
381 char *new_name = ((char**)arg)[1];
382 int old_len = *((int*)&((char**)arg)[2]);
383 char *name;
384 if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
385 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
386 if (!strcmp( w->filename, new_name )) {
387 free(name);
388 } else {
389 rbdelete(w, tree_filename);
390 free( w->filename );
391 w->filename = name;
392 rbsearch(w, tree_filename);
393 }
394 }
395 }
396
400 void get_num(const void *nodep,
401 const VISIT which,
402 const int depth, void *arg) {
403 if (which != endorder && which != leaf) return;
404 ++(*((int*)arg));
405 }
406
407
420 void inotifytools_initialize_stats() {
421 niceassert( init, "inotifytools_initialize not called yet" );
422
423 // if already collecting stats, reset stats
424 if (collect_stats) {
425 rbwalk(tree_wd, empty_stats, 0);
426 }
427
428 num_access = 0;
429 num_modify = 0;
430 num_attrib = 0;
431 num_close_nowrite = 0;
432 num_close_write = 0;
433 num_open = 0;
434 num_move_self = 0;
435 num_moved_from = 0;
436 num_moved_to = 0;
437 num_create = 0;
438 num_delete = 0;
439 num_delete_self = 0;
440 num_unmount = 0;
441 num_total = 0;
442
443 collect_stats = 1;
444 }
445
473 int inotifytools_str_to_event_sep(char const * event, char sep) {
474 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
475 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
476 return -1;
477 }
478
479 int ret, ret1, len;
480 char * event1, * event2;
481 char eventstr[4096];
482 ret = 0;
483
484 if ( !event || !event[0] ) return 0;
485
486 event1 = (char *)event;
487 event2 = strchr( event1, sep );
488 while ( event1 && event1[0] ) {
489 if ( event2 ) {
490 len = event2 - event1;
491 niceassert( len < 4096, "malformed event string (very long)" );
492 }
493 else {
494 len = strlen(event1);
495 }
496 if ( len > 4095 ) len = 4095;
497 strncpy( eventstr, event1, len );
498 eventstr[len] = 0;
499
500 ret1 = onestr_to_event( eventstr );
501 if ( 0 == ret1 || -1 == ret1 ) {
502 ret = ret1;
503 break;
504 }
505 ret |= ret1;
506
507 event1 = event2;
508 if ( event1 && event1[0] ) {
509 // jump over 'sep' character
510 ++event1;
511 // if last character was 'sep'...
512 if ( !event1[0] ) return 0;
513 event2 = strchr( event1, sep );
514 }
515 }
516
517 return ret;
518 }
519
543 int inotifytools_str_to_event(char const * event) {
544 return inotifytools_str_to_event_sep( event, ',' );
545 }
546
558 int onestr_to_event(char const * event)
559 {
560 static int ret;
561 ret = -1;
562
563 if ( !event || !event[0] )
564 ret = 0;
565 else if ( 0 == strcasecmp(event, "ACCESS") )
566 ret = IN_ACCESS;
567 else if ( 0 == strcasecmp(event, "MODIFY") )
568 ret = IN_MODIFY;
569 else if ( 0 == strcasecmp(event, "ATTRIB") )
570 ret = IN_ATTRIB;
571 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
572 ret = IN_CLOSE_WRITE;
573 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
574 ret = IN_CLOSE_NOWRITE;
575 else if ( 0 == strcasecmp(event, "OPEN") )
576 ret = IN_OPEN;
577 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
578 ret = IN_MOVED_FROM;
579 else if ( 0 == strcasecmp(event, "MOVED_TO") )
580 ret = IN_MOVED_TO;
581 else if ( 0 == strcasecmp(event, "CREATE") )
582 ret = IN_CREATE;
583 else if ( 0 == strcasecmp(event, "DELETE") )
584 ret = IN_DELETE;
585 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
586 ret = IN_DELETE_SELF;
587 else if ( 0 == strcasecmp(event, "UNMOUNT") )
588 ret = IN_UNMOUNT;
589 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
590 ret = IN_Q_OVERFLOW;
591 else if ( 0 == strcasecmp(event, "IGNORED") )
592 ret = IN_IGNORED;
593 else if ( 0 == strcasecmp(event, "CLOSE") )
594 ret = IN_CLOSE;
595 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
596 ret = IN_MOVE_SELF;
597 else if ( 0 == strcasecmp(event, "MOVE") )
598 ret = IN_MOVE;
599 else if ( 0 == strcasecmp(event, "ISDIR") )
600 ret = IN_ISDIR;
601 else if ( 0 == strcasecmp(event, "ONESHOT") )
602 ret = IN_ONESHOT;
603 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
604 ret = IN_ALL_EVENTS;
605
606 return ret;
607 }
608
630 char * inotifytools_event_to_str(int events) {
631 return inotifytools_event_to_str_sep(events, ',');
632 }
633
658 char * inotifytools_event_to_str_sep(int events, char sep)
659 {
660 static char ret[1024];
661 ret[0] = '\0';
662 ret[1] = '\0';
663
664 if ( IN_ACCESS & events ) {
665 strcat( ret, chrtostr(sep) );
666 strcat( ret, "ACCESS" );
667 }
668 if ( IN_MODIFY & events ) {
669 strcat( ret, chrtostr(sep) );
670 strcat( ret, "MODIFY" );
671 }
672 if ( IN_ATTRIB & events ) {
673 strcat( ret, chrtostr(sep) );
674 strcat( ret, "ATTRIB" );
675 }
676 if ( IN_CLOSE_WRITE & events ) {
677 strcat( ret, chrtostr(sep) );
678 strcat( ret, "CLOSE_WRITE" );
679 }
680 if ( IN_CLOSE_NOWRITE & events ) {
681 strcat( ret, chrtostr(sep) );
682 strcat( ret, "CLOSE_NOWRITE" );
683 }
684 if ( IN_OPEN & events ) {
685 strcat( ret, chrtostr(sep) );
686 strcat( ret, "OPEN" );
687 }
688 if ( IN_MOVED_FROM & events ) {
689 strcat( ret, chrtostr(sep) );
690 strcat( ret, "MOVED_FROM" );
691 }
692 if ( IN_MOVED_TO & events ) {
693 strcat( ret, chrtostr(sep) );
694 strcat( ret, "MOVED_TO" );
695 }
696 if ( IN_CREATE & events ) {
697 strcat( ret, chrtostr(sep) );
698 strcat( ret, "CREATE" );
699 }
700 if ( IN_DELETE & events ) {
701 strcat( ret, chrtostr(sep) );
702 strcat( ret, "DELETE" );
703 }
704 if ( IN_DELETE_SELF & events ) {
705 strcat( ret, chrtostr(sep) );
706 strcat( ret, "DELETE_SELF" );
707 }
708 if ( IN_UNMOUNT & events ) {
709 strcat( ret, chrtostr(sep) );
710 strcat( ret, "UNMOUNT" );
711 }
712 if ( IN_Q_OVERFLOW & events ) {
713 strcat( ret, chrtostr(sep) );
714 strcat( ret, "Q_OVERFLOW" );
715 }
716 if ( IN_IGNORED & events ) {
717 strcat( ret, chrtostr(sep) );
718 strcat( ret, "IGNORED" );
719 }
720 if ( IN_CLOSE & events ) {
721 strcat( ret, chrtostr(sep) );
722 strcat( ret, "CLOSE" );
723 }
724 if ( IN_MOVE_SELF & events ) {
725 strcat( ret, chrtostr(sep) );
726 strcat( ret, "MOVE_SELF" );
727 }
728 if ( IN_ISDIR & events ) {
729 strcat( ret, chrtostr(sep) );
730 strcat( ret, "ISDIR" );
731 }
732 if ( IN_ONESHOT & events ) {
733 strcat( ret, chrtostr(sep) );
734 strcat( ret, "ONESHOT" );
735 }
736
737 // Maybe we didn't match any... ?
738 if (ret[0] == '\0') {
739 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
740 }
741
742 return &ret[1];
743 }
744
765 char * inotifytools_filename_from_wd( int wd ) {
766 niceassert( init, "inotifytools_initialize not called yet" );
767 watch *w = watch_from_wd(wd);
768 if (!w)
769 return NULL;
770
771 return w->filename;
772 }
773
788 int inotifytools_wd_from_filename( char const * filename ) {
789 niceassert( init, "inotifytools_initialize not called yet" );
790 watch *w = watch_from_filename(filename);
791 if (!w) return -1;
792 return w->wd;
793 }
794
809 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
810 niceassert( init, "inotifytools_initialize not called yet" );
811 watch *w = watch_from_wd(wd);
812 if (!w) return;
813 if (w->filename) free(w->filename);
814 w->filename = strdup(filename);
815 }
816
831 void inotifytools_set_filename_by_filename( char const * oldname,
832 char const * newname ) {
833 watch *w = watch_from_filename(oldname);
834 if (!w) return;
835 if (w->filename) free(w->filename);
836 w->filename = strdup(newname);
837 }
838
861 void inotifytools_replace_filename( char const * oldname,
862 char const * newname ) {
863 if ( !oldname || !newname ) return;
864 char *names[2+sizeof(int)/sizeof(char*)];
865 names[0] = (char*)oldname;
866 names[1] = (char*)newname;
867 *((int*)&names[2]) = strlen(oldname);
868 rbwalk(tree_filename, replace_filename, (void*)names);
869 }
870
874 int remove_inotify_watch(watch *w) {
875 error = 0;
876 int status = inotify_rm_watch( inotify_fd, w->wd );
877 if ( status < 0 ) {
878 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
879 strerror(status) );
880 error = status;
881 return 0;
882 }
883 return 1;
884 }
885
889 watch *create_watch(int wd, char *filename) {
890 if ( wd <= 0 || !filename) return 0;
891
892 watch *w = (watch*)calloc(1, sizeof(watch));
893 w->wd = wd;
894 w->filename = strdup(filename);
895 rbsearch(w, tree_wd);
896 rbsearch(w, tree_filename);
897 }
898
911 int inotifytools_remove_watch_by_wd( int wd ) {
912 niceassert( init, "inotifytools_initialize not called yet" );
913 watch *w = watch_from_wd(wd);
914 if (!w) return 1;
915
916 if (!remove_inotify_watch(w)) return 0;
917 rbdelete(w, tree_wd);
918 rbdelete(w, tree_filename);
919 destroy_watch(w);
920 return 1;
921 }
922
934 int inotifytools_remove_watch_by_filename( char const * filename ) {
935 niceassert( init, "inotifytools_initialize not called yet" );
936 watch *w = watch_from_filename(filename);
937 if (!w) return 1;
938
939 if (!remove_inotify_watch(w)) return 0;
940 rbdelete(w, tree_wd);
941 rbdelete(w, tree_filename);
942 destroy_watch(w);
943 return 1;
944 }
945
957 int inotifytools_watch_file( char const * filename, int events ) {
958 static char const * filenames[2];
959 filenames[0] = filename;
960 filenames[1] = NULL;
961 return inotifytools_watch_files( filenames, events );
962 }
963
979 int inotifytools_watch_files( char const * filenames[], int events ) {
980 niceassert( init, "inotifytools_initialize not called yet" );
981 error = 0;
982
983 static int i;
984 for ( i = 0; filenames[i]; ++i ) {
985 static int wd;
986 wd = inotify_add_watch( inotify_fd, filenames[i], events );
987 if ( wd < 0 ) {
988 if ( wd == -1 ) {
989 error = errno;
990 return 0;
991 } // if ( wd == -1 )
992 else {
993 fprintf( stderr, "Failed to watch %s: returned wd was %d "
994 "(expected -1 or >0 )", filenames[i], wd );
995 // no appropriate value for error
996 return 0;
997 } // else
998 } // if ( wd < 0 )
999
1000 char *filename;
1001 // Always end filename with / if it is a directory
1002 if ( !isdir(filenames[i])
1003 || filenames[i][strlen(filenames[i])-1] == '/') {
1004 filename = strdup(filenames[i]);
1005 }
1006 else {
1007 nasprintf( &filename, "%s/", filenames[i] );
1008 }
1009 create_watch(wd, filename);
1010 free(filename);
1011 } // for
1012
1013 return 1;
1014 }
1015
1042 struct inotify_event * inotifytools_next_event( long int timeout ) {
1043 return inotifytools_next_events( timeout, 1 );
1044 }
1045
1046
1095 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1096 niceassert( init, "inotifytools_initialize not called yet" );
1097 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1098
1099 if ( num_events < 1 ) return NULL;
1100
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;
1105 static jmp_buf jmp;
1106 static char match_name[MAX_STRLEN];
1107
1108 #define RETURN(A) {\
1109 if (regex) {\
1110 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1111 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1112 if (!invert_regexp)\
1113 longjmp(jmp,0);\
1114 } else {\
1115 if (invert_regexp)\
1116 longjmp(jmp,0);\
1117 }\
1118 }\
1119 if ( collect_stats ) {\
1120 record_stats( A );\
1121 }\
1122 return A;\
1123 }
1124
1125 setjmp(jmp);
1126
1127 error = 0;
1128
1129 // first_byte is index into event buffer
1130 if ( first_byte != 0
1131 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1132
1133 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1134 first_byte += sizeof(struct inotify_event) + ret->len;
1135
1136 // if the pointer to the next event exactly hits end of bytes read,
1137 // that's good. next time we're called, we'll read.
1138 if ( first_byte == bytes ) {
1139 first_byte = 0;
1140 }
1141 else if ( first_byte > bytes ) {
1142 // oh... no. this can't be happening. An incomplete event.
1143 // Copy what we currently have into first element, call self to
1144 // read remainder.
1145 // oh, and they BETTER NOT overlap.
1146 // Boy I hope this code works.
1147 // But I think this can never happen due to how inotify is written.
1148 niceassert( (long)((char *)&event[0] +
1149 sizeof(struct inotify_event) +
1150 event[0].len) <= (long)ret,
1151 "extremely unlucky user, death imminent" );
1152 // how much of the event do we have?
1153 bytes = (char *)&event[0] + bytes - (char *)ret;
1154 memcpy( &event[0], ret, bytes );
1155 return inotifytools_next_events( timeout, num_events );
1156 }
1157 RETURN(ret);
1158
1159 }
1160
1161 else if ( first_byte == 0 ) {
1162 bytes = 0;
1163 }
1164
1165
1166 static ssize_t this_bytes;
1167 static unsigned int bytes_to_read;
1168 static int rc;
1169 static fd_set read_fds;
1170
1171 static struct timeval read_timeout;
1172 read_timeout.tv_sec = timeout;
1173 read_timeout.tv_usec = 0;
1174 static struct timeval * read_timeout_ptr;
1175 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1176
1177 FD_ZERO(&read_fds);
1178 FD_SET(inotify_fd, &read_fds);
1179 rc = select(inotify_fd + 1, &read_fds,
1180 NULL, NULL, read_timeout_ptr);
1181 if ( rc < 0 ) {
1182 // error
1183 error = errno;
1184 return NULL;
1185 }
1186 else if ( rc == 0 ) {
1187 // timeout
1188 return NULL;
1189 }
1190
1191 // wait until we have enough bytes to read
1192 do {
1193 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1194 } while ( !rc &&
1195 bytes_to_read < sizeof(struct inotify_event)*num_events );
1196
1197 if ( rc == -1 ) {
1198 error = errno;
1199 return NULL;
1200 }
1201
1202 this_bytes = read(inotify_fd, &event[0] + bytes,
1203 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1204 if ( this_bytes < 0 ) {
1205 error = errno;
1206 return NULL;
1207 }
1208 if ( this_bytes == 0 ) {
1209 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1210 "events occurred at once.\n");
1211 return NULL;
1212 }
1213 bytes += this_bytes;
1214
1215 ret = &event[0];
1216 first_byte = sizeof(struct inotify_event) + ret->len;
1217 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1218 "almost certainly screw up." );
1219 if ( first_byte == bytes ) {
1220 first_byte = 0;
1221 }
1222
1223 RETURN(ret);
1224
1225 #undef RETURN
1226 }
1227
1253 int inotifytools_watch_recursively( char const * path, int events ) {
1254 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1255 }
1256
1289 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1290 char const ** exclude_list ) {
1291 niceassert( init, "inotifytools_initialize not called yet" );
1292
1293 DIR * dir;
1294 char * my_path;
1295 error = 0;
1296 dir = opendir( path );
1297 if ( !dir ) {
1298 // If not a directory, don't need to do anything special
1299 if ( errno == ENOTDIR ) {
1300 return inotifytools_watch_file( path, events );
1301 }
1302 else {
1303 error = errno;
1304 return 0;
1305 }
1306 }
1307
1308 if ( path[strlen(path)-1] != '/' ) {
1309 nasprintf( &my_path, "%s/", path );
1310 }
1311 else {
1312 my_path = (char *)path;
1313 }
1314
1315 static struct dirent * ent;
1316 char * next_file;
1317 static struct stat64 my_stat;
1318 ent = readdir( dir );
1319 // Watch each directory within this directory
1320 while ( ent ) {
1321 if ( (0 != strcmp( ent->d_name, "." )) &&
1322 (0 != strcmp( ent->d_name, ".." )) ) {
1323 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1324 if ( -1 == lstat64( next_file, &my_stat ) ) {
1325 error = errno;
1326 free( next_file );
1327 if ( errno != EACCES ) {
1328 error = errno;
1329 if ( my_path != path ) free( my_path );
1330 closedir( dir );
1331 return 0;
1332 }
1333 }
1334 else if ( S_ISDIR( my_stat.st_mode ) &&
1335 !S_ISLNK( my_stat.st_mode )) {
1336 free( next_file );
1337 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1338 static unsigned int no_watch;
1339 static char const ** exclude_entry;
1340
1341 no_watch = 0;
1342 for (exclude_entry = exclude_list;
1343 exclude_entry && *exclude_entry && !no_watch;
1344 ++exclude_entry) {
1345 static int exclude_length;
1346
1347 exclude_length = strlen(*exclude_entry);
1348 if ((*exclude_entry)[exclude_length-1] == '/') {
1349 --exclude_length;
1350 }
1351 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1352 !strncmp(*exclude_entry, next_file, exclude_length)) {
1353 // directory found in exclude list
1354 no_watch = 1;
1355 }
1356 }
1357 if (!no_watch) {
1358 static int status;
1359 status = inotifytools_watch_recursively_with_exclude(
1360 next_file,
1361 events,
1362 exclude_list );
1363 // For some errors, we will continue.
1364 if ( !status && (EACCES != error) && (ENOENT != error) &&
1365 (ELOOP != error) ) {
1366 free( next_file );
1367 if ( my_path != path ) free( my_path );
1368 closedir( dir );
1369 return 0;
1370 }
1371 } // if !no_watch
1372 free( next_file );
1373 } // if isdir and not islnk
1374 else {
1375 free( next_file );
1376 }
1377 }
1378 ent = readdir( dir );
1379 error = 0;
1380 }
1381
1382 closedir( dir );
1383
1384 int ret = inotifytools_watch_file( my_path, events );
1385 if ( my_path != path ) free( my_path );
1386 return ret;
1387 }
1388
1392 void record_stats( struct inotify_event const * event ) {
1393 if (!event) return;
1394 watch *w = watch_from_wd(event->wd);
1395 if (!w) return;
1396 if ( IN_ACCESS & event->mask ) {
1397 ++w->hit_access;
1398 ++num_access;
1399 }
1400 if ( IN_MODIFY & event->mask ) {
1401 ++w->hit_modify;
1402 ++num_modify;
1403 }
1404 if ( IN_ATTRIB & event->mask ) {
1405 ++w->hit_attrib;
1406 ++num_attrib;
1407 }
1408 if ( IN_CLOSE_WRITE & event->mask ) {
1409 ++w->hit_close_write;
1410 ++num_close_write;
1411 }
1412 if ( IN_CLOSE_NOWRITE & event->mask ) {
1413 ++w->hit_close_nowrite;
1414 ++num_close_nowrite;
1415 }
1416 if ( IN_OPEN & event->mask ) {
1417 ++w->hit_open;
1418 ++num_open;
1419 }
1420 if ( IN_MOVED_FROM & event->mask ) {
1421 ++w->hit_moved_from;
1422 ++num_moved_from;
1423 }
1424 if ( IN_MOVED_TO & event->mask ) {
1425 ++w->hit_moved_to;
1426 ++num_moved_to;
1427 }
1428 if ( IN_CREATE & event->mask ) {
1429 ++w->hit_create;
1430 ++num_create;
1431 }
1432 if ( IN_DELETE & event->mask ) {
1433 ++w->hit_delete;
1434 ++num_delete;
1435 }
1436 if ( IN_DELETE_SELF & event->mask ) {
1437 ++w->hit_delete_self;
1438 ++num_delete_self;
1439 }
1440 if ( IN_UNMOUNT & event->mask ) {
1441 ++w->hit_unmount;
1442 ++num_unmount;
1443 }
1444 if ( IN_MOVE_SELF & event->mask ) {
1445 ++w->hit_move_self;
1446 ++num_move_self;
1447 }
1448
1449 ++w->hit_total;
1450 ++num_total;
1451
1452 }
1453
1454 int *stat_ptr(watch *w, int event)
1455 {
1456 if ( IN_ACCESS == event )
1457 return &w->hit_access;
1458 if ( IN_MODIFY == event )
1459 return &w->hit_modify;
1460 if ( IN_ATTRIB == event )
1461 return &w->hit_attrib;
1462 if ( IN_CLOSE_WRITE == event )
1463 return &w->hit_close_write;
1464 if ( IN_CLOSE_NOWRITE == event )
1465 return &w->hit_close_nowrite;
1466 if ( IN_OPEN == event )
1467 return &w->hit_open;
1468 if ( IN_MOVED_FROM == event )
1469 return &w->hit_moved_from;
1470 if ( IN_MOVED_TO == event )
1471 return &w->hit_moved_to;
1472 if ( IN_CREATE == event )
1473 return &w->hit_create;
1474 if ( IN_DELETE == event )
1475 return &w->hit_delete;
1476 if ( IN_DELETE_SELF == event )
1477 return &w->hit_delete_self;
1478 if ( IN_UNMOUNT == event )
1479 return &w->hit_unmount;
1480 if ( IN_MOVE_SELF == event )
1481 return &w->hit_move_self;
1482 if ( 0 == event )
1483 return &w->hit_total;
1484 return 0;
1485 }
1486
1502 int inotifytools_get_stat_by_wd( int wd, int event ) {
1503 if (!collect_stats) return -1;
1504
1505 watch *w = watch_from_wd(wd);
1506 if (!w) return -1;
1507 int *i = stat_ptr(w, event);
1508 if (!i) return -1;
1509 return *i;
1510 }
1511
1525 int inotifytools_get_stat_total( int event ) {
1526 if (!collect_stats) return -1;
1527 if ( IN_ACCESS == event )
1528 return num_access;
1529 if ( IN_MODIFY == event )
1530 return num_modify;
1531 if ( IN_ATTRIB == event )
1532 return num_attrib;
1533 if ( IN_CLOSE_WRITE == event )
1534 return num_close_write;
1535 if ( IN_CLOSE_NOWRITE == event )
1536 return num_close_nowrite;
1537 if ( IN_OPEN == event )
1538 return num_open;
1539 if ( IN_MOVED_FROM == event )
1540 return num_moved_from;
1541 if ( IN_MOVED_TO == event )
1542 return num_moved_to;
1543 if ( IN_CREATE == event )
1544 return num_create;
1545 if ( IN_DELETE == event )
1546 return num_delete;
1547 if ( IN_DELETE_SELF == event )
1548 return num_delete_self;
1549 if ( IN_UNMOUNT == event )
1550 return num_unmount;
1551 if ( IN_MOVE_SELF == event )
1552 return num_move_self;
1553
1554 if ( 0 == event )
1555 return num_total;
1556
1557 return -1;
1558 }
1559
1579 int inotifytools_get_stat_by_filename( char const * filename,
1580 int event ) {
1581 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
1582 filename ), event );
1583 }
1584
1595 int inotifytools_error() {
1596 return error;
1597 }
1598
1602 static int isdir( char const * path ) {
1603 static struct stat64 my_stat;
1604
1605 if ( -1 == lstat64( path, &my_stat ) ) {
1606 if (errno == ENOENT) return 0;
1607 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1608 return 0;
1609 }
1610
1611 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1612 }
1613
1614
1621 int inotifytools_get_num_watches() {
1622 int ret = 0;
1623 rbwalk(tree_filename, get_num, (void*)&ret);
1624 return ret;
1625 }
1626
1667 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1668 return inotifytools_fprintf( stdout, event, fmt );
1669 }
1670
1712 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1713 static char out[MAX_STRLEN+1];
1714 static int ret;
1715 ret = inotifytools_sprintf( out, event, fmt );
1716 if ( -1 != ret ) fprintf( file, "%s", out );
1717 return ret;
1718 }
1719
1770 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1771 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1772 }
1773
1774
1821 int inotifytools_snprintf( char * out, int size,
1822 struct inotify_event* event, char* fmt ) {
1823 static char * filename, * eventname, * eventstr;
1824 static unsigned int i, ind;
1825 static char ch1;
1826 static char timestr[MAX_STRLEN];
1827 static time_t now;
1828
1829
1830 if ( event->len > 0 ) {
1831 eventname = event->name;
1832 }
1833 else {
1834 eventname = NULL;
1835 }
1836
1837
1838 filename = inotifytools_filename_from_wd( event->wd );
1839
1840 if ( !fmt || 0 == strlen(fmt) ) {
1841 error = EINVAL;
1842 return -1;
1843 }
1844 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1845 error = EMSGSIZE;
1846 return -1;
1847 }
1848
1849 ind = 0;
1850 for ( i = 0; i < strlen(fmt) &&
1851 (int)ind < size - 1; ++i ) {
1852 if ( fmt[i] != '%' ) {
1853 out[ind++] = fmt[i];
1854 continue;
1855 }
1856
1857 if ( i == strlen(fmt) - 1 ) {
1858 // last character is %, invalid
1859 error = EINVAL;
1860 return ind;
1861 }
1862
1863 ch1 = fmt[i+1];
1864
1865 if ( ch1 == '%' ) {
1866 out[ind++] = '%';
1867 ++i;
1868 continue;
1869 }
1870
1871 if ( ch1 == 'w' ) {
1872 if ( filename ) {
1873 strncpy( &out[ind], filename, size - ind );
1874 ind += strlen(filename);
1875 }
1876 ++i;
1877 continue;
1878 }
1879
1880 if ( ch1 == 'f' ) {
1881 if ( eventname ) {
1882 strncpy( &out[ind], eventname, size - ind );
1883 ind += strlen(eventname);
1884 }
1885 ++i;
1886 continue;
1887 }
1888
1889 if ( ch1 == 'e' ) {
1890 eventstr = inotifytools_event_to_str( event->mask );
1891 strncpy( &out[ind], eventstr, size - ind );
1892 ind += strlen(eventstr);
1893 ++i;
1894 continue;
1895 }
1896
1897 if ( ch1 == 'T' ) {
1898
1899 if ( timefmt ) {
1900
1901 now = time(0);
1902 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1903 localtime( &now ) ) ) {
1904
1905 // time format probably invalid
1906 error = EINVAL;
1907 return ind;
1908 }
1909 }
1910 else {
1911 timestr[0] = 0;
1912 }
1913
1914 strncpy( &out[ind], timestr, size - ind );
1915 ind += strlen(timestr);
1916 ++i;
1917 continue;
1918 }
1919
1920 // Check if next char in fmt is e
1921 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1922 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1923 strncpy( &out[ind], eventstr, size - ind );
1924 ind += strlen(eventstr);
1925 i += 2;
1926 continue;
1927 }
1928
1929 // OK, this wasn't a special format character, just output it as normal
1930 if ( ind < MAX_STRLEN ) out[ind++] = '%';
1931 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1932 ++i;
1933 }
1934 out[ind] = 0;
1935
1936 return ind - 1;
1937 }
1938
1948 void inotifytools_set_printf_timefmt( char * fmt ) {
1949 timefmt = fmt;
1950 }
1951
1960 int inotifytools_get_max_queued_events() {
1961 int ret;
1962 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1963 return ret;
1964 }
1965
1975 int inotifytools_get_max_user_instances() {
1976 int ret;
1977 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1978 return ret;
1979 }
1980
1990 int inotifytools_get_max_user_watches() {
1991 int ret;
1992 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
1993 return ret;
1994 }
1995
2009 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2010 if (!pattern) {
2011 if (regex) {
2012 regfree(regex);
2013 free(regex);
2014 regex = 0;
2015 }
2016 return 1;
2017 }
2018
2019 if (regex) { regfree(regex); }
2020 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2021
2022 invert_regexp = invert;
2023 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2024 if (0 == ret) return 1;
2025
2026 regfree(regex);
2027 free(regex);
2028 regex = 0;
2029 error = EINVAL;
2030 return 0;
2031 }
2032
2044 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2045 return do_ignore_events_by_regex(pattern, flags, 0);
2046 }
2047
2059 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2060 return do_ignore_events_by_regex(pattern, flags, 1);
2061 }
2062
2063 int event_compare(const void *p1, const void *p2, const void *config)
2064 {
2065 if (!p1 || !p2) return p1 - p2;
2066 char asc = 1;
2067 int sort_event = (int)config;
2068 if (sort_event == -1) {
2069 sort_event = 0;
2070 asc = 0;
2071 } else if (sort_event < 0) {
2072 sort_event = -sort_event;
2073 asc = 0;
2074 }
2075 int *i1 = stat_ptr((watch*)p1, sort_event);
2076 int *i2 = stat_ptr((watch*)p2, sort_event);
2077 if (0 == *i1 - *i2) {
2078 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2079 }
2080 if (asc)
2081 return *i1 - *i2;
2082 else
2083 return *i2 - *i1;
2084 }
2085
2086 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2087 {
2088 struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
2089 RBLIST *all = rbopenlist(tree_wd);
2090 void const *p = rbreadlist(all);
2091 while (p) {
2092 void const *r = rbsearch(p, ret);
2093 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2094 p = rbreadlist(all);
2095 }
2096 rbcloselist(all);
2097 return ret;
2098 }
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:765
inotifytools_snprintf
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1821
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1667
inotifytools_remove_watch_by_wd
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:911
inotifytools.h
inotifytools library public interface.
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1948
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:1990
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1289
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1095
inotifytools_sprintf
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1770
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:420
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1712
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:630
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1525
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:831
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:788
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:809
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:979
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1621
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1502
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1595
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:934
inotifytools_ignore_events_by_inverted_regex
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
Definition: inotifytools.c:2059
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:957
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:1975
inotifytools_cleanup
void inotifytools_cleanup()
Definition: inotifytools.c:328
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2044
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:861
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1042
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:543
inotifytools_initialize
int inotifytools_initialize()
Definition: inotifytools.c:283
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1579
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:473
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:1960
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:658
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1253