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 MAX_STRLEN 4096
125 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
126 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
127 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
128 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
129
130 static int inotify_fd;
131 static unsigned num_access;
132 static unsigned num_modify;
133 static unsigned num_attrib;
134 static unsigned num_close_nowrite;
135 static unsigned num_close_write;
136 static unsigned num_open;
137 static unsigned num_move_self;
138 static unsigned num_moved_to;
139 static unsigned num_moved_from;
140 static unsigned num_create;
141 static unsigned num_delete;
142 static unsigned num_delete_self;
143 static unsigned num_unmount;
144 static unsigned num_total;
145 static int collect_stats = 0;
146
147 struct rbtree *tree_wd = 0;
148 struct rbtree *tree_filename = 0;
149 static int error = 0;
150 static int init = 0;
151 static char* timefmt = 0;
152 static regex_t* regex = 0;
153 /* 0: --exclude[i], 1: --include[i] */
154 static int invert_regexp = 0;
155
156 static int isdir( char const * path );
157 void record_stats( struct inotify_event const * event );
158 int onestr_to_event(char const * event);
159
177 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
178 #cond, mesg)
179
180 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
181
199 static void _niceassert( long cond, int line, char const * file,
200 char const * condstr, char const * mesg ) {
201 if ( cond ) return;
202
203 if ( mesg ) {
204 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
205 condstr, mesg );
206 }
207 else {
208 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
209 }
210 }
211
221 char * chrtostr(char ch) {
222 static char str[2] = { '\0', '\0' };
223 str[0] = ch;
224 return str;
225 }
226
230 int read_num_from_file( char * filename, int * num ) {
231 FILE * file = fopen( filename, "r" );
232 if ( !file ) {
233 error = errno;
234 return 0;
235 }
236
237 if ( EOF == fscanf( file, "%d", num ) ) {
238 error = errno;
239 return 0;
240 }
241
242 niceassert( 0 == fclose( file ), 0 );
243
244 return 1;
245 }
246
247 int wd_compare(const void *d1, const void *d2, const void *config) {
248 if (!d1 || !d2) return d1 - d2;
249 return ((watch*)d1)->wd - ((watch*)d2)->wd;
250 }
251
252 int filename_compare(const void *d1, const void *d2, const void *config) {
253 if (!d1 || !d2) return d1 - d2;
254 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
255 }
256
260 watch *watch_from_wd( int wd ) {
261 watch w;
262 w.wd = wd;
263 return (watch*)rbfind(&w, tree_wd);
264 }
265
269 watch *watch_from_filename( char const *filename ) {
270 watch w;
271 w.filename = (char*)filename;
272 return (watch*)rbfind(&w, tree_filename);
273 }
274
284 int inotifytools_initialize() {
285 if (init) return 1;
286
287 error = 0;
288 // Try to initialise inotify
289 inotify_fd = inotify_init();
290 if (inotify_fd < 0) {
291 error = errno;
292 return 0;
293 }
294
295 collect_stats = 0;
296 init = 1;
297 tree_wd = rbinit(wd_compare, 0);
298 tree_filename = rbinit(filename_compare, 0);
299 timefmt = 0;
300
301 return 1;
302 }
303
307 void destroy_watch(watch *w) {
308 if (w->filename) free(w->filename);
309 free(w);
310 }
311
315 void cleanup_tree(const void *nodep,
316 const VISIT which,
317 const int depth, void* arg) {
318 if (which != endorder && which != leaf) return;
319 watch *w = (watch*)nodep;
320 destroy_watch(w);
321 }
322
329 void inotifytools_cleanup() {
330 if (!init) return;
331
332 init = 0;
333 close(inotify_fd);
334 collect_stats = 0;
335 error = 0;
336 timefmt = 0;
337
338 if (regex) {
339 regfree(regex);
340 free(regex);
341 regex = 0;
342 }
343
344 rbwalk(tree_wd, cleanup_tree, 0);
345 rbdestroy(tree_wd); tree_wd = 0;
346 rbdestroy(tree_filename); tree_filename = 0;
347 }
348
352 void empty_stats(const void *nodep,
353 const VISIT which,
354 const int depth, void *arg) {
355 if (which != endorder && which != leaf) return;
356 watch *w = (watch*)nodep;
357 w->hit_access = 0;
358 w->hit_modify = 0;
359 w->hit_attrib = 0;
360 w->hit_close_nowrite = 0;
361 w->hit_close_write = 0;
362 w->hit_open = 0;
363 w->hit_move_self = 0;
364 w->hit_moved_from = 0;
365 w->hit_moved_to = 0;
366 w->hit_create = 0;
367 w->hit_delete = 0;
368 w->hit_delete_self = 0;
369 w->hit_unmount = 0;
370 w->hit_total = 0;
371 }
372
376 struct replace_filename_data {
377 char const *old_name;
378 char const *new_name;
379 size_t old_len;
380 };
381
385 void replace_filename(const void *nodep, const VISIT which, const int depth,
386 const struct replace_filename_data *data) {
387 if (which != endorder && which != leaf) return;
388 watch *w = (watch*)nodep;
389 char *name;
390 if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
391 nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
392 if (!strcmp( w->filename, data->new_name )) {
393 free(name);
394 } else {
395 rbdelete(w, tree_filename);
396 free( w->filename );
397 w->filename = name;
398 rbsearch(w, tree_filename);
399 }
400 }
401 }
402
406 void get_num(const void *nodep,
407 const VISIT which,
408 const int depth, void *arg) {
409 if (which != endorder && which != leaf) return;
410 ++(*((int*)arg));
411 }
412
413
426 void inotifytools_initialize_stats() {
427 niceassert( init, "inotifytools_initialize not called yet" );
428
429 // if already collecting stats, reset stats
430 if (collect_stats) {
431 rbwalk(tree_wd, empty_stats, 0);
432 }
433
434 num_access = 0;
435 num_modify = 0;
436 num_attrib = 0;
437 num_close_nowrite = 0;
438 num_close_write = 0;
439 num_open = 0;
440 num_move_self = 0;
441 num_moved_from = 0;
442 num_moved_to = 0;
443 num_create = 0;
444 num_delete = 0;
445 num_delete_self = 0;
446 num_unmount = 0;
447 num_total = 0;
448
449 collect_stats = 1;
450 }
451
479 int inotifytools_str_to_event_sep(char const * event, char sep) {
480 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
481 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
482 return -1;
483 }
484
485 int ret, ret1, len;
486 char * event1, * event2;
487 static const size_t eventstr_size = 4096;
488 char eventstr[eventstr_size];
489 ret = 0;
490
491 if ( !event || !event[0] ) return 0;
492
493 event1 = (char *)event;
494 event2 = strchr( event1, sep );
495 while ( event1 && event1[0] ) {
496 if ( event2 ) {
497 len = event2 - event1;
498 niceassert(len < eventstr_size,
499 "malformed event string (very long)");
500 }
501 else {
502 len = strlen(event1);
503 }
504 if (len > eventstr_size - 1)
505 len = eventstr_size - 1;
506
507 if (event2 || len == eventstr_size - 1) {
508 strncpy(eventstr, event1, len);
509 } else {
510 strcpy(eventstr, event1);
511 }
512
513 eventstr[len] = 0;
514
515 ret1 = onestr_to_event( eventstr );
516 if ( 0 == ret1 || -1 == ret1 ) {
517 ret = ret1;
518 break;
519 }
520 ret |= ret1;
521
522 event1 = event2;
523 if ( event1 && event1[0] ) {
524 // jump over 'sep' character
525 ++event1;
526 // if last character was 'sep'...
527 if ( !event1[0] ) return 0;
528 event2 = strchr( event1, sep );
529 }
530 }
531
532 return ret;
533 }
534
558 int inotifytools_str_to_event(char const * event) {
559 return inotifytools_str_to_event_sep( event, ',' );
560 }
561
573 int onestr_to_event(char const * event)
574 {
575 static int ret;
576 ret = -1;
577
578 if ( !event || !event[0] )
579 ret = 0;
580 else if ( 0 == strcasecmp(event, "ACCESS") )
581 ret = IN_ACCESS;
582 else if ( 0 == strcasecmp(event, "MODIFY") )
583 ret = IN_MODIFY;
584 else if ( 0 == strcasecmp(event, "ATTRIB") )
585 ret = IN_ATTRIB;
586 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
587 ret = IN_CLOSE_WRITE;
588 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
589 ret = IN_CLOSE_NOWRITE;
590 else if ( 0 == strcasecmp(event, "OPEN") )
591 ret = IN_OPEN;
592 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
593 ret = IN_MOVED_FROM;
594 else if ( 0 == strcasecmp(event, "MOVED_TO") )
595 ret = IN_MOVED_TO;
596 else if ( 0 == strcasecmp(event, "CREATE") )
597 ret = IN_CREATE;
598 else if ( 0 == strcasecmp(event, "DELETE") )
599 ret = IN_DELETE;
600 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
601 ret = IN_DELETE_SELF;
602 else if ( 0 == strcasecmp(event, "UNMOUNT") )
603 ret = IN_UNMOUNT;
604 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
605 ret = IN_Q_OVERFLOW;
606 else if ( 0 == strcasecmp(event, "IGNORED") )
607 ret = IN_IGNORED;
608 else if ( 0 == strcasecmp(event, "CLOSE") )
609 ret = IN_CLOSE;
610 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
611 ret = IN_MOVE_SELF;
612 else if ( 0 == strcasecmp(event, "MOVE") )
613 ret = IN_MOVE;
614 else if ( 0 == strcasecmp(event, "ISDIR") )
615 ret = IN_ISDIR;
616 else if ( 0 == strcasecmp(event, "ONESHOT") )
617 ret = IN_ONESHOT;
618 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
619 ret = IN_ALL_EVENTS;
620
621 return ret;
622 }
623
645 char * inotifytools_event_to_str(int events) {
646 return inotifytools_event_to_str_sep(events, ',');
647 }
648
673 char * inotifytools_event_to_str_sep(int events, char sep)
674 {
675 static char ret[1024];
676 ret[0] = '\0';
677 ret[1] = '\0';
678
679 if ( IN_ACCESS & events ) {
680 strcat( ret, chrtostr(sep) );
681 strcat( ret, "ACCESS" );
682 }
683 if ( IN_MODIFY & events ) {
684 strcat( ret, chrtostr(sep) );
685 strcat( ret, "MODIFY" );
686 }
687 if ( IN_ATTRIB & events ) {
688 strcat( ret, chrtostr(sep) );
689 strcat( ret, "ATTRIB" );
690 }
691 if ( IN_CLOSE_WRITE & events ) {
692 strcat( ret, chrtostr(sep) );
693 strcat( ret, "CLOSE_WRITE" );
694 }
695 if ( IN_CLOSE_NOWRITE & events ) {
696 strcat( ret, chrtostr(sep) );
697 strcat( ret, "CLOSE_NOWRITE" );
698 }
699 if ( IN_OPEN & events ) {
700 strcat( ret, chrtostr(sep) );
701 strcat( ret, "OPEN" );
702 }
703 if ( IN_MOVED_FROM & events ) {
704 strcat( ret, chrtostr(sep) );
705 strcat( ret, "MOVED_FROM" );
706 }
707 if ( IN_MOVED_TO & events ) {
708 strcat( ret, chrtostr(sep) );
709 strcat( ret, "MOVED_TO" );
710 }
711 if ( IN_CREATE & events ) {
712 strcat( ret, chrtostr(sep) );
713 strcat( ret, "CREATE" );
714 }
715 if ( IN_DELETE & events ) {
716 strcat( ret, chrtostr(sep) );
717 strcat( ret, "DELETE" );
718 }
719 if ( IN_DELETE_SELF & events ) {
720 strcat( ret, chrtostr(sep) );
721 strcat( ret, "DELETE_SELF" );
722 }
723 if ( IN_UNMOUNT & events ) {
724 strcat( ret, chrtostr(sep) );
725 strcat( ret, "UNMOUNT" );
726 }
727 if ( IN_Q_OVERFLOW & events ) {
728 strcat( ret, chrtostr(sep) );
729 strcat( ret, "Q_OVERFLOW" );
730 }
731 if ( IN_IGNORED & events ) {
732 strcat( ret, chrtostr(sep) );
733 strcat( ret, "IGNORED" );
734 }
735 if ( IN_CLOSE & events ) {
736 strcat( ret, chrtostr(sep) );
737 strcat( ret, "CLOSE" );
738 }
739 if ( IN_MOVE_SELF & events ) {
740 strcat( ret, chrtostr(sep) );
741 strcat( ret, "MOVE_SELF" );
742 }
743 if ( IN_ISDIR & events ) {
744 strcat( ret, chrtostr(sep) );
745 strcat( ret, "ISDIR" );
746 }
747 if ( IN_ONESHOT & events ) {
748 strcat( ret, chrtostr(sep) );
749 strcat( ret, "ONESHOT" );
750 }
751
752 // Maybe we didn't match any... ?
753 if (ret[0] == '\0') {
754 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
755 }
756
757 return &ret[1];
758 }
759
780 char * inotifytools_filename_from_wd( int wd ) {
781 niceassert( init, "inotifytools_initialize not called yet" );
782 watch *w = watch_from_wd(wd);
783 if (!w)
784 return NULL;
785
786 return w->filename;
787 }
788
803 int inotifytools_wd_from_filename( char const * filename ) {
804 niceassert( init, "inotifytools_initialize not called yet" );
805 watch *w = watch_from_filename(filename);
806 if (!w) return -1;
807 return w->wd;
808 }
809
824 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
825 niceassert( init, "inotifytools_initialize not called yet" );
826 watch *w = watch_from_wd(wd);
827 if (!w) return;
828 if (w->filename) free(w->filename);
829 w->filename = strdup(filename);
830 }
831
846 void inotifytools_set_filename_by_filename( char const * oldname,
847 char const * newname ) {
848 watch *w = watch_from_filename(oldname);
849 if (!w) return;
850 if (w->filename) free(w->filename);
851 w->filename = strdup(newname);
852 }
853
876 void inotifytools_replace_filename( char const * oldname,
877 char const * newname ) {
878 if ( !oldname || !newname ) return;
879 struct replace_filename_data data;
880 data.old_name = oldname;
881 data.new_name = newname;
882 data.old_len = strlen(oldname);
883 rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
884 }
885
889 int remove_inotify_watch(watch *w) {
890 error = 0;
891 int status = inotify_rm_watch( inotify_fd, w->wd );
892 if ( status < 0 ) {
893 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
894 strerror(status) );
895 error = status;
896 return 0;
897 }
898 return 1;
899 }
900
904 watch *create_watch(int wd, char *filename) {
905 if ( wd <= 0 || !filename) return 0;
906
907 watch *w = (watch*)calloc(1, sizeof(watch));
908 w->wd = wd;
909 w->filename = strdup(filename);
910 rbsearch(w, tree_wd);
911 rbsearch(w, tree_filename);
912 return NULL;
913 }
914
927 int inotifytools_remove_watch_by_wd( int wd ) {
928 niceassert( init, "inotifytools_initialize not called yet" );
929 watch *w = watch_from_wd(wd);
930 if (!w) return 1;
931
932 if (!remove_inotify_watch(w)) return 0;
933 rbdelete(w, tree_wd);
934 rbdelete(w, tree_filename);
935 destroy_watch(w);
936 return 1;
937 }
938
950 int inotifytools_remove_watch_by_filename( char const * filename ) {
951 niceassert( init, "inotifytools_initialize not called yet" );
952 watch *w = watch_from_filename(filename);
953 if (!w) return 1;
954
955 if (!remove_inotify_watch(w)) return 0;
956 rbdelete(w, tree_wd);
957 rbdelete(w, tree_filename);
958 destroy_watch(w);
959 return 1;
960 }
961
973 int inotifytools_watch_file( char const * filename, int events ) {
974 static char const * filenames[2];
975 filenames[0] = filename;
976 filenames[1] = NULL;
977 return inotifytools_watch_files( filenames, events );
978 }
979
995 int inotifytools_watch_files( char const * filenames[], int events ) {
996 niceassert( init, "inotifytools_initialize not called yet" );
997 error = 0;
998
999 static int i;
1000 for ( i = 0; filenames[i]; ++i ) {
1001 static int wd;
1002 wd = inotify_add_watch( inotify_fd, filenames[i], events );
1003 if ( wd < 0 ) {
1004 if ( wd == -1 ) {
1005 error = errno;
1006 return 0;
1007 } // if ( wd == -1 )
1008 else {
1009 fprintf( stderr, "Failed to watch %s: returned wd was %d "
1010 "(expected -1 or >0 )", filenames[i], wd );
1011 // no appropriate value for error
1012 return 0;
1013 } // else
1014 } // if ( wd < 0 )
1015
1016 char *filename;
1017 // Always end filename with / if it is a directory
1018 if ( !isdir(filenames[i])
1019 || filenames[i][strlen(filenames[i])-1] == '/') {
1020 filename = strdup(filenames[i]);
1021 }
1022 else {
1023 nasprintf( &filename, "%s/", filenames[i] );
1024 }
1025 create_watch(wd, filename);
1026 free(filename);
1027 } // for
1028
1029 return 1;
1030 }
1031
1058 struct inotify_event * inotifytools_next_event( long int timeout ) {
1059 return inotifytools_next_events( timeout, 1 );
1060 }
1061
1062
1111 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1112 niceassert( init, "inotifytools_initialize not called yet" );
1113 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1114
1115 if ( num_events < 1 ) return NULL;
1116
1117 static struct inotify_event event[MAX_EVENTS];
1118 static struct inotify_event * ret;
1119 static int first_byte = 0;
1120 static ssize_t bytes;
1121 static jmp_buf jmp;
1122 static char match_name[MAX_STRLEN];
1123
1124 #define RETURN(A) {\
1125 if (regex) {\
1126 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1127 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1128 if (!invert_regexp)\
1129 longjmp(jmp,0);\
1130 } else {\
1131 if (invert_regexp)\
1132 longjmp(jmp,0);\
1133 }\
1134 }\
1135 if ( collect_stats ) {\
1136 record_stats( A );\
1137 }\
1138 return A;\
1139 }
1140
1141 setjmp(jmp);
1142
1143 error = 0;
1144
1145 // first_byte is index into event buffer
1146 if ( first_byte != 0
1147 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1148
1149 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1150 first_byte += sizeof(struct inotify_event) + ret->len;
1151
1152 // if the pointer to the next event exactly hits end of bytes read,
1153 // that's good. next time we're called, we'll read.
1154 if ( first_byte == bytes ) {
1155 first_byte = 0;
1156 }
1157 else if ( first_byte > bytes ) {
1158 // oh... no. this can't be happening. An incomplete event.
1159 // Copy what we currently have into first element, call self to
1160 // read remainder.
1161 // oh, and they BETTER NOT overlap.
1162 // Boy I hope this code works.
1163 // But I think this can never happen due to how inotify is written.
1164 niceassert( (long)((char *)&event[0] +
1165 sizeof(struct inotify_event) +
1166 event[0].len) <= (long)ret,
1167 "extremely unlucky user, death imminent" );
1168 // how much of the event do we have?
1169 bytes = (char *)&event[0] + bytes - (char *)ret;
1170 memcpy( &event[0], ret, bytes );
1171 return inotifytools_next_events( timeout, num_events );
1172 }
1173 RETURN(ret);
1174
1175 }
1176
1177 else if ( first_byte == 0 ) {
1178 bytes = 0;
1179 }
1180
1181
1182 static ssize_t this_bytes;
1183 static unsigned int bytes_to_read;
1184 static int rc;
1185 static fd_set read_fds;
1186
1187 static struct timeval read_timeout;
1188 read_timeout.tv_sec = timeout;
1189 read_timeout.tv_usec = 0;
1190 static struct timeval * read_timeout_ptr;
1191 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1192
1193 FD_ZERO(&read_fds);
1194 FD_SET(inotify_fd, &read_fds);
1195 rc = select(inotify_fd + 1, &read_fds,
1196 NULL, NULL, read_timeout_ptr);
1197 if ( rc < 0 ) {
1198 // error
1199 error = errno;
1200 return NULL;
1201 }
1202 else if ( rc == 0 ) {
1203 // timeout
1204 return NULL;
1205 }
1206
1207 // wait until we have enough bytes to read
1208 do {
1209 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1210 } while ( !rc &&
1211 bytes_to_read < sizeof(struct inotify_event)*num_events );
1212
1213 if ( rc == -1 ) {
1214 error = errno;
1215 return NULL;
1216 }
1217
1218 this_bytes = read(inotify_fd, &event[0] + bytes,
1219 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1220 if ( this_bytes < 0 ) {
1221 error = errno;
1222 return NULL;
1223 }
1224 if ( this_bytes == 0 ) {
1225 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1226 "events occurred at once.\n");
1227 return NULL;
1228 }
1229 bytes += this_bytes;
1230
1231 ret = &event[0];
1232 first_byte = sizeof(struct inotify_event) + ret->len;
1233 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1234 "almost certainly screw up." );
1235 if ( first_byte == bytes ) {
1236 first_byte = 0;
1237 }
1238
1239 RETURN(ret);
1240
1241 #undef RETURN
1242 }
1243
1269 int inotifytools_watch_recursively( char const * path, int events ) {
1270 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1271 }
1272
1305 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1306 char const ** exclude_list ) {
1307 niceassert( init, "inotifytools_initialize not called yet" );
1308
1309 DIR * dir;
1310 char * my_path;
1311 error = 0;
1312 dir = opendir( path );
1313 if ( !dir ) {
1314 // If not a directory, don't need to do anything special
1315 if ( errno == ENOTDIR ) {
1316 return inotifytools_watch_file( path, events );
1317 }
1318 else {
1319 error = errno;
1320 return 0;
1321 }
1322 }
1323
1324 if ( path[strlen(path)-1] != '/' ) {
1325 nasprintf( &my_path, "%s/", path );
1326 }
1327 else {
1328 my_path = (char *)path;
1329 }
1330
1331 static struct dirent * ent;
1332 char * next_file;
1333 static struct stat64 my_stat;
1334 ent = readdir( dir );
1335 // Watch each directory within this directory
1336 while ( ent ) {
1337 if ( (0 != strcmp( ent->d_name, "." )) &&
1338 (0 != strcmp( ent->d_name, ".." )) ) {
1339 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1340 if ( -1 == lstat64( next_file, &my_stat ) ) {
1341 error = errno;
1342 free( next_file );
1343 if ( errno != EACCES ) {
1344 error = errno;
1345 if ( my_path != path ) free( my_path );
1346 closedir( dir );
1347 return 0;
1348 }
1349 }
1350 else if ( S_ISDIR( my_stat.st_mode ) &&
1351 !S_ISLNK( my_stat.st_mode )) {
1352 free( next_file );
1353 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1354 static unsigned int no_watch;
1355 static char const ** exclude_entry;
1356
1357 no_watch = 0;
1358 for (exclude_entry = exclude_list;
1359 exclude_entry && *exclude_entry && !no_watch;
1360 ++exclude_entry) {
1361 static int exclude_length;
1362
1363 exclude_length = strlen(*exclude_entry);
1364 if ((*exclude_entry)[exclude_length-1] == '/') {
1365 --exclude_length;
1366 }
1367 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1368 !strncmp(*exclude_entry, next_file, exclude_length)) {
1369 // directory found in exclude list
1370 no_watch = 1;
1371 }
1372 }
1373 if (!no_watch) {
1374 static int status;
1375 status = inotifytools_watch_recursively_with_exclude(
1376 next_file,
1377 events,
1378 exclude_list );
1379 // For some errors, we will continue.
1380 if ( !status && (EACCES != error) && (ENOENT != error) &&
1381 (ELOOP != error) ) {
1382 free( next_file );
1383 if ( my_path != path ) free( my_path );
1384 closedir( dir );
1385 return 0;
1386 }
1387 } // if !no_watch
1388 free( next_file );
1389 } // if isdir and not islnk
1390 else {
1391 free( next_file );
1392 }
1393 }
1394 ent = readdir( dir );
1395 error = 0;
1396 }
1397
1398 closedir( dir );
1399
1400 int ret = inotifytools_watch_file( my_path, events );
1401 if ( my_path != path ) free( my_path );
1402 return ret;
1403 }
1404
1408 void record_stats( struct inotify_event const * event ) {
1409 if (!event) return;
1410 watch *w = watch_from_wd(event->wd);
1411 if (!w) return;
1412 if ( IN_ACCESS & event->mask ) {
1413 ++w->hit_access;
1414 ++num_access;
1415 }
1416 if ( IN_MODIFY & event->mask ) {
1417 ++w->hit_modify;
1418 ++num_modify;
1419 }
1420 if ( IN_ATTRIB & event->mask ) {
1421 ++w->hit_attrib;
1422 ++num_attrib;
1423 }
1424 if ( IN_CLOSE_WRITE & event->mask ) {
1425 ++w->hit_close_write;
1426 ++num_close_write;
1427 }
1428 if ( IN_CLOSE_NOWRITE & event->mask ) {
1429 ++w->hit_close_nowrite;
1430 ++num_close_nowrite;
1431 }
1432 if ( IN_OPEN & event->mask ) {
1433 ++w->hit_open;
1434 ++num_open;
1435 }
1436 if ( IN_MOVED_FROM & event->mask ) {
1437 ++w->hit_moved_from;
1438 ++num_moved_from;
1439 }
1440 if ( IN_MOVED_TO & event->mask ) {
1441 ++w->hit_moved_to;
1442 ++num_moved_to;
1443 }
1444 if ( IN_CREATE & event->mask ) {
1445 ++w->hit_create;
1446 ++num_create;
1447 }
1448 if ( IN_DELETE & event->mask ) {
1449 ++w->hit_delete;
1450 ++num_delete;
1451 }
1452 if ( IN_DELETE_SELF & event->mask ) {
1453 ++w->hit_delete_self;
1454 ++num_delete_self;
1455 }
1456 if ( IN_UNMOUNT & event->mask ) {
1457 ++w->hit_unmount;
1458 ++num_unmount;
1459 }
1460 if ( IN_MOVE_SELF & event->mask ) {
1461 ++w->hit_move_self;
1462 ++num_move_self;
1463 }
1464
1465 ++w->hit_total;
1466 ++num_total;
1467
1468 }
1469
1470 unsigned int *stat_ptr(watch *w, int event)
1471 {
1472 if ( IN_ACCESS == event )
1473 return &w->hit_access;
1474 if ( IN_MODIFY == event )
1475 return &w->hit_modify;
1476 if ( IN_ATTRIB == event )
1477 return &w->hit_attrib;
1478 if ( IN_CLOSE_WRITE == event )
1479 return &w->hit_close_write;
1480 if ( IN_CLOSE_NOWRITE == event )
1481 return &w->hit_close_nowrite;
1482 if ( IN_OPEN == event )
1483 return &w->hit_open;
1484 if ( IN_MOVED_FROM == event )
1485 return &w->hit_moved_from;
1486 if ( IN_MOVED_TO == event )
1487 return &w->hit_moved_to;
1488 if ( IN_CREATE == event )
1489 return &w->hit_create;
1490 if ( IN_DELETE == event )
1491 return &w->hit_delete;
1492 if ( IN_DELETE_SELF == event )
1493 return &w->hit_delete_self;
1494 if ( IN_UNMOUNT == event )
1495 return &w->hit_unmount;
1496 if ( IN_MOVE_SELF == event )
1497 return &w->hit_move_self;
1498 if ( 0 == event )
1499 return &w->hit_total;
1500 return 0;
1501 }
1502
1518 int inotifytools_get_stat_by_wd( int wd, int event ) {
1519 if (!collect_stats) return -1;
1520
1521 watch *w = watch_from_wd(wd);
1522 if (!w) return -1;
1523 unsigned int *i = stat_ptr(w, event);
1524 if (!i) return -1;
1525 return *i;
1526 }
1527
1541 int inotifytools_get_stat_total( int event ) {
1542 if (!collect_stats) return -1;
1543 if ( IN_ACCESS == event )
1544 return num_access;
1545 if ( IN_MODIFY == event )
1546 return num_modify;
1547 if ( IN_ATTRIB == event )
1548 return num_attrib;
1549 if ( IN_CLOSE_WRITE == event )
1550 return num_close_write;
1551 if ( IN_CLOSE_NOWRITE == event )
1552 return num_close_nowrite;
1553 if ( IN_OPEN == event )
1554 return num_open;
1555 if ( IN_MOVED_FROM == event )
1556 return num_moved_from;
1557 if ( IN_MOVED_TO == event )
1558 return num_moved_to;
1559 if ( IN_CREATE == event )
1560 return num_create;
1561 if ( IN_DELETE == event )
1562 return num_delete;
1563 if ( IN_DELETE_SELF == event )
1564 return num_delete_self;
1565 if ( IN_UNMOUNT == event )
1566 return num_unmount;
1567 if ( IN_MOVE_SELF == event )
1568 return num_move_self;
1569
1570 if ( 0 == event )
1571 return num_total;
1572
1573 return -1;
1574 }
1575
1595 int inotifytools_get_stat_by_filename( char const * filename,
1596 int event ) {
1597 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
1598 filename ), event );
1599 }
1600
1611 int inotifytools_error() {
1612 return error;
1613 }
1614
1618 static int isdir( char const * path ) {
1619 static struct stat64 my_stat;
1620
1621 if ( -1 == lstat64( path, &my_stat ) ) {
1622 if (errno == ENOENT) return 0;
1623 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1624 return 0;
1625 }
1626
1627 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1628 }
1629
1630
1637 int inotifytools_get_num_watches() {
1638 int ret = 0;
1639 rbwalk(tree_filename, get_num, (void*)&ret);
1640 return ret;
1641 }
1642
1683 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1684 return inotifytools_fprintf( stdout, event, fmt );
1685 }
1686
1728 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1729 static char out[MAX_STRLEN+1];
1730 static int ret;
1731 ret = inotifytools_sprintf( out, event, fmt );
1732 if ( -1 != ret ) fprintf( file, "%s", out );
1733 return ret;
1734 }
1735
1786 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1787 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1788 }
1789
1790
1837 int inotifytools_snprintf( char * out, int size,
1838 struct inotify_event* event, char* fmt ) {
1839 static char * filename, * eventname, * eventstr;
1840 static unsigned int i, ind;
1841 static char ch1;
1842 static char timestr[MAX_STRLEN];
1843 static time_t now;
1844
1845
1846 if ( event->len > 0 ) {
1847 eventname = event->name;
1848 }
1849 else {
1850 eventname = NULL;
1851 }
1852
1853
1854 filename = inotifytools_filename_from_wd( event->wd );
1855
1856 if ( !fmt || 0 == strlen(fmt) ) {
1857 error = EINVAL;
1858 return -1;
1859 }
1860 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1861 error = EMSGSIZE;
1862 return -1;
1863 }
1864
1865 ind = 0;
1866 for ( i = 0; i < strlen(fmt) &&
1867 (int)ind < size - 1; ++i ) {
1868 if ( fmt[i] != '%' ) {
1869 out[ind++] = fmt[i];
1870 continue;
1871 }
1872
1873 if ( i == strlen(fmt) - 1 ) {
1874 // last character is %, invalid
1875 error = EINVAL;
1876 return ind;
1877 }
1878
1879 ch1 = fmt[i+1];
1880
1881 if ( ch1 == '%' ) {
1882 out[ind++] = '%';
1883 ++i;
1884 continue;
1885 }
1886
1887 if ( ch1 == 'w' ) {
1888 if ( filename ) {
1889 strncpy( &out[ind], filename, size - ind );
1890 ind += strlen(filename);
1891 }
1892 ++i;
1893 continue;
1894 }
1895
1896 if ( ch1 == 'f' ) {
1897 if ( eventname ) {
1898 strncpy( &out[ind], eventname, size - ind );
1899 ind += strlen(eventname);
1900 }
1901 ++i;
1902 continue;
1903 }
1904
1905 if ( ch1 == 'e' ) {
1906 eventstr = inotifytools_event_to_str( event->mask );
1907 strncpy( &out[ind], eventstr, size - ind );
1908 ind += strlen(eventstr);
1909 ++i;
1910 continue;
1911 }
1912
1913 if ( ch1 == 'T' ) {
1914
1915 if ( timefmt ) {
1916
1917 now = time(0);
1918 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1919 localtime( &now ) ) ) {
1920
1921 // time format probably invalid
1922 error = EINVAL;
1923 return ind;
1924 }
1925 }
1926 else {
1927 timestr[0] = 0;
1928 }
1929
1930 strncpy( &out[ind], timestr, size - ind );
1931 ind += strlen(timestr);
1932 ++i;
1933 continue;
1934 }
1935
1936 // Check if next char in fmt is e
1937 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1938 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1939 strncpy( &out[ind], eventstr, size - ind );
1940 ind += strlen(eventstr);
1941 i += 2;
1942 continue;
1943 }
1944
1945 // OK, this wasn't a special format character, just output it as normal
1946 if ( ind < MAX_STRLEN ) out[ind++] = '%';
1947 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1948 ++i;
1949 }
1950 out[ind] = 0;
1951
1952 return ind - 1;
1953 }
1954
1964 void inotifytools_set_printf_timefmt( char * fmt ) {
1965 timefmt = fmt;
1966 }
1967
1976 int inotifytools_get_max_queued_events() {
1977 int ret;
1978 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1979 return ret;
1980 }
1981
1991 int inotifytools_get_max_user_instances() {
1992 int ret;
1993 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1994 return ret;
1995 }
1996
2006 int inotifytools_get_max_user_watches() {
2007 int ret;
2008 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2009 return ret;
2010 }
2011
2025 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2026 if (!pattern) {
2027 if (regex) {
2028 regfree(regex);
2029 free(regex);
2030 regex = 0;
2031 }
2032 return 1;
2033 }
2034
2035 if (regex) { regfree(regex); }
2036 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2037
2038 invert_regexp = invert;
2039 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2040 if (0 == ret) return 1;
2041
2042 regfree(regex);
2043 free(regex);
2044 regex = 0;
2045 error = EINVAL;
2046 return 0;
2047 }
2048
2060 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2061 return do_ignore_events_by_regex(pattern, flags, 0);
2062 }
2063
2075 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2076 return do_ignore_events_by_regex(pattern, flags, 1);
2077 }
2078
2079 int event_compare(const void *p1, const void *p2, const void *config)
2080 {
2081 if (!p1 || !p2) return p1 - p2;
2082 char asc = 1;
2083 int sort_event = *(int*)config;
2084 if (sort_event == -1) {
2085 sort_event = 0;
2086 asc = 0;
2087 } else if (sort_event < 0) {
2088 sort_event = -sort_event;
2089 asc = 0;
2090 }
2091 unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2092 unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2093 if (0 == *i1 - *i2) {
2094 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2095 }
2096 if (asc)
2097 return *i1 - *i2;
2098 else
2099 return *i2 - *i1;
2100 }
2101
2102 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2103 {
2104 struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2105 RBLIST *all = rbopenlist(tree_wd);
2106 void const *p = rbreadlist(all);
2107 while (p) {
2108 void const *r = rbsearch(p, ret);
2109 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2110 p = rbreadlist(all);
2111 }
2112 rbcloselist(all);
2113 return ret;
2114 }
inotifytools_initialize
int inotifytools_initialize()
Definition: inotifytools.c:284
inotifytools_remove_watch_by_wd
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:927
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1111
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1595
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1964
inotifytools_sprintf
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1786
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:673
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:426
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1305
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1683
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:2006
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:846
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1728
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1541
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:824
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1637
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1518
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:645
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:973
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1611
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:803
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:558
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:995
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2060
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:950
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:876
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1058
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:479
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:780
inotifytools_snprintf
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1837
inotifytools_cleanup
void inotifytools_cleanup()
Definition: inotifytools.c:329
inotifytools_ignore_events_by_inverted_regex
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
Definition: inotifytools.c:2075
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1269
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:1976
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:1991