libinotifytools
inotifytools.c
1// kate: replace-tabs off; space-indent off;
2
15#include "../../config.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
129static int inotify_fd;
130static unsigned num_access;
131static unsigned num_modify;
132static unsigned num_attrib;
133static unsigned num_close_nowrite;
134static unsigned num_close_write;
135static unsigned num_open;
136static unsigned num_move_self;
137static unsigned num_moved_to;
138static unsigned num_moved_from;
139static unsigned num_create;
140static unsigned num_delete;
141static unsigned num_delete_self;
142static unsigned num_unmount;
143static unsigned num_total;
144static int collect_stats = 0;
145
146struct rbtree *tree_wd = 0;
147struct rbtree *tree_filename = 0;
148static int error = 0;
149static int init = 0;
150static char* timefmt = 0;
151static regex_t* regex = 0;
152
153int isdir( char const * path );
154void record_stats( struct inotify_event const * event );
155int onestr_to_event(char const * event);
156
174#define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
175 #cond, mesg)
176
177#define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
178
196void _niceassert( long cond, int line, char const * file, char const * condstr,
197 char const * mesg ) {
198 if ( cond ) return;
199
200 if ( mesg ) {
201 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
202 condstr, mesg );
203 }
204 else {
205 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
206 }
207}
208
218char * chrtostr(char ch) {
219 static char str[2] = { '\0', '\0' };
220 str[0] = ch;
221 return str;
222}
223
227int read_num_from_file( char * filename, int * num ) {
228 FILE * file = fopen( filename, "r" );
229 if ( !file ) {
230 error = errno;
231 return 0;
232 }
233
234 if ( EOF == fscanf( file, "%d", num ) ) {
235 error = errno;
236 return 0;
237 }
238
239 niceassert( 0 == fclose( file ), 0 );
240
241 return 1;
242}
243
244int 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;
247}
248
249int 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);
252}
253
257watch *watch_from_wd( int wd ) {
258 watch w;
259 w.wd = wd;
260 return (watch*)rbfind(&w, tree_wd);
261}
262
266watch *watch_from_filename( char const *filename ) {
267 watch w;
268 w.filename = (char*)filename;
269 return (watch*)rbfind(&w, tree_filename);
270}
271
282 if (init) return 1;
283
284 error = 0;
285 // Try to initialise inotify
286 inotify_fd = inotify_init();
287 if (inotify_fd < 0) {
288 error = inotify_fd;
289 return 0;
290 }
291
292 collect_stats = 0;
293 init = 1;
294 tree_wd = rbinit(wd_compare, 0);
295 tree_filename = rbinit(filename_compare, 0);
296 timefmt = 0;
297
298 return 1;
299}
300
304void destroy_watch(watch *w) {
305 if (w->filename) free(w->filename);
306 free(w);
307}
308
312void cleanup_tree(const void *nodep,
313 const VISIT which,
314 const int depth, void* arg) {
315 if (which != endorder && which != leaf) return;
316 watch *w = (watch*)nodep;
317 destroy_watch(w);
318}
319
327 if (!init) return;
328
329 init = 0;
330 close(inotify_fd);
331 collect_stats = 0;
332 error = 0;
333 timefmt = 0;
334
335 if (regex) {
336 regfree(regex);
337 free(regex);
338 regex = 0;
339 }
340
341 rbwalk(tree_wd, cleanup_tree, 0);
342 rbdestroy(tree_wd); tree_wd = 0;
343 rbdestroy(tree_filename); tree_filename = 0;
344}
345
349void empty_stats(const void *nodep,
350 const VISIT which,
351 const int depth, void *arg) {
352 if (which != endorder && which != leaf) return;
353 watch *w = (watch*)nodep;
354 w->hit_access = 0;
355 w->hit_modify = 0;
356 w->hit_attrib = 0;
357 w->hit_close_nowrite = 0;
358 w->hit_close_write = 0;
359 w->hit_open = 0;
360 w->hit_move_self = 0;
361 w->hit_moved_from = 0;
362 w->hit_moved_to = 0;
363 w->hit_create = 0;
364 w->hit_delete = 0;
365 w->hit_delete_self = 0;
366 w->hit_unmount = 0;
367 w->hit_total = 0;
368}
369
373void replace_filename(const void *nodep,
374 const VISIT which,
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]);
381 char *name;
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 )) {
385 free(name);
386 } else {
387 rbdelete(w, tree_filename);
388 free( w->filename );
389 w->filename = name;
390 rbsearch(w, tree_filename);
391 }
392 }
393}
394
398void get_num(const void *nodep,
399 const VISIT which,
400 const int depth, void *arg) {
401 if (which != endorder && which != leaf) return;
402 ++(*((int*)arg));
403}
404
405
419 niceassert( init, "inotifytools_initialize not called yet" );
420
421 // if already collecting stats, reset stats
422 if (collect_stats) {
423 rbwalk(tree_wd, empty_stats, 0);
424 }
425
426 num_access = 0;
427 num_modify = 0;
428 num_attrib = 0;
429 num_close_nowrite = 0;
430 num_close_write = 0;
431 num_open = 0;
432 num_move_self = 0;
433 num_moved_from = 0;
434 num_moved_to = 0;
435 num_create = 0;
436 num_delete = 0;
437 num_delete_self = 0;
438 num_unmount = 0;
439 num_total = 0;
440
441 collect_stats = 1;
442}
443
471int inotifytools_str_to_event_sep(char const * event, char sep) {
472 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
473 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
474 return -1;
475 }
476
477 int ret, ret1, len;
478 char * event1, * event2;
479 char eventstr[4096];
480 ret = 0;
481
482 if ( !event || !event[0] ) return 0;
483
484 event1 = (char *)event;
485 event2 = strchr( event1, sep );
486 while ( event1 && event1[0] ) {
487 if ( event2 ) {
488 len = event2 - event1;
489 niceassert( len < 4096, "malformed event string (very long)" );
490 }
491 else {
492 len = strlen(event1);
493 }
494 if ( len > 4095 ) len = 4095;
495 strncpy( eventstr, event1, len );
496 eventstr[len] = 0;
497
498 ret1 = onestr_to_event( eventstr );
499 if ( 0 == ret1 || -1 == ret1 ) {
500 ret = ret1;
501 break;
502 }
503 ret |= ret1;
504
505 event1 = event2;
506 if ( event1 && event1[0] ) {
507 // jump over 'sep' character
508 ++event1;
509 // if last character was 'sep'...
510 if ( !event1[0] ) return 0;
511 event2 = strchr( event1, sep );
512 }
513 }
514
515 return ret;
516}
517
541int inotifytools_str_to_event(char const * event) {
542 return inotifytools_str_to_event_sep( event, ',' );
543}
544
556int onestr_to_event(char const * event)
557{
558 static int ret;
559 ret = -1;
560
561 if ( !event || !event[0] )
562 ret = 0;
563 else if ( 0 == strcasecmp(event, "ACCESS") )
564 ret = IN_ACCESS;
565 else if ( 0 == strcasecmp(event, "MODIFY") )
566 ret = IN_MODIFY;
567 else if ( 0 == strcasecmp(event, "ATTRIB") )
568 ret = IN_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") )
574 ret = IN_OPEN;
575 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
576 ret = IN_MOVED_FROM;
577 else if ( 0 == strcasecmp(event, "MOVED_TO") )
578 ret = IN_MOVED_TO;
579 else if ( 0 == strcasecmp(event, "CREATE") )
580 ret = IN_CREATE;
581 else if ( 0 == strcasecmp(event, "DELETE") )
582 ret = IN_DELETE;
583 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
584 ret = IN_DELETE_SELF;
585 else if ( 0 == strcasecmp(event, "UNMOUNT") )
586 ret = IN_UNMOUNT;
587 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
588 ret = IN_Q_OVERFLOW;
589 else if ( 0 == strcasecmp(event, "IGNORED") )
590 ret = IN_IGNORED;
591 else if ( 0 == strcasecmp(event, "CLOSE") )
592 ret = IN_CLOSE;
593 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
594 ret = IN_MOVE_SELF;
595 else if ( 0 == strcasecmp(event, "MOVE") )
596 ret = IN_MOVE;
597 else if ( 0 == strcasecmp(event, "ISDIR") )
598 ret = IN_ISDIR;
599 else if ( 0 == strcasecmp(event, "ONESHOT") )
600 ret = IN_ONESHOT;
601 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
602 ret = IN_ALL_EVENTS;
603
604 return ret;
605}
606
628char * inotifytools_event_to_str(int events) {
629 return inotifytools_event_to_str_sep(events, ',');
630}
631
656char * inotifytools_event_to_str_sep(int events, char sep)
657{
658 static char ret[1024];
659 ret[0] = '\0';
660 ret[1] = '\0';
661
662 if ( IN_ACCESS & events ) {
663 strcat( ret, chrtostr(sep) );
664 strcat( ret, "ACCESS" );
665 }
666 if ( IN_MODIFY & events ) {
667 strcat( ret, chrtostr(sep) );
668 strcat( ret, "MODIFY" );
669 }
670 if ( IN_ATTRIB & events ) {
671 strcat( ret, chrtostr(sep) );
672 strcat( ret, "ATTRIB" );
673 }
674 if ( IN_CLOSE_WRITE & events ) {
675 strcat( ret, chrtostr(sep) );
676 strcat( ret, "CLOSE_WRITE" );
677 }
678 if ( IN_CLOSE_NOWRITE & events ) {
679 strcat( ret, chrtostr(sep) );
680 strcat( ret, "CLOSE_NOWRITE" );
681 }
682 if ( IN_OPEN & events ) {
683 strcat( ret, chrtostr(sep) );
684 strcat( ret, "OPEN" );
685 }
686 if ( IN_MOVED_FROM & events ) {
687 strcat( ret, chrtostr(sep) );
688 strcat( ret, "MOVED_FROM" );
689 }
690 if ( IN_MOVED_TO & events ) {
691 strcat( ret, chrtostr(sep) );
692 strcat( ret, "MOVED_TO" );
693 }
694 if ( IN_CREATE & events ) {
695 strcat( ret, chrtostr(sep) );
696 strcat( ret, "CREATE" );
697 }
698 if ( IN_DELETE & events ) {
699 strcat( ret, chrtostr(sep) );
700 strcat( ret, "DELETE" );
701 }
702 if ( IN_DELETE_SELF & events ) {
703 strcat( ret, chrtostr(sep) );
704 strcat( ret, "DELETE_SELF" );
705 }
706 if ( IN_UNMOUNT & events ) {
707 strcat( ret, chrtostr(sep) );
708 strcat( ret, "UNMOUNT" );
709 }
710 if ( IN_Q_OVERFLOW & events ) {
711 strcat( ret, chrtostr(sep) );
712 strcat( ret, "Q_OVERFLOW" );
713 }
714 if ( IN_IGNORED & events ) {
715 strcat( ret, chrtostr(sep) );
716 strcat( ret, "IGNORED" );
717 }
718 if ( IN_CLOSE & events ) {
719 strcat( ret, chrtostr(sep) );
720 strcat( ret, "CLOSE" );
721 }
722 if ( IN_MOVE_SELF & events ) {
723 strcat( ret, chrtostr(sep) );
724 strcat( ret, "MOVE_SELF" );
725 }
726 if ( IN_ISDIR & events ) {
727 strcat( ret, chrtostr(sep) );
728 strcat( ret, "ISDIR" );
729 }
730 if ( IN_ONESHOT & events ) {
731 strcat( ret, chrtostr(sep) );
732 strcat( ret, "ONESHOT" );
733 }
734
735 // Maybe we didn't match any... ?
736 if (ret[0] == '\0') {
737 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
738 }
739
740 return &ret[1];
741}
742
764 niceassert( init, "inotifytools_initialize not called yet" );
765 watch *w = watch_from_wd(wd);
766 if (!w)
767 return NULL;
768
769 return w->filename;
770}
771
786int inotifytools_wd_from_filename( char const * filename ) {
787 niceassert( init, "inotifytools_initialize not called yet" );
788 watch *w = watch_from_filename(filename);
789 if (!w) return -1;
790 return w->wd;
791}
792
807void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
808 niceassert( init, "inotifytools_initialize not called yet" );
809 watch *w = watch_from_wd(wd);
810 if (!w) return;
811 if (w->filename) free(w->filename);
812 w->filename = strdup(filename);
813}
814
829void inotifytools_set_filename_by_filename( char const * oldname,
830 char const * newname ) {
831 watch *w = watch_from_filename(oldname);
832 if (!w) return;
833 if (w->filename) free(w->filename);
834 w->filename = strdup(newname);
835}
836
859void inotifytools_replace_filename( char const * oldname,
860 char const * newname ) {
861 if ( !oldname || !newname ) return;
862 char *names[2+sizeof(int)/sizeof(char*) + 1];
863 names[0] = (char*)oldname;
864 names[1] = (char*)newname;
865 *((int*)&names[2]) = strlen(oldname);
866 rbwalk(tree_filename, replace_filename, (void*)names);
867}
868
872int remove_inotify_watch(watch *w) {
873 error = 0;
874 int status = inotify_rm_watch( inotify_fd, w->wd );
875 if ( status < 0 ) {
876 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
877 strerror(status) );
878 error = status;
879 return 0;
880 }
881 return 1;
882}
883
887watch *create_watch(int wd, char *filename) {
888 if ( wd <= 0 || !filename) return 0;
889
890 watch *w = (watch*)calloc(1, sizeof(watch));
891 w->wd = wd;
892 w->filename = strdup(filename);
893 rbsearch(w, tree_wd);
894 rbsearch(w, tree_filename);
895}
896
910 niceassert( init, "inotifytools_initialize not called yet" );
911 watch *w = watch_from_wd(wd);
912 if (!w) return 1;
913
914 if (!remove_inotify_watch(w)) return 0;
915 rbdelete(w, tree_wd);
916 rbdelete(w, tree_filename);
917 destroy_watch(w);
918 return 1;
919}
920
932int inotifytools_remove_watch_by_filename( char const * filename ) {
933 niceassert( init, "inotifytools_initialize not called yet" );
934 watch *w = watch_from_filename(filename);
935 if (!w) return 1;
936
937 if (!remove_inotify_watch(w)) return 0;
938 rbdelete(w, tree_wd);
939 rbdelete(w, tree_filename);
940 destroy_watch(w);
941 return 1;
942}
943
955int inotifytools_watch_file( char const * filename, int events ) {
956 static char const * filenames[2];
957 filenames[0] = filename;
958 filenames[1] = NULL;
959 return inotifytools_watch_files( filenames, events );
960}
961
977int inotifytools_watch_files( char const * filenames[], int events ) {
978 niceassert( init, "inotifytools_initialize not called yet" );
979 error = 0;
980
981 static int i;
982 for ( i = 0; filenames[i]; ++i ) {
983 static int wd;
984 wd = inotify_add_watch( inotify_fd, filenames[i], events );
985 if ( wd < 0 ) {
986 if ( wd == -1 ) {
987 error = errno;
988 return 0;
989 } // if ( wd == -1 )
990 else {
991 fprintf( stderr, "Failed to watch %s: returned wd was %d "
992 "(expected -1 or >0 )", filenames[i], wd );
993 // no appropriate value for error
994 return 0;
995 } // else
996 } // if ( wd < 0 )
997
998 char *filename;
999 // Always end filename with / if it is a directory
1000 if ( !isdir(filenames[i])
1001 || filenames[i][strlen(filenames[i])-1] == '/') {
1002 filename = strdup(filenames[i]);
1003 }
1004 else {
1005 nasprintf( &filename, "%s/", filenames[i] );
1006 }
1007 create_watch(wd, filename);
1008 free(filename);
1009 } // for
1010
1011 return 1;
1012}
1013
1040struct inotify_event * inotifytools_next_event( int timeout ) {
1041 return inotifytools_next_events( timeout, 1 );
1042}
1043
1044
1094struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
1095 niceassert( init, "inotifytools_initialize not called yet" );
1096 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1097
1098 if ( num_events < 1 ) return NULL;
1099
1100 static struct inotify_event event[MAX_EVENTS];
1101 static struct inotify_event * ret;
1102 static int first_byte = 0;
1103 static ssize_t bytes;
1104 static jmp_buf jmp;
1105 static char match_name[MAX_STRLEN];
1106
1107#define RETURN(A) {\
1108 if (regex) {\
1109 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1110 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1111 longjmp(jmp,0);\
1112 }\
1113 }\
1114 if ( collect_stats ) {\
1115 record_stats( A );\
1116 }\
1117 return A;\
1118}
1119
1120 setjmp(jmp);
1121
1122 error = 0;
1123
1124 // first_byte is index into event buffer
1125 if ( first_byte != 0
1126 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1127
1128 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1129 first_byte += sizeof(struct inotify_event) + ret->len;
1130
1131 // if the pointer to the next event exactly hits end of bytes read,
1132 // that's good. next time we're called, we'll read.
1133 if ( first_byte == bytes ) {
1134 first_byte = 0;
1135 }
1136 else if ( first_byte > bytes ) {
1137 // oh... no. this can't be happening. An incomplete event.
1138 // Copy what we currently have into first element, call self to
1139 // read remainder.
1140 // oh, and they BETTER NOT overlap.
1141 // Boy I hope this code works.
1142 // But I think this can never happen due to how inotify is written.
1143 niceassert( (long)((char *)&event[0] +
1144 sizeof(struct inotify_event) +
1145 event[0].len) <= (long)ret,
1146 "extremely unlucky user, death imminent" );
1147 // how much of the event do we have?
1148 bytes = (char *)&event[0] + bytes - (char *)ret;
1149 memcpy( &event[0], ret, bytes );
1150 return inotifytools_next_events( timeout, num_events );
1151 }
1152 RETURN(ret);
1153
1154 }
1155
1156 else if ( first_byte == 0 ) {
1157 bytes = 0;
1158 }
1159
1160
1161 static ssize_t this_bytes;
1162 static unsigned int bytes_to_read;
1163 static int rc;
1164 static fd_set read_fds;
1165
1166 static struct timeval read_timeout;
1167 read_timeout.tv_sec = timeout;
1168 read_timeout.tv_usec = 0;
1169 static struct timeval * read_timeout_ptr;
1170 read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
1171
1172 FD_ZERO(&read_fds);
1173 FD_SET(inotify_fd, &read_fds);
1174 rc = select(inotify_fd + 1, &read_fds,
1175 NULL, NULL, read_timeout_ptr);
1176 if ( rc < 0 ) {
1177 // error
1178 error = errno;
1179 return NULL;
1180 }
1181 else if ( rc == 0 ) {
1182 // timeout
1183 return NULL;
1184 }
1185
1186 // wait until we have enough bytes to read
1187 do {
1188 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1189 } while ( !rc &&
1190 bytes_to_read < sizeof(struct inotify_event)*num_events );
1191
1192 if ( rc == -1 ) {
1193 error = errno;
1194 return NULL;
1195 }
1196
1197 this_bytes = read(inotify_fd, &event[0] + bytes,
1198 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1199 if ( this_bytes < 0 ) {
1200 error = errno;
1201 return NULL;
1202 }
1203 if ( this_bytes == 0 ) {
1204 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1205 "events occurred at once.\n");
1206 return NULL;
1207 }
1208 bytes += this_bytes;
1209
1210 ret = &event[0];
1211 first_byte = sizeof(struct inotify_event) + ret->len;
1212 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1213 "almost certainly screw up." );
1214 if ( first_byte == bytes ) {
1215 first_byte = 0;
1216 }
1217
1218 RETURN(ret);
1219
1220#undef RETURN
1221}
1222
1248int inotifytools_watch_recursively( char const * path, int events ) {
1249 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1250}
1251
1284int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1285 char const ** exclude_list ) {
1286 niceassert( init, "inotifytools_initialize not called yet" );
1287
1288 DIR * dir;
1289 char * my_path;
1290 error = 0;
1291 dir = opendir( path );
1292 if ( !dir ) {
1293 // If not a directory, don't need to do anything special
1294 if ( errno == ENOTDIR ) {
1295 return inotifytools_watch_file( path, events );
1296 }
1297 else {
1298 error = errno;
1299 return 0;
1300 }
1301 }
1302
1303 if ( path[strlen(path)-1] != '/' ) {
1304 nasprintf( &my_path, "%s/", path );
1305 }
1306 else {
1307 my_path = (char *)path;
1308 }
1309
1310 static struct dirent * ent;
1311 char * next_file;
1312 static struct stat64 my_stat;
1313 ent = readdir( dir );
1314 // Watch each directory within this directory
1315 while ( ent ) {
1316 if ( (0 != strcmp( ent->d_name, "." )) &&
1317 (0 != strcmp( ent->d_name, ".." )) ) {
1318 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1319 if ( -1 == lstat64( next_file, &my_stat ) ) {
1320 error = errno;
1321 free( next_file );
1322 if ( errno != EACCES ) {
1323 error = errno;
1324 if ( my_path != path ) free( my_path );
1325 closedir( dir );
1326 return 0;
1327 }
1328 }
1329 else if ( S_ISDIR( my_stat.st_mode ) &&
1330 !S_ISLNK( my_stat.st_mode )) {
1331 free( next_file );
1332 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1333 static unsigned int no_watch;
1334 static char const ** exclude_entry;
1335
1336 no_watch = 0;
1337 for (exclude_entry = exclude_list;
1338 exclude_entry && *exclude_entry && !no_watch;
1339 ++exclude_entry) {
1340 static int exclude_length;
1341
1342 exclude_length = strlen(*exclude_entry);
1343 if ((*exclude_entry)[exclude_length-1] == '/') {
1344 --exclude_length;
1345 }
1346 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1347 !strncmp(*exclude_entry, next_file, exclude_length)) {
1348 // directory found in exclude list
1349 no_watch = 1;
1350 }
1351 }
1352 if (!no_watch) {
1353 static int status;
1355 next_file,
1356 events,
1357 exclude_list );
1358 // For some errors, we will continue.
1359 if ( !status && (EACCES != error) && (ENOENT != error) &&
1360 (ELOOP != error) ) {
1361 free( next_file );
1362 if ( my_path != path ) free( my_path );
1363 closedir( dir );
1364 return 0;
1365 }
1366 } // if !no_watch
1367 free( next_file );
1368 } // if isdir and not islnk
1369 else {
1370 free( next_file );
1371 }
1372 }
1373 ent = readdir( dir );
1374 error = 0;
1375 }
1376
1377 closedir( dir );
1378
1379 int ret = inotifytools_watch_file( my_path, events );
1380 if ( my_path != path ) free( my_path );
1381 return ret;
1382}
1383
1387void record_stats( struct inotify_event const * event ) {
1388 if (!event) return;
1389 watch *w = watch_from_wd(event->wd);
1390 if (!w) return;
1391 if ( IN_ACCESS & event->mask ) {
1392 ++w->hit_access;
1393 ++num_access;
1394 }
1395 if ( IN_MODIFY & event->mask ) {
1396 ++w->hit_modify;
1397 ++num_modify;
1398 }
1399 if ( IN_ATTRIB & event->mask ) {
1400 ++w->hit_attrib;
1401 ++num_attrib;
1402 }
1403 if ( IN_CLOSE_WRITE & event->mask ) {
1404 ++w->hit_close_write;
1405 ++num_close_write;
1406 }
1407 if ( IN_CLOSE_NOWRITE & event->mask ) {
1408 ++w->hit_close_nowrite;
1409 ++num_close_nowrite;
1410 }
1411 if ( IN_OPEN & event->mask ) {
1412 ++w->hit_open;
1413 ++num_open;
1414 }
1415 if ( IN_MOVED_FROM & event->mask ) {
1416 ++w->hit_moved_from;
1417 ++num_moved_from;
1418 }
1419 if ( IN_MOVED_TO & event->mask ) {
1420 ++w->hit_moved_to;
1421 ++num_moved_to;
1422 }
1423 if ( IN_CREATE & event->mask ) {
1424 ++w->hit_create;
1425 ++num_create;
1426 }
1427 if ( IN_DELETE & event->mask ) {
1428 ++w->hit_delete;
1429 ++num_delete;
1430 }
1431 if ( IN_DELETE_SELF & event->mask ) {
1432 ++w->hit_delete_self;
1433 ++num_delete_self;
1434 }
1435 if ( IN_UNMOUNT & event->mask ) {
1436 ++w->hit_unmount;
1437 ++num_unmount;
1438 }
1439 if ( IN_MOVE_SELF & event->mask ) {
1440 ++w->hit_move_self;
1441 ++num_move_self;
1442 }
1443
1444 ++w->hit_total;
1445 ++num_total;
1446
1447}
1448
1449int *stat_ptr(watch *w, int event)
1450{
1451 if ( IN_ACCESS == event )
1452 return &w->hit_access;
1453 if ( IN_MODIFY == event )
1454 return &w->hit_modify;
1455 if ( IN_ATTRIB == event )
1456 return &w->hit_attrib;
1457 if ( IN_CLOSE_WRITE == event )
1458 return &w->hit_close_write;
1459 if ( IN_CLOSE_NOWRITE == event )
1460 return &w->hit_close_nowrite;
1461 if ( IN_OPEN == event )
1462 return &w->hit_open;
1463 if ( IN_MOVED_FROM == event )
1464 return &w->hit_moved_from;
1465 if ( IN_MOVED_TO == event )
1466 return &w->hit_moved_to;
1467 if ( IN_CREATE == event )
1468 return &w->hit_create;
1469 if ( IN_DELETE == event )
1470 return &w->hit_delete;
1471 if ( IN_DELETE_SELF == event )
1472 return &w->hit_delete_self;
1473 if ( IN_UNMOUNT == event )
1474 return &w->hit_unmount;
1475 if ( IN_MOVE_SELF == event )
1476 return &w->hit_move_self;
1477 if ( 0 == event )
1478 return &w->hit_total;
1479 return 0;
1480}
1481
1497int inotifytools_get_stat_by_wd( int wd, int event ) {
1498 if (!collect_stats) return -1;
1499
1500 watch *w = watch_from_wd(wd);
1501 if (!w) return -1;
1502 int *i = stat_ptr(w, event);
1503 if (!i) return -1;
1504 return *i;
1505}
1506
1521 if (!collect_stats) return -1;
1522 if ( IN_ACCESS == event )
1523 return num_access;
1524 if ( IN_MODIFY == event )
1525 return num_modify;
1526 if ( IN_ATTRIB == event )
1527 return num_attrib;
1528 if ( IN_CLOSE_WRITE == event )
1529 return num_close_write;
1530 if ( IN_CLOSE_NOWRITE == event )
1531 return num_close_nowrite;
1532 if ( IN_OPEN == event )
1533 return num_open;
1534 if ( IN_MOVED_FROM == event )
1535 return num_moved_from;
1536 if ( IN_MOVED_TO == event )
1537 return num_moved_to;
1538 if ( IN_CREATE == event )
1539 return num_create;
1540 if ( IN_DELETE == event )
1541 return num_delete;
1542 if ( IN_DELETE_SELF == event )
1543 return num_delete_self;
1544 if ( IN_UNMOUNT == event )
1545 return num_unmount;
1546 if ( IN_MOVE_SELF == event )
1547 return num_move_self;
1548
1549 if ( 0 == event )
1550 return num_total;
1551
1552 return -1;
1553}
1554
1574int inotifytools_get_stat_by_filename( char const * filename,
1575 int event ) {
1577 filename ), event );
1578}
1579
1591 return error;
1592}
1593
1597int isdir( char const * path ) {
1598 static struct stat64 my_stat;
1599
1600 if ( -1 == lstat64( path, &my_stat ) ) {
1601 if (errno == ENOENT) return 0;
1602 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1603 return 0;
1604 }
1605
1606 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1607}
1608
1609
1617 int ret = 0;
1618 rbwalk(tree_filename, get_num, (void*)&ret);
1619 return ret;
1620}
1621
1662int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1663 return inotifytools_fprintf( stdout, event, fmt );
1664}
1665
1707int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1708 static char out[MAX_STRLEN+1];
1709 static int ret;
1710 ret = inotifytools_sprintf( out, event, fmt );
1711 if ( -1 != ret ) fprintf( file, "%s", out );
1712 return ret;
1713}
1714
1765int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1766 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1767}
1768
1769
1816int inotifytools_snprintf( char * out, int size,
1817 struct inotify_event* event, char* fmt ) {
1818 static char * filename, * eventname, * eventstr;
1819 static unsigned int i, ind;
1820 static char ch1;
1821 static char timestr[MAX_STRLEN];
1822 static time_t now;
1823
1824
1825 if ( event->len > 0 ) {
1826 eventname = event->name;
1827 }
1828 else {
1829 eventname = NULL;
1830 }
1831
1832
1833 filename = inotifytools_filename_from_wd( event->wd );
1834
1835 if ( !fmt || 0 == strlen(fmt) ) {
1836 error = EINVAL;
1837 return -1;
1838 }
1839 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1840 error = EMSGSIZE;
1841 return -1;
1842 }
1843
1844 ind = 0;
1845 for ( i = 0; i < strlen(fmt) &&
1846 (int)ind < size - 1; ++i ) {
1847 if ( fmt[i] != '%' ) {
1848 out[ind++] = fmt[i];
1849 continue;
1850 }
1851
1852 if ( i == strlen(fmt) - 1 ) {
1853 // last character is %, invalid
1854 error = EINVAL;
1855 return ind;
1856 }
1857
1858 ch1 = fmt[i+1];
1859
1860 if ( ch1 == '%' ) {
1861 out[ind++] = '%';
1862 ++i;
1863 continue;
1864 }
1865
1866 if ( ch1 == 'w' ) {
1867 if ( filename ) {
1868 strncpy( &out[ind], filename, size - ind );
1869 ind += strlen(filename);
1870 }
1871 ++i;
1872 continue;
1873 }
1874
1875 if ( ch1 == 'f' ) {
1876 if ( eventname ) {
1877 strncpy( &out[ind], eventname, size - ind );
1878 ind += strlen(eventname);
1879 }
1880 ++i;
1881 continue;
1882 }
1883
1884 if ( ch1 == 'e' ) {
1885 eventstr = inotifytools_event_to_str( event->mask );
1886 strncpy( &out[ind], eventstr, size - ind );
1887 ind += strlen(eventstr);
1888 ++i;
1889 continue;
1890 }
1891
1892 if ( ch1 == 'T' ) {
1893
1894 if ( timefmt ) {
1895
1896 now = time(0);
1897 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1898 localtime( &now ) ) ) {
1899
1900 // time format probably invalid
1901 error = EINVAL;
1902 return ind;
1903 }
1904 }
1905 else {
1906 timestr[0] = 0;
1907 }
1908
1909 strncpy( &out[ind], timestr, size - ind );
1910 ind += strlen(timestr);
1911 ++i;
1912 continue;
1913 }
1914
1915 // Check if next char in fmt is e
1916 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1917 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1918 strncpy( &out[ind], eventstr, size - ind );
1919 ind += strlen(eventstr);
1920 i += 2;
1921 continue;
1922 }
1923
1924 // OK, this wasn't a special format character, just output it as normal
1925 if ( ind < MAX_STRLEN ) out[ind++] = '%';
1926 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1927 ++i;
1928 }
1929 out[ind] = 0;
1930
1931 return ind - 1;
1932}
1933
1944 timefmt = fmt;
1945}
1946
1956 int ret;
1957 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1958 return ret;
1959}
1960
1971 int ret;
1972 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1973 return ret;
1974}
1975
1986 int ret;
1987 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
1988 return ret;
1989}
1990
2002int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2003 if (!pattern) {
2004 if (regex) {
2005 regfree(regex);
2006 free(regex);
2007 regex = 0;
2008 }
2009 return 1;
2010 }
2011
2012 if (regex) { regfree(regex); }
2013 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2014
2015 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2016 if (0 == ret) return 1;
2017
2018 regfree(regex);
2019 free(regex);
2020 regex = 0;
2021 error = EINVAL;
2022 return 0;
2023}
2024
2025int event_compare(const void *p1, const void *p2, const void *config)
2026{
2027 if (!p1 || !p2) return p1 - p2;
2028 char asc = 1;
2029 int sort_event = (int)config;
2030 if (sort_event == -1) {
2031 sort_event = 0;
2032 asc = 0;
2033 } else if (sort_event < 0) {
2034 sort_event = -sort_event;
2035 asc = 0;
2036 }
2037 int *i1 = stat_ptr((watch*)p1, sort_event);
2038 int *i2 = stat_ptr((watch*)p2, sort_event);
2039 if (0 == *i1 - *i2) {
2040 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2041 }
2042 if (asc)
2043 return *i1 - *i2;
2044 else
2045 return *i2 - *i1;
2046}
2047
2048struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2049{
2050 struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
2051 RBLIST *all = rbopenlist(tree_wd);
2052 void const *p = rbreadlist(all);
2053 while (p) {
2054 void const *r = rbsearch(p, ret);
2055 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2056 p = rbreadlist(all);
2057 }
2058 rbcloselist(all);
2059 return ret;
2060}
inotifytools library public interface.
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
int inotifytools_get_max_queued_events()
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:932
int inotifytools_error()
struct inotify_event * inotifytools_next_events(int timeout, int num_events)
int inotifytools_initialize()
Definition: inotifytools.c:281
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:763
int inotifytools_get_stat_by_wd(int wd, int event)
int inotifytools_watch_recursively(char const *path, int events)
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:786
struct inotify_event * inotifytools_next_event(int timeout)
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
int inotifytools_get_stat_by_filename(char const *filename, int event)
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:829
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:909
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:656
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:471
void inotifytools_cleanup()
Definition: inotifytools.c:326
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:977
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
int inotifytools_get_max_user_instances()
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
int inotifytools_get_num_watches()
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:628
int inotifytools_printf(struct inotify_event *event, char *fmt)
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:541
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:859
void inotifytools_initialize_stats()
Definition: inotifytools.c:418
int inotifytools_get_max_user_watches()
int inotifytools_get_stat_total(int event)
void inotifytools_set_printf_timefmt(char *fmt)
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:807
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:955