libinotifytools: inotifytools.c Source File

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 <stdint.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <sys/select.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <time.h>
32 #include <regex.h>
33 #include <setjmp.h>
34
35 #include "inotifytools/inotify.h"
36
123 #define MAX_EVENTS 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 struct replace_filename_data {
376 char const *old_name;
377 char const *new_name;
378 size_t old_len;
379 };
380
384 void replace_filename(const void *nodep, const VISIT which, const int depth,
385 const struct replace_filename_data *data) {
386 if (which != endorder && which != leaf) return;
387 watch *w = (watch*)nodep;
388 char *name;
389 if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
390 nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
391 if (!strcmp( w->filename, data->new_name )) {
392 free(name);
393 } else {
394 rbdelete(w, tree_filename);
395 free( w->filename );
396 w->filename = name;
397 rbsearch(w, tree_filename);
398 }
399 }
400 }
401
405 void get_num(const void *nodep,
406 const VISIT which,
407 const int depth, void *arg) {
408 if (which != endorder && which != leaf) return;
409 ++(*((int*)arg));
410 }
411
412
425 void inotifytools_initialize_stats() {
426 niceassert( init, "inotifytools_initialize not called yet" );
427
428 // if already collecting stats, reset stats
429 if (collect_stats) {
430 rbwalk(tree_wd, empty_stats, 0);
431 }
432
433 num_access = 0;
434 num_modify = 0;
435 num_attrib = 0;
436 num_close_nowrite = 0;
437 num_close_write = 0;
438 num_open = 0;
439 num_move_self = 0;
440 num_moved_from = 0;
441 num_moved_to = 0;
442 num_create = 0;
443 num_delete = 0;
444 num_delete_self = 0;
445 num_unmount = 0;
446 num_total = 0;
447
448 collect_stats = 1;
449 }
450
478 int inotifytools_str_to_event_sep(char const * event, char sep) {
479 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
480 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
481 return -1;
482 }
483
484 int ret, ret1, len;
485 char * event1, * event2;
486 static const size_t eventstr_size = 4096;
487 char eventstr[eventstr_size];
488 ret = 0;
489
490 if ( !event || !event[0] ) return 0;
491
492 event1 = (char *)event;
493 event2 = strchr( event1, sep );
494 while ( event1 && event1[0] ) {
495 if ( event2 ) {
496 len = event2 - event1;
497 niceassert(len < eventstr_size,
498 "malformed event string (very long)");
499 }
500 else {
501 len = strlen(event1);
502 }
503 if (len > eventstr_size - 1)
504 len = eventstr_size - 1;
505
506 if (event2 || len == eventstr_size - 1) {
507 strncpy(eventstr, event1, len);
508 } else {
509 strcpy(eventstr, event1);
510 }
511
512 eventstr[len] = 0;
513
514 ret1 = onestr_to_event( eventstr );
515 if ( 0 == ret1 || -1 == ret1 ) {
516 ret = ret1;
517 break;
518 }
519 ret |= ret1;
520
521 event1 = event2;
522 if ( event1 && event1[0] ) {
523 // jump over 'sep' character
524 ++event1;
525 // if last character was 'sep'...
526 if ( !event1[0] ) return 0;
527 event2 = strchr( event1, sep );
528 }
529 }
530
531 return ret;
532 }
533
557 int inotifytools_str_to_event(char const * event) {
558 return inotifytools_str_to_event_sep( event, ',' );
559 }
560
572 int onestr_to_event(char const * event)
573 {
574 static int ret;
575 ret = -1;
576
577 if ( !event || !event[0] )
578 ret = 0;
579 else if ( 0 == strcasecmp(event, "ACCESS") )
580 ret = IN_ACCESS;
581 else if ( 0 == strcasecmp(event, "MODIFY") )
582 ret = IN_MODIFY;
583 else if ( 0 == strcasecmp(event, "ATTRIB") )
584 ret = IN_ATTRIB;
585 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
586 ret = IN_CLOSE_WRITE;
587 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
588 ret = IN_CLOSE_NOWRITE;
589 else if ( 0 == strcasecmp(event, "OPEN") )
590 ret = IN_OPEN;
591 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
592 ret = IN_MOVED_FROM;
593 else if ( 0 == strcasecmp(event, "MOVED_TO") )
594 ret = IN_MOVED_TO;
595 else if ( 0 == strcasecmp(event, "CREATE") )
596 ret = IN_CREATE;
597 else if ( 0 == strcasecmp(event, "DELETE") )
598 ret = IN_DELETE;
599 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
600 ret = IN_DELETE_SELF;
601 else if ( 0 == strcasecmp(event, "UNMOUNT") )
602 ret = IN_UNMOUNT;
603 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
604 ret = IN_Q_OVERFLOW;
605 else if ( 0 == strcasecmp(event, "IGNORED") )
606 ret = IN_IGNORED;
607 else if ( 0 == strcasecmp(event, "CLOSE") )
608 ret = IN_CLOSE;
609 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
610 ret = IN_MOVE_SELF;
611 else if ( 0 == strcasecmp(event, "MOVE") )
612 ret = IN_MOVE;
613 else if ( 0 == strcasecmp(event, "ISDIR") )
614 ret = IN_ISDIR;
615 else if ( 0 == strcasecmp(event, "ONESHOT") )
616 ret = IN_ONESHOT;
617 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
618 ret = IN_ALL_EVENTS;
619
620 return ret;
621 }
622
644 char * inotifytools_event_to_str(int events) {
645 return inotifytools_event_to_str_sep(events, ',');
646 }
647
672 char * inotifytools_event_to_str_sep(int events, char sep)
673 {
674 static char ret[1024];
675 ret[0] = '\0';
676 ret[1] = '\0';
677
678 if ( IN_ACCESS & events ) {
679 strcat( ret, chrtostr(sep) );
680 strcat( ret, "ACCESS" );
681 }
682 if ( IN_MODIFY & events ) {
683 strcat( ret, chrtostr(sep) );
684 strcat( ret, "MODIFY" );
685 }
686 if ( IN_ATTRIB & events ) {
687 strcat( ret, chrtostr(sep) );
688 strcat( ret, "ATTRIB" );
689 }
690 if ( IN_CLOSE_WRITE & events ) {
691 strcat( ret, chrtostr(sep) );
692 strcat( ret, "CLOSE_WRITE" );
693 }
694 if ( IN_CLOSE_NOWRITE & events ) {
695 strcat( ret, chrtostr(sep) );
696 strcat( ret, "CLOSE_NOWRITE" );
697 }
698 if ( IN_OPEN & events ) {
699 strcat( ret, chrtostr(sep) );
700 strcat( ret, "OPEN" );
701 }
702 if ( IN_MOVED_FROM & events ) {
703 strcat( ret, chrtostr(sep) );
704 strcat( ret, "MOVED_FROM" );
705 }
706 if ( IN_MOVED_TO & events ) {
707 strcat( ret, chrtostr(sep) );
708 strcat( ret, "MOVED_TO" );
709 }
710 if ( IN_CREATE & events ) {
711 strcat( ret, chrtostr(sep) );
712 strcat( ret, "CREATE" );
713 }
714 if ( IN_DELETE & events ) {
715 strcat( ret, chrtostr(sep) );
716 strcat( ret, "DELETE" );
717 }
718 if ( IN_DELETE_SELF & events ) {
719 strcat( ret, chrtostr(sep) );
720 strcat( ret, "DELETE_SELF" );
721 }
722 if ( IN_UNMOUNT & events ) {
723 strcat( ret, chrtostr(sep) );
724 strcat( ret, "UNMOUNT" );
725 }
726 if ( IN_Q_OVERFLOW & events ) {
727 strcat( ret, chrtostr(sep) );
728 strcat( ret, "Q_OVERFLOW" );
729 }
730 if ( IN_IGNORED & events ) {
731 strcat( ret, chrtostr(sep) );
732 strcat( ret, "IGNORED" );
733 }
734 if ( IN_CLOSE & events ) {
735 strcat( ret, chrtostr(sep) );
736 strcat( ret, "CLOSE" );
737 }
738 if ( IN_MOVE_SELF & events ) {
739 strcat( ret, chrtostr(sep) );
740 strcat( ret, "MOVE_SELF" );
741 }
742 if ( IN_ISDIR & events ) {
743 strcat( ret, chrtostr(sep) );
744 strcat( ret, "ISDIR" );
745 }
746 if ( IN_ONESHOT & events ) {
747 strcat( ret, chrtostr(sep) );
748 strcat( ret, "ONESHOT" );
749 }
750
751 // Maybe we didn't match any... ?
752 if (ret[0] == '\0') {
753 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
754 }
755
756 return &ret[1];
757 }
758
779 char * inotifytools_filename_from_wd( int wd ) {
780 niceassert( init, "inotifytools_initialize not called yet" );
781 watch *w = watch_from_wd(wd);
782 if (!w)
783 return NULL;
784
785 return w->filename;
786 }
787
802 int inotifytools_wd_from_filename( char const * filename ) {
803 niceassert( init, "inotifytools_initialize not called yet" );
804 watch *w = watch_from_filename(filename);
805 if (!w) return -1;
806 return w->wd;
807 }
808
823 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
824 niceassert( init, "inotifytools_initialize not called yet" );
825 watch *w = watch_from_wd(wd);
826 if (!w) return;
827 if (w->filename) free(w->filename);
828 w->filename = strdup(filename);
829 }
830
845 void inotifytools_set_filename_by_filename( char const * oldname,
846 char const * newname ) {
847 watch *w = watch_from_filename(oldname);
848 if (!w) return;
849 if (w->filename) free(w->filename);
850 w->filename = strdup(newname);
851 }
852
875 void inotifytools_replace_filename( char const * oldname,
876 char const * newname ) {
877 if ( !oldname || !newname ) return;
878 struct replace_filename_data data;
879 data.old_name = oldname;
880 data.new_name = newname;
881 data.old_len = strlen(oldname);
882 rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
883 }
884
888 int remove_inotify_watch(watch *w) {
889 error = 0;
890 int status = inotify_rm_watch( inotify_fd, w->wd );
891 if ( status < 0 ) {
892 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
893 strerror(status) );
894 error = status;
895 return 0;
896 }
897 return 1;
898 }
899
903 watch *create_watch(int wd, char *filename) {
904 if ( wd <= 0 || !filename) return 0;
905
906 watch *w = (watch*)calloc(1, sizeof(watch));
907 w->wd = wd;
908 w->filename = strdup(filename);
909 rbsearch(w, tree_wd);
910 rbsearch(w, tree_filename);
911 return NULL;
912 }
913
926 int inotifytools_remove_watch_by_wd( int wd ) {
927 niceassert( init, "inotifytools_initialize not called yet" );
928 watch *w = watch_from_wd(wd);
929 if (!w) return 1;
930
931 if (!remove_inotify_watch(w)) return 0;
932 rbdelete(w, tree_wd);
933 rbdelete(w, tree_filename);
934 destroy_watch(w);
935 return 1;
936 }
937
949 int inotifytools_remove_watch_by_filename( char const * filename ) {
950 niceassert( init, "inotifytools_initialize not called yet" );
951 watch *w = watch_from_filename(filename);
952 if (!w) return 1;
953
954 if (!remove_inotify_watch(w)) return 0;
955 rbdelete(w, tree_wd);
956 rbdelete(w, tree_filename);
957 destroy_watch(w);
958 return 1;
959 }
960
972 int inotifytools_watch_file( char const * filename, int events ) {
973 static char const * filenames[2];
974 filenames[0] = filename;
975 filenames[1] = NULL;
976 return inotifytools_watch_files( filenames, events );
977 }
978
994 int inotifytools_watch_files( char const * filenames[], int events ) {
995 niceassert( init, "inotifytools_initialize not called yet" );
996 error = 0;
997
998 static int i;
999 for ( i = 0; filenames[i]; ++i ) {
1000 static int wd;
1001 wd = inotify_add_watch( inotify_fd, filenames[i], events );
1002 if ( wd < 0 ) {
1003 if ( wd == -1 ) {
1004 error = errno;
1005 return 0;
1006 } // if ( wd == -1 )
1007 else {
1008 fprintf( stderr, "Failed to watch %s: returned wd was %d "
1009 "(expected -1 or >0 )", filenames[i], wd );
1010 // no appropriate value for error
1011 return 0;
1012 } // else
1013 } // if ( wd < 0 )
1014
1015 char *filename;
1016 // Always end filename with / if it is a directory
1017 if ( !isdir(filenames[i])
1018 || filenames[i][strlen(filenames[i])-1] == '/') {
1019 filename = strdup(filenames[i]);
1020 }
1021 else {
1022 nasprintf( &filename, "%s/", filenames[i] );
1023 }
1024 create_watch(wd, filename);
1025 free(filename);
1026 } // for
1027
1028 return 1;
1029 }
1030
1057 struct inotify_event * inotifytools_next_event( long int timeout ) {
1058 return inotifytools_next_events( timeout, 1 );
1059 }
1060
1061
1110 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1111 niceassert( init, "inotifytools_initialize not called yet" );
1112 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1113
1114 if ( num_events < 1 ) return NULL;
1115
1116 static struct inotify_event event[MAX_EVENTS];
1117 static struct inotify_event * ret;
1118 static int first_byte = 0;
1119 static ssize_t bytes;
1120 static jmp_buf jmp;
1121 static struct nstring match_name;
1122 static char match_name_string[MAX_STRLEN+1];
1123
1124 #define RETURN(A) {\
1125 if (regex) {\
1126 inotifytools_snprintf(&match_name, MAX_STRLEN, A, "%w%f");\
1127 memcpy(&match_name_string, &match_name.buf, match_name.len);\
1128 match_name_string[match_name.len] = '\0';\
1129 if (0 == regexec(regex, match_name_string, 0, 0, 0)) {\
1130 if (!invert_regexp)\
1131 longjmp(jmp,0);\
1132 } else {\
1133 if (invert_regexp)\
1134 longjmp(jmp,0);\
1135 }\
1136 }\
1137 if ( collect_stats ) {\
1138 record_stats( A );\
1139 }\
1140 return A;\
1141 }
1142
1143 setjmp(jmp);
1144
1145 error = 0;
1146
1147 // first_byte is index into event buffer
1148 if ( first_byte != 0
1149 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1150
1151 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1152 first_byte += sizeof(struct inotify_event) + ret->len;
1153
1154 // if the pointer to the next event exactly hits end of bytes read,
1155 // that's good. next time we're called, we'll read.
1156 if ( first_byte == bytes ) {
1157 first_byte = 0;
1158 }
1159 else if ( first_byte > bytes ) {
1160 // oh... no. this can't be happening. An incomplete event.
1161 // Copy what we currently have into first element, call self to
1162 // read remainder.
1163 // oh, and they BETTER NOT overlap.
1164 // Boy I hope this code works.
1165 // But I think this can never happen due to how inotify is written.
1166 niceassert( (long)((char *)&event[0] +
1167 sizeof(struct inotify_event) +
1168 event[0].len) <= (long)ret,
1169 "extremely unlucky user, death imminent" );
1170 // how much of the event do we have?
1171 bytes = (char *)&event[0] + bytes - (char *)ret;
1172 memcpy( &event[0], ret, bytes );
1173 return inotifytools_next_events( timeout, num_events );
1174 }
1175 RETURN(ret);
1176
1177 }
1178
1179 else if ( first_byte == 0 ) {
1180 bytes = 0;
1181 }
1182
1183
1184 static ssize_t this_bytes;
1185 static unsigned int bytes_to_read;
1186 static int rc;
1187 static fd_set read_fds;
1188
1189 static struct timeval read_timeout;
1190 read_timeout.tv_sec = timeout;
1191 read_timeout.tv_usec = 0;
1192 static struct timeval * read_timeout_ptr;
1193 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1194
1195 FD_ZERO(&read_fds);
1196 FD_SET(inotify_fd, &read_fds);
1197 rc = select(inotify_fd + 1, &read_fds,
1198 NULL, NULL, read_timeout_ptr);
1199 if ( rc < 0 ) {
1200 // error
1201 error = errno;
1202 return NULL;
1203 }
1204 else if ( rc == 0 ) {
1205 // timeout
1206 return NULL;
1207 }
1208
1209 // wait until we have enough bytes to read
1210 do {
1211 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1212 } while ( !rc &&
1213 bytes_to_read < sizeof(struct inotify_event)*num_events );
1214
1215 if ( rc == -1 ) {
1216 error = errno;
1217 return NULL;
1218 }
1219
1220 this_bytes = read(inotify_fd, &event[0] + bytes,
1221 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1222 if ( this_bytes < 0 ) {
1223 error = errno;
1224 return NULL;
1225 }
1226 if ( this_bytes == 0 ) {
1227 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1228 "events occurred at once.\n");
1229 return NULL;
1230 }
1231 bytes += this_bytes;
1232
1233 ret = &event[0];
1234 first_byte = sizeof(struct inotify_event) + ret->len;
1235 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1236 "almost certainly screw up." );
1237 if ( first_byte == bytes ) {
1238 first_byte = 0;
1239 }
1240
1241 RETURN(ret);
1242
1243 #undef RETURN
1244 }
1245
1271 int inotifytools_watch_recursively( char const * path, int events ) {
1272 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1273 }
1274
1307 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1308 char const ** exclude_list ) {
1309 niceassert( init, "inotifytools_initialize not called yet" );
1310
1311 DIR * dir;
1312 char * my_path;
1313 error = 0;
1314 dir = opendir( path );
1315 if ( !dir ) {
1316 // If not a directory, don't need to do anything special
1317 if ( errno == ENOTDIR ) {
1318 return inotifytools_watch_file( path, events );
1319 }
1320 else {
1321 error = errno;
1322 return 0;
1323 }
1324 }
1325
1326 if ( path[strlen(path)-1] != '/' ) {
1327 nasprintf( &my_path, "%s/", path );
1328 }
1329 else {
1330 my_path = (char *)path;
1331 }
1332
1333 static struct dirent * ent;
1334 char * next_file;
1335 static struct stat64 my_stat;
1336 ent = readdir( dir );
1337 // Watch each directory within this directory
1338 while ( ent ) {
1339 if ( (0 != strcmp( ent->d_name, "." )) &&
1340 (0 != strcmp( ent->d_name, ".." )) ) {
1341 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1342 if ( -1 == lstat64( next_file, &my_stat ) ) {
1343 error = errno;
1344 free( next_file );
1345 if ( errno != EACCES ) {
1346 error = errno;
1347 if ( my_path != path ) free( my_path );
1348 closedir( dir );
1349 return 0;
1350 }
1351 }
1352 else if ( S_ISDIR( my_stat.st_mode ) &&
1353 !S_ISLNK( my_stat.st_mode )) {
1354 free( next_file );
1355 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1356 static unsigned int no_watch;
1357 static char const ** exclude_entry;
1358
1359 no_watch = 0;
1360 for (exclude_entry = exclude_list;
1361 exclude_entry && *exclude_entry && !no_watch;
1362 ++exclude_entry) {
1363 static int exclude_length;
1364
1365 exclude_length = strlen(*exclude_entry);
1366 if ((*exclude_entry)[exclude_length-1] == '/') {
1367 --exclude_length;
1368 }
1369 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1370 !strncmp(*exclude_entry, next_file, exclude_length)) {
1371 // directory found in exclude list
1372 no_watch = 1;
1373 }
1374 }
1375 if (!no_watch) {
1376 static int status;
1377 status = inotifytools_watch_recursively_with_exclude(
1378 next_file,
1379 events,
1380 exclude_list );
1381 // For some errors, we will continue.
1382 if ( !status && (EACCES != error) && (ENOENT != error) &&
1383 (ELOOP != error) ) {
1384 free( next_file );
1385 if ( my_path != path ) free( my_path );
1386 closedir( dir );
1387 return 0;
1388 }
1389 } // if !no_watch
1390 free( next_file );
1391 } // if isdir and not islnk
1392 else {
1393 free( next_file );
1394 }
1395 }
1396 ent = readdir( dir );
1397 error = 0;
1398 }
1399
1400 closedir( dir );
1401
1402 int ret = inotifytools_watch_file( my_path, events );
1403 if ( my_path != path ) free( my_path );
1404 return ret;
1405 }
1406
1410 void record_stats( struct inotify_event const * event ) {
1411 if (!event) return;
1412 watch *w = watch_from_wd(event->wd);
1413 if (!w) return;
1414 if ( IN_ACCESS & event->mask ) {
1415 ++w->hit_access;
1416 ++num_access;
1417 }
1418 if ( IN_MODIFY & event->mask ) {
1419 ++w->hit_modify;
1420 ++num_modify;
1421 }
1422 if ( IN_ATTRIB & event->mask ) {
1423 ++w->hit_attrib;
1424 ++num_attrib;
1425 }
1426 if ( IN_CLOSE_WRITE & event->mask ) {
1427 ++w->hit_close_write;
1428 ++num_close_write;
1429 }
1430 if ( IN_CLOSE_NOWRITE & event->mask ) {
1431 ++w->hit_close_nowrite;
1432 ++num_close_nowrite;
1433 }
1434 if ( IN_OPEN & event->mask ) {
1435 ++w->hit_open;
1436 ++num_open;
1437 }
1438 if ( IN_MOVED_FROM & event->mask ) {
1439 ++w->hit_moved_from;
1440 ++num_moved_from;
1441 }
1442 if ( IN_MOVED_TO & event->mask ) {
1443 ++w->hit_moved_to;
1444 ++num_moved_to;
1445 }
1446 if ( IN_CREATE & event->mask ) {
1447 ++w->hit_create;
1448 ++num_create;
1449 }
1450 if ( IN_DELETE & event->mask ) {
1451 ++w->hit_delete;
1452 ++num_delete;
1453 }
1454 if ( IN_DELETE_SELF & event->mask ) {
1455 ++w->hit_delete_self;
1456 ++num_delete_self;
1457 }
1458 if ( IN_UNMOUNT & event->mask ) {
1459 ++w->hit_unmount;
1460 ++num_unmount;
1461 }
1462 if ( IN_MOVE_SELF & event->mask ) {
1463 ++w->hit_move_self;
1464 ++num_move_self;
1465 }
1466
1467 ++w->hit_total;
1468 ++num_total;
1469
1470 }
1471
1472 unsigned int *stat_ptr(watch *w, int event)
1473 {
1474 if ( IN_ACCESS == event )
1475 return &w->hit_access;
1476 if ( IN_MODIFY == event )
1477 return &w->hit_modify;
1478 if ( IN_ATTRIB == event )
1479 return &w->hit_attrib;
1480 if ( IN_CLOSE_WRITE == event )
1481 return &w->hit_close_write;
1482 if ( IN_CLOSE_NOWRITE == event )
1483 return &w->hit_close_nowrite;
1484 if ( IN_OPEN == event )
1485 return &w->hit_open;
1486 if ( IN_MOVED_FROM == event )
1487 return &w->hit_moved_from;
1488 if ( IN_MOVED_TO == event )
1489 return &w->hit_moved_to;
1490 if ( IN_CREATE == event )
1491 return &w->hit_create;
1492 if ( IN_DELETE == event )
1493 return &w->hit_delete;
1494 if ( IN_DELETE_SELF == event )
1495 return &w->hit_delete_self;
1496 if ( IN_UNMOUNT == event )
1497 return &w->hit_unmount;
1498 if ( IN_MOVE_SELF == event )
1499 return &w->hit_move_self;
1500 if ( 0 == event )
1501 return &w->hit_total;
1502 return 0;
1503 }
1504
1520 int inotifytools_get_stat_by_wd( int wd, int event ) {
1521 if (!collect_stats) return -1;
1522
1523 watch *w = watch_from_wd(wd);
1524 if (!w) return -1;
1525 unsigned int *i = stat_ptr(w, event);
1526 if (!i) return -1;
1527 return *i;
1528 }
1529
1543 int inotifytools_get_stat_total( int event ) {
1544 if (!collect_stats) return -1;
1545 if ( IN_ACCESS == event )
1546 return num_access;
1547 if ( IN_MODIFY == event )
1548 return num_modify;
1549 if ( IN_ATTRIB == event )
1550 return num_attrib;
1551 if ( IN_CLOSE_WRITE == event )
1552 return num_close_write;
1553 if ( IN_CLOSE_NOWRITE == event )
1554 return num_close_nowrite;
1555 if ( IN_OPEN == event )
1556 return num_open;
1557 if ( IN_MOVED_FROM == event )
1558 return num_moved_from;
1559 if ( IN_MOVED_TO == event )
1560 return num_moved_to;
1561 if ( IN_CREATE == event )
1562 return num_create;
1563 if ( IN_DELETE == event )
1564 return num_delete;
1565 if ( IN_DELETE_SELF == event )
1566 return num_delete_self;
1567 if ( IN_UNMOUNT == event )
1568 return num_unmount;
1569 if ( IN_MOVE_SELF == event )
1570 return num_move_self;
1571
1572 if ( 0 == event )
1573 return num_total;
1574
1575 return -1;
1576 }
1577
1597 int inotifytools_get_stat_by_filename( char const * filename,
1598 int event ) {
1599 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
1600 filename ), event );
1601 }
1602
1613 int inotifytools_error() {
1614 return error;
1615 }
1616
1620 static int isdir( char const * path ) {
1621 static struct stat64 my_stat;
1622
1623 if ( -1 == lstat64( path, &my_stat ) ) {
1624 if (errno == ENOENT) return 0;
1625 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1626 return 0;
1627 }
1628
1629 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1630 }
1631
1632
1639 int inotifytools_get_num_watches() {
1640 int ret = 0;
1641 rbwalk(tree_filename, get_num, (void*)&ret);
1642 return ret;
1643 }
1644
1689 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1690 return inotifytools_fprintf( stdout, event, fmt );
1691 }
1692
1738 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1739 static struct nstring out;
1740 static int ret;
1741 ret = inotifytools_sprintf( &out, event, fmt );
1742 if ( -1 != ret ) fwrite( out.buf, sizeof(char), out.len, file );
1743 return ret;
1744 }
1745
1798 int inotifytools_sprintf( struct nstring * out, struct inotify_event* event, char* fmt ) {
1799 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1800 }
1801
1802
1853 int inotifytools_snprintf( struct nstring * out, int size,
1854 struct inotify_event* event, char* fmt ) {
1855 static char * filename, * eventname, * eventstr;
1856 static unsigned int i, ind;
1857 static char ch1;
1858 static char timestr[MAX_STRLEN];
1859 static time_t now;
1860
1861 if ( event->len > 0 ) {
1862 eventname = event->name;
1863 }
1864 else {
1865 eventname = NULL;
1866 }
1867
1868
1869 filename = inotifytools_filename_from_wd( event->wd );
1870
1871 if ( !fmt || 0 == strlen(fmt) ) {
1872 error = EINVAL;
1873 return -1;
1874 }
1875 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1876 error = EMSGSIZE;
1877 return -1;
1878 }
1879
1880 ind = 0;
1881 for ( i = 0; i < strlen(fmt) &&
1882 (int)ind < size - 1; ++i ) {
1883 if ( fmt[i] != '%' ) {
1884 out->buf[ind++] = fmt[i];
1885 continue;
1886 }
1887
1888 if ( i == strlen(fmt) - 1 ) {
1889 // last character is %, invalid
1890 error = EINVAL;
1891 return ind;
1892 }
1893
1894 ch1 = fmt[i+1];
1895
1896 if ( ch1 == '%' ) {
1897 out->buf[ind++] = '%';
1898 ++i;
1899 continue;
1900 }
1901
1902 if ( ch1 == '0' ) {
1903 out->buf[ind++] = '\0';
1904 ++i;
1905 continue;
1906 }
1907
1908 if ( ch1 == 'n' ) {
1909 out->buf[ind++] = '\n';
1910 ++i;
1911 continue;
1912 }
1913
1914 if ( ch1 == 'w' ) {
1915 if ( filename ) {
1916 strncpy( &out->buf[ind], filename, size - ind );
1917 ind += strlen(filename);
1918 }
1919 ++i;
1920 continue;
1921 }
1922
1923 if ( ch1 == 'f' ) {
1924 if ( eventname ) {
1925 strncpy( &out->buf[ind], eventname, size - ind );
1926 ind += strlen(eventname);
1927 }
1928 ++i;
1929 continue;
1930 }
1931
1932 if ( ch1 == 'c' ) {
1933 ind += snprintf( &out->buf[ind], size-ind, "%x", event->cookie);
1934 ++i;
1935 continue;
1936 }
1937
1938 if ( ch1 == 'e' ) {
1939 eventstr = inotifytools_event_to_str( event->mask );
1940 strncpy( &out->buf[ind], eventstr, size - ind );
1941 ind += strlen(eventstr);
1942 ++i;
1943 continue;
1944 }
1945
1946 if ( ch1 == 'T' ) {
1947
1948 if ( timefmt ) {
1949 now = time(0);
1950 struct tm now_tm;
1951 if (0 >= strftime(timestr, MAX_STRLEN - 1,
1952 timefmt,
1953 localtime_r(&now, &now_tm))) {
1954 // time format probably invalid
1955 error = EINVAL;
1956 return ind;
1957 }
1958 }
1959 else {
1960 timestr[0] = 0;
1961 }
1962
1963 strncpy( &out->buf[ind], timestr, size - ind );
1964 ind += strlen(timestr);
1965 ++i;
1966 continue;
1967 }
1968
1969 // Check if next char in fmt is e
1970 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1971 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1972 strncpy( &out->buf[ind], eventstr, size - ind );
1973 ind += strlen(eventstr);
1974 i += 2;
1975 continue;
1976 }
1977
1978 // OK, this wasn't a special format character, just output it as normal
1979 if ( ind < MAX_STRLEN ) out->buf[ind++] = '%';
1980 if ( ind < MAX_STRLEN ) out->buf[ind++] = ch1;
1981 ++i;
1982 }
1983 out->len = ind;
1984
1985 return ind - 1;
1986 }
1987
1997 void inotifytools_set_printf_timefmt( char * fmt ) {
1998 timefmt = fmt;
1999 }
2000
2009 int inotifytools_get_max_queued_events() {
2010 int ret;
2011 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
2012 return ret;
2013 }
2014
2024 int inotifytools_get_max_user_instances() {
2025 int ret;
2026 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
2027 return ret;
2028 }
2029
2039 int inotifytools_get_max_user_watches() {
2040 int ret;
2041 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2042 return ret;
2043 }
2044
2058 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2059 if (!pattern) {
2060 if (regex) {
2061 regfree(regex);
2062 free(regex);
2063 regex = 0;
2064 }
2065 return 1;
2066 }
2067
2068 if (regex) { regfree(regex); }
2069 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2070
2071 invert_regexp = invert;
2072 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2073 if (0 == ret) return 1;
2074
2075 regfree(regex);
2076 free(regex);
2077 regex = 0;
2078 error = EINVAL;
2079 return 0;
2080 }
2081
2093 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2094 return do_ignore_events_by_regex(pattern, flags, 0);
2095 }
2096
2108 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2109 return do_ignore_events_by_regex(pattern, flags, 1);
2110 }
2111
2112 int event_compare(const void *p1, const void *p2, const void *config)
2113 {
2114 if (!p1 || !p2) return p1 - p2;
2115 char asc = 1;
2116 long sort_event = (long)config;
2117 if (sort_event == -1) {
2118 sort_event = 0;
2119 asc = 0;
2120 } else if (sort_event < 0) {
2121 sort_event = -sort_event;
2122 asc = 0;
2123 }
2124 unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2125 unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2126 if (0 == *i1 - *i2) {
2127 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2128 }
2129 if (asc)
2130 return *i1 - *i2;
2131 else
2132 return *i2 - *i1;
2133 }
2134
2135 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2136 {
2137 struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2138 RBLIST *all = rbopenlist(tree_wd);
2139 void const *p = rbreadlist(all);
2140 while (p) {
2141 void const *r = rbsearch(p, ret);
2142 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2143 p = rbreadlist(all);
2144 }
2145 rbcloselist(all);
2146 return ret;
2147 }
inotifytools_initialize
int inotifytools_initialize()
Definition: inotifytools.c:283
inotifytools_remove_watch_by_wd
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:926
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1110
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1597
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1997
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:672
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:425
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1307
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1689
nstring::buf
char buf[MAX_STRLEN]
Definition: inotifytools.h:26
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:2039
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:845
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1738
inotifytools_sprintf
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1798
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1543
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:823
inotifytools_snprintf
int inotifytools_snprintf(struct nstring *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1853
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1639
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1520
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:644
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:972
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1613
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:802
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:557
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:994
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2093
nstring
This structure holds string that can contain any charater including NULL.
Definition: inotifytools.h:25
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:949
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:875
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1057
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:478
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:779
inotifytools_cleanup
void inotifytools_cleanup()
Definition: inotifytools.c:328
inotifytools_ignore_events_by_inverted_regex
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
Definition: inotifytools.c:2108
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1271
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:2009
nstring::len
unsigned int len
Definition: inotifytools.h:27
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:2024