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 char eventstr[4096];
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 < 4096, "malformed event string (very long)" );
498 }
499 else {
500 len = strlen(event1);
501 }
502 if ( len > 4095 ) len = 4095;
503
504 if (event2) {
505 strncpy(eventstr, event1, len);
506 } else {
507 strcpy(eventstr, event1);
508 }
509
510 eventstr[len] = 0;
511
512 ret1 = onestr_to_event( eventstr );
513 if ( 0 == ret1 || -1 == ret1 ) {
514 ret = ret1;
515 break;
516 }
517 ret |= ret1;
518
519 event1 = event2;
520 if ( event1 && event1[0] ) {
521 // jump over 'sep' character
522 ++event1;
523 // if last character was 'sep'...
524 if ( !event1[0] ) return 0;
525 event2 = strchr( event1, sep );
526 }
527 }
528
529 return ret;
530 }
531
555 int inotifytools_str_to_event(char const * event) {
556 return inotifytools_str_to_event_sep( event, ',' );
557 }
558
570 int onestr_to_event(char const * event)
571 {
572 static int ret;
573 ret = -1;
574
575 if ( !event || !event[0] )
576 ret = 0;
577 else if ( 0 == strcasecmp(event, "ACCESS") )
578 ret = IN_ACCESS;
579 else if ( 0 == strcasecmp(event, "MODIFY") )
580 ret = IN_MODIFY;
581 else if ( 0 == strcasecmp(event, "ATTRIB") )
582 ret = IN_ATTRIB;
583 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
584 ret = IN_CLOSE_WRITE;
585 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
586 ret = IN_CLOSE_NOWRITE;
587 else if ( 0 == strcasecmp(event, "OPEN") )
588 ret = IN_OPEN;
589 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
590 ret = IN_MOVED_FROM;
591 else if ( 0 == strcasecmp(event, "MOVED_TO") )
592 ret = IN_MOVED_TO;
593 else if ( 0 == strcasecmp(event, "CREATE") )
594 ret = IN_CREATE;
595 else if ( 0 == strcasecmp(event, "DELETE") )
596 ret = IN_DELETE;
597 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
598 ret = IN_DELETE_SELF;
599 else if ( 0 == strcasecmp(event, "UNMOUNT") )
600 ret = IN_UNMOUNT;
601 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
602 ret = IN_Q_OVERFLOW;
603 else if ( 0 == strcasecmp(event, "IGNORED") )
604 ret = IN_IGNORED;
605 else if ( 0 == strcasecmp(event, "CLOSE") )
606 ret = IN_CLOSE;
607 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
608 ret = IN_MOVE_SELF;
609 else if ( 0 == strcasecmp(event, "MOVE") )
610 ret = IN_MOVE;
611 else if ( 0 == strcasecmp(event, "ISDIR") )
612 ret = IN_ISDIR;
613 else if ( 0 == strcasecmp(event, "ONESHOT") )
614 ret = IN_ONESHOT;
615 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
616 ret = IN_ALL_EVENTS;
617
618 return ret;
619 }
620
642 char * inotifytools_event_to_str(int events) {
643 return inotifytools_event_to_str_sep(events, ',');
644 }
645
670 char * inotifytools_event_to_str_sep(int events, char sep)
671 {
672 static char ret[1024];
673 ret[0] = '\0';
674 ret[1] = '\0';
675
676 if ( IN_ACCESS & events ) {
677 strcat( ret, chrtostr(sep) );
678 strcat( ret, "ACCESS" );
679 }
680 if ( IN_MODIFY & events ) {
681 strcat( ret, chrtostr(sep) );
682 strcat( ret, "MODIFY" );
683 }
684 if ( IN_ATTRIB & events ) {
685 strcat( ret, chrtostr(sep) );
686 strcat( ret, "ATTRIB" );
687 }
688 if ( IN_CLOSE_WRITE & events ) {
689 strcat( ret, chrtostr(sep) );
690 strcat( ret, "CLOSE_WRITE" );
691 }
692 if ( IN_CLOSE_NOWRITE & events ) {
693 strcat( ret, chrtostr(sep) );
694 strcat( ret, "CLOSE_NOWRITE" );
695 }
696 if ( IN_OPEN & events ) {
697 strcat( ret, chrtostr(sep) );
698 strcat( ret, "OPEN" );
699 }
700 if ( IN_MOVED_FROM & events ) {
701 strcat( ret, chrtostr(sep) );
702 strcat( ret, "MOVED_FROM" );
703 }
704 if ( IN_MOVED_TO & events ) {
705 strcat( ret, chrtostr(sep) );
706 strcat( ret, "MOVED_TO" );
707 }
708 if ( IN_CREATE & events ) {
709 strcat( ret, chrtostr(sep) );
710 strcat( ret, "CREATE" );
711 }
712 if ( IN_DELETE & events ) {
713 strcat( ret, chrtostr(sep) );
714 strcat( ret, "DELETE" );
715 }
716 if ( IN_DELETE_SELF & events ) {
717 strcat( ret, chrtostr(sep) );
718 strcat( ret, "DELETE_SELF" );
719 }
720 if ( IN_UNMOUNT & events ) {
721 strcat( ret, chrtostr(sep) );
722 strcat( ret, "UNMOUNT" );
723 }
724 if ( IN_Q_OVERFLOW & events ) {
725 strcat( ret, chrtostr(sep) );
726 strcat( ret, "Q_OVERFLOW" );
727 }
728 if ( IN_IGNORED & events ) {
729 strcat( ret, chrtostr(sep) );
730 strcat( ret, "IGNORED" );
731 }
732 if ( IN_CLOSE & events ) {
733 strcat( ret, chrtostr(sep) );
734 strcat( ret, "CLOSE" );
735 }
736 if ( IN_MOVE_SELF & events ) {
737 strcat( ret, chrtostr(sep) );
738 strcat( ret, "MOVE_SELF" );
739 }
740 if ( IN_ISDIR & events ) {
741 strcat( ret, chrtostr(sep) );
742 strcat( ret, "ISDIR" );
743 }
744 if ( IN_ONESHOT & events ) {
745 strcat( ret, chrtostr(sep) );
746 strcat( ret, "ONESHOT" );
747 }
748
749 // Maybe we didn't match any... ?
750 if (ret[0] == '\0') {
751 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
752 }
753
754 return &ret[1];
755 }
756
777 char * inotifytools_filename_from_wd( int wd ) {
778 niceassert( init, "inotifytools_initialize not called yet" );
779 watch *w = watch_from_wd(wd);
780 if (!w)
781 return NULL;
782
783 return w->filename;
784 }
785
800 int inotifytools_wd_from_filename( char const * filename ) {
801 niceassert( init, "inotifytools_initialize not called yet" );
802 watch *w = watch_from_filename(filename);
803 if (!w) return -1;
804 return w->wd;
805 }
806
821 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
822 niceassert( init, "inotifytools_initialize not called yet" );
823 watch *w = watch_from_wd(wd);
824 if (!w) return;
825 if (w->filename) free(w->filename);
826 w->filename = strdup(filename);
827 }
828
843 void inotifytools_set_filename_by_filename( char const * oldname,
844 char const * newname ) {
845 watch *w = watch_from_filename(oldname);
846 if (!w) return;
847 if (w->filename) free(w->filename);
848 w->filename = strdup(newname);
849 }
850
873 void inotifytools_replace_filename( char const * oldname,
874 char const * newname ) {
875 if ( !oldname || !newname ) return;
876 struct replace_filename_data data;
877 data.old_name = oldname;
878 data.new_name = newname;
879 data.old_len = strlen(oldname);
880 rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
881 }
882
886 int remove_inotify_watch(watch *w) {
887 error = 0;
888 int status = inotify_rm_watch( inotify_fd, w->wd );
889 if ( status < 0 ) {
890 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
891 strerror(status) );
892 error = status;
893 return 0;
894 }
895 return 1;
896 }
897
901 watch *create_watch(int wd, char *filename) {
902 if ( wd <= 0 || !filename) return 0;
903
904 watch *w = (watch*)calloc(1, sizeof(watch));
905 w->wd = wd;
906 w->filename = strdup(filename);
907 rbsearch(w, tree_wd);
908 rbsearch(w, tree_filename);
909 return NULL;
910 }
911
924 int inotifytools_remove_watch_by_wd( int wd ) {
925 niceassert( init, "inotifytools_initialize not called yet" );
926 watch *w = watch_from_wd(wd);
927 if (!w) return 1;
928
929 if (!remove_inotify_watch(w)) return 0;
930 rbdelete(w, tree_wd);
931 rbdelete(w, tree_filename);
932 destroy_watch(w);
933 return 1;
934 }
935
947 int inotifytools_remove_watch_by_filename( char const * filename ) {
948 niceassert( init, "inotifytools_initialize not called yet" );
949 watch *w = watch_from_filename(filename);
950 if (!w) return 1;
951
952 if (!remove_inotify_watch(w)) return 0;
953 rbdelete(w, tree_wd);
954 rbdelete(w, tree_filename);
955 destroy_watch(w);
956 return 1;
957 }
958
970 int inotifytools_watch_file( char const * filename, int events ) {
971 static char const * filenames[2];
972 filenames[0] = filename;
973 filenames[1] = NULL;
974 return inotifytools_watch_files( filenames, events );
975 }
976
992 int inotifytools_watch_files( char const * filenames[], int events ) {
993 niceassert( init, "inotifytools_initialize not called yet" );
994 error = 0;
995
996 static int i;
997 for ( i = 0; filenames[i]; ++i ) {
998 static int wd;
999 wd = inotify_add_watch( inotify_fd, filenames[i], events );
1000 if ( wd < 0 ) {
1001 if ( wd == -1 ) {
1002 error = errno;
1003 return 0;
1004 } // if ( wd == -1 )
1005 else {
1006 fprintf( stderr, "Failed to watch %s: returned wd was %d "
1007 "(expected -1 or >0 )", filenames[i], wd );
1008 // no appropriate value for error
1009 return 0;
1010 } // else
1011 } // if ( wd < 0 )
1012
1013 char *filename;
1014 // Always end filename with / if it is a directory
1015 if ( !isdir(filenames[i])
1016 || filenames[i][strlen(filenames[i])-1] == '/') {
1017 filename = strdup(filenames[i]);
1018 }
1019 else {
1020 nasprintf( &filename, "%s/", filenames[i] );
1021 }
1022 create_watch(wd, filename);
1023 free(filename);
1024 } // for
1025
1026 return 1;
1027 }
1028
1055 struct inotify_event * inotifytools_next_event( long int timeout ) {
1056 return inotifytools_next_events( timeout, 1 );
1057 }
1058
1059
1108 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1109 niceassert( init, "inotifytools_initialize not called yet" );
1110 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1111
1112 if ( num_events < 1 ) return NULL;
1113
1114 static struct inotify_event event[MAX_EVENTS];
1115 static struct inotify_event * ret;
1116 static int first_byte = 0;
1117 static ssize_t bytes;
1118 static jmp_buf jmp;
1119 static char match_name[MAX_STRLEN];
1120
1121 #define RETURN(A) {\
1122 if (regex) {\
1123 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1124 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1125 if (!invert_regexp)\
1126 longjmp(jmp,0);\
1127 } else {\
1128 if (invert_regexp)\
1129 longjmp(jmp,0);\
1130 }\
1131 }\
1132 if ( collect_stats ) {\
1133 record_stats( A );\
1134 }\
1135 return A;\
1136 }
1137
1138 setjmp(jmp);
1139
1140 error = 0;
1141
1142 // first_byte is index into event buffer
1143 if ( first_byte != 0
1144 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1145
1146 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1147 first_byte += sizeof(struct inotify_event) + ret->len;
1148
1149 // if the pointer to the next event exactly hits end of bytes read,
1150 // that's good. next time we're called, we'll read.
1151 if ( first_byte == bytes ) {
1152 first_byte = 0;
1153 }
1154 else if ( first_byte > bytes ) {
1155 // oh... no. this can't be happening. An incomplete event.
1156 // Copy what we currently have into first element, call self to
1157 // read remainder.
1158 // oh, and they BETTER NOT overlap.
1159 // Boy I hope this code works.
1160 // But I think this can never happen due to how inotify is written.
1161 niceassert( (long)((char *)&event[0] +
1162 sizeof(struct inotify_event) +
1163 event[0].len) <= (long)ret,
1164 "extremely unlucky user, death imminent" );
1165 // how much of the event do we have?
1166 bytes = (char *)&event[0] + bytes - (char *)ret;
1167 memcpy( &event[0], ret, bytes );
1168 return inotifytools_next_events( timeout, num_events );
1169 }
1170 RETURN(ret);
1171
1172 }
1173
1174 else if ( first_byte == 0 ) {
1175 bytes = 0;
1176 }
1177
1178
1179 static ssize_t this_bytes;
1180 static unsigned int bytes_to_read;
1181 static int rc;
1182 static fd_set read_fds;
1183
1184 static struct timeval read_timeout;
1185 read_timeout.tv_sec = timeout;
1186 read_timeout.tv_usec = 0;
1187 static struct timeval * read_timeout_ptr;
1188 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1189
1190 FD_ZERO(&read_fds);
1191 FD_SET(inotify_fd, &read_fds);
1192 rc = select(inotify_fd + 1, &read_fds,
1193 NULL, NULL, read_timeout_ptr);
1194 if ( rc < 0 ) {
1195 // error
1196 error = errno;
1197 return NULL;
1198 }
1199 else if ( rc == 0 ) {
1200 // timeout
1201 return NULL;
1202 }
1203
1204 // wait until we have enough bytes to read
1205 do {
1206 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1207 } while ( !rc &&
1208 bytes_to_read < sizeof(struct inotify_event)*num_events );
1209
1210 if ( rc == -1 ) {
1211 error = errno;
1212 return NULL;
1213 }
1214
1215 this_bytes = read(inotify_fd, &event[0] + bytes,
1216 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1217 if ( this_bytes < 0 ) {
1218 error = errno;
1219 return NULL;
1220 }
1221 if ( this_bytes == 0 ) {
1222 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1223 "events occurred at once.\n");
1224 return NULL;
1225 }
1226 bytes += this_bytes;
1227
1228 ret = &event[0];
1229 first_byte = sizeof(struct inotify_event) + ret->len;
1230 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1231 "almost certainly screw up." );
1232 if ( first_byte == bytes ) {
1233 first_byte = 0;
1234 }
1235
1236 RETURN(ret);
1237
1238 #undef RETURN
1239 }
1240
1266 int inotifytools_watch_recursively( char const * path, int events ) {
1267 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1268 }
1269
1302 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1303 char const ** exclude_list ) {
1304 niceassert( init, "inotifytools_initialize not called yet" );
1305
1306 DIR * dir;
1307 char * my_path;
1308 error = 0;
1309 dir = opendir( path );
1310 if ( !dir ) {
1311 // If not a directory, don't need to do anything special
1312 if ( errno == ENOTDIR ) {
1313 return inotifytools_watch_file( path, events );
1314 }
1315 else {
1316 error = errno;
1317 return 0;
1318 }
1319 }
1320
1321 if ( path[strlen(path)-1] != '/' ) {
1322 nasprintf( &my_path, "%s/", path );
1323 }
1324 else {
1325 my_path = (char *)path;
1326 }
1327
1328 static struct dirent * ent;
1329 char * next_file;
1330 static struct stat64 my_stat;
1331 ent = readdir( dir );
1332 // Watch each directory within this directory
1333 while ( ent ) {
1334 if ( (0 != strcmp( ent->d_name, "." )) &&
1335 (0 != strcmp( ent->d_name, ".." )) ) {
1336 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1337 if ( -1 == lstat64( next_file, &my_stat ) ) {
1338 error = errno;
1339 free( next_file );
1340 if ( errno != EACCES ) {
1341 error = errno;
1342 if ( my_path != path ) free( my_path );
1343 closedir( dir );
1344 return 0;
1345 }
1346 }
1347 else if ( S_ISDIR( my_stat.st_mode ) &&
1348 !S_ISLNK( my_stat.st_mode )) {
1349 free( next_file );
1350 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1351 static unsigned int no_watch;
1352 static char const ** exclude_entry;
1353
1354 no_watch = 0;
1355 for (exclude_entry = exclude_list;
1356 exclude_entry && *exclude_entry && !no_watch;
1357 ++exclude_entry) {
1358 static int exclude_length;
1359
1360 exclude_length = strlen(*exclude_entry);
1361 if ((*exclude_entry)[exclude_length-1] == '/') {
1362 --exclude_length;
1363 }
1364 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1365 !strncmp(*exclude_entry, next_file, exclude_length)) {
1366 // directory found in exclude list
1367 no_watch = 1;
1368 }
1369 }
1370 if (!no_watch) {
1371 static int status;
1372 status = inotifytools_watch_recursively_with_exclude(
1373 next_file,
1374 events,
1375 exclude_list );
1376 // For some errors, we will continue.
1377 if ( !status && (EACCES != error) && (ENOENT != error) &&
1378 (ELOOP != error) ) {
1379 free( next_file );
1380 if ( my_path != path ) free( my_path );
1381 closedir( dir );
1382 return 0;
1383 }
1384 } // if !no_watch
1385 free( next_file );
1386 } // if isdir and not islnk
1387 else {
1388 free( next_file );
1389 }
1390 }
1391 ent = readdir( dir );
1392 error = 0;
1393 }
1394
1395 closedir( dir );
1396
1397 int ret = inotifytools_watch_file( my_path, events );
1398 if ( my_path != path ) free( my_path );
1399 return ret;
1400 }
1401
1405 void record_stats( struct inotify_event const * event ) {
1406 if (!event) return;
1407 watch *w = watch_from_wd(event->wd);
1408 if (!w) return;
1409 if ( IN_ACCESS & event->mask ) {
1410 ++w->hit_access;
1411 ++num_access;
1412 }
1413 if ( IN_MODIFY & event->mask ) {
1414 ++w->hit_modify;
1415 ++num_modify;
1416 }
1417 if ( IN_ATTRIB & event->mask ) {
1418 ++w->hit_attrib;
1419 ++num_attrib;
1420 }
1421 if ( IN_CLOSE_WRITE & event->mask ) {
1422 ++w->hit_close_write;
1423 ++num_close_write;
1424 }
1425 if ( IN_CLOSE_NOWRITE & event->mask ) {
1426 ++w->hit_close_nowrite;
1427 ++num_close_nowrite;
1428 }
1429 if ( IN_OPEN & event->mask ) {
1430 ++w->hit_open;
1431 ++num_open;
1432 }
1433 if ( IN_MOVED_FROM & event->mask ) {
1434 ++w->hit_moved_from;
1435 ++num_moved_from;
1436 }
1437 if ( IN_MOVED_TO & event->mask ) {
1438 ++w->hit_moved_to;
1439 ++num_moved_to;
1440 }
1441 if ( IN_CREATE & event->mask ) {
1442 ++w->hit_create;
1443 ++num_create;
1444 }
1445 if ( IN_DELETE & event->mask ) {
1446 ++w->hit_delete;
1447 ++num_delete;
1448 }
1449 if ( IN_DELETE_SELF & event->mask ) {
1450 ++w->hit_delete_self;
1451 ++num_delete_self;
1452 }
1453 if ( IN_UNMOUNT & event->mask ) {
1454 ++w->hit_unmount;
1455 ++num_unmount;
1456 }
1457 if ( IN_MOVE_SELF & event->mask ) {
1458 ++w->hit_move_self;
1459 ++num_move_self;
1460 }
1461
1462 ++w->hit_total;
1463 ++num_total;
1464
1465 }
1466
1467 unsigned int *stat_ptr(watch *w, int event)
1468 {
1469 if ( IN_ACCESS == event )
1470 return &w->hit_access;
1471 if ( IN_MODIFY == event )
1472 return &w->hit_modify;
1473 if ( IN_ATTRIB == event )
1474 return &w->hit_attrib;
1475 if ( IN_CLOSE_WRITE == event )
1476 return &w->hit_close_write;
1477 if ( IN_CLOSE_NOWRITE == event )
1478 return &w->hit_close_nowrite;
1479 if ( IN_OPEN == event )
1480 return &w->hit_open;
1481 if ( IN_MOVED_FROM == event )
1482 return &w->hit_moved_from;
1483 if ( IN_MOVED_TO == event )
1484 return &w->hit_moved_to;
1485 if ( IN_CREATE == event )
1486 return &w->hit_create;
1487 if ( IN_DELETE == event )
1488 return &w->hit_delete;
1489 if ( IN_DELETE_SELF == event )
1490 return &w->hit_delete_self;
1491 if ( IN_UNMOUNT == event )
1492 return &w->hit_unmount;
1493 if ( IN_MOVE_SELF == event )
1494 return &w->hit_move_self;
1495 if ( 0 == event )
1496 return &w->hit_total;
1497 return 0;
1498 }
1499
1515 int inotifytools_get_stat_by_wd( int wd, int event ) {
1516 if (!collect_stats) return -1;
1517
1518 watch *w = watch_from_wd(wd);
1519 if (!w) return -1;
1520 unsigned int *i = stat_ptr(w, event);
1521 if (!i) return -1;
1522 return *i;
1523 }
1524
1538 int inotifytools_get_stat_total( int event ) {
1539 if (!collect_stats) return -1;
1540 if ( IN_ACCESS == event )
1541 return num_access;
1542 if ( IN_MODIFY == event )
1543 return num_modify;
1544 if ( IN_ATTRIB == event )
1545 return num_attrib;
1546 if ( IN_CLOSE_WRITE == event )
1547 return num_close_write;
1548 if ( IN_CLOSE_NOWRITE == event )
1549 return num_close_nowrite;
1550 if ( IN_OPEN == event )
1551 return num_open;
1552 if ( IN_MOVED_FROM == event )
1553 return num_moved_from;
1554 if ( IN_MOVED_TO == event )
1555 return num_moved_to;
1556 if ( IN_CREATE == event )
1557 return num_create;
1558 if ( IN_DELETE == event )
1559 return num_delete;
1560 if ( IN_DELETE_SELF == event )
1561 return num_delete_self;
1562 if ( IN_UNMOUNT == event )
1563 return num_unmount;
1564 if ( IN_MOVE_SELF == event )
1565 return num_move_self;
1566
1567 if ( 0 == event )
1568 return num_total;
1569
1570 return -1;
1571 }
1572
1592 int inotifytools_get_stat_by_filename( char const * filename,
1593 int event ) {
1594 return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
1595 filename ), event );
1596 }
1597
1608 int inotifytools_error() {
1609 return error;
1610 }
1611
1615 static int isdir( char const * path ) {
1616 static struct stat64 my_stat;
1617
1618 if ( -1 == lstat64( path, &my_stat ) ) {
1619 if (errno == ENOENT) return 0;
1620 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1621 return 0;
1622 }
1623
1624 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1625 }
1626
1627
1634 int inotifytools_get_num_watches() {
1635 int ret = 0;
1636 rbwalk(tree_filename, get_num, (void*)&ret);
1637 return ret;
1638 }
1639
1680 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1681 return inotifytools_fprintf( stdout, event, fmt );
1682 }
1683
1725 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1726 static char out[MAX_STRLEN+1];
1727 static int ret;
1728 ret = inotifytools_sprintf( out, event, fmt );
1729 if ( -1 != ret ) fprintf( file, "%s", out );
1730 return ret;
1731 }
1732
1783 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1784 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1785 }
1786
1787
1834 int inotifytools_snprintf( char * out, int size,
1835 struct inotify_event* event, char* fmt ) {
1836 static char * filename, * eventname, * eventstr;
1837 static unsigned int i, ind;
1838 static char ch1;
1839 static char timestr[MAX_STRLEN];
1840 static time_t now;
1841
1842
1843 if ( event->len > 0 ) {
1844 eventname = event->name;
1845 }
1846 else {
1847 eventname = NULL;
1848 }
1849
1850
1851 filename = inotifytools_filename_from_wd( event->wd );
1852
1853 if ( !fmt || 0 == strlen(fmt) ) {
1854 error = EINVAL;
1855 return -1;
1856 }
1857 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1858 error = EMSGSIZE;
1859 return -1;
1860 }
1861
1862 ind = 0;
1863 for ( i = 0; i < strlen(fmt) &&
1864 (int)ind < size - 1; ++i ) {
1865 if ( fmt[i] != '%' ) {
1866 out[ind++] = fmt[i];
1867 continue;
1868 }
1869
1870 if ( i == strlen(fmt) - 1 ) {
1871 // last character is %, invalid
1872 error = EINVAL;
1873 return ind;
1874 }
1875
1876 ch1 = fmt[i+1];
1877
1878 if ( ch1 == '%' ) {
1879 out[ind++] = '%';
1880 ++i;
1881 continue;
1882 }
1883
1884 if ( ch1 == 'w' ) {
1885 if ( filename ) {
1886 strncpy( &out[ind], filename, size - ind );
1887 ind += strlen(filename);
1888 }
1889 ++i;
1890 continue;
1891 }
1892
1893 if ( ch1 == 'f' ) {
1894 if ( eventname ) {
1895 strncpy( &out[ind], eventname, size - ind );
1896 ind += strlen(eventname);
1897 }
1898 ++i;
1899 continue;
1900 }
1901
1902 if ( ch1 == 'e' ) {
1903 eventstr = inotifytools_event_to_str( event->mask );
1904 strncpy( &out[ind], eventstr, size - ind );
1905 ind += strlen(eventstr);
1906 ++i;
1907 continue;
1908 }
1909
1910 if ( ch1 == 'T' ) {
1911
1912 if ( timefmt ) {
1913
1914 now = time(0);
1915 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1916 localtime( &now ) ) ) {
1917
1918 // time format probably invalid
1919 error = EINVAL;
1920 return ind;
1921 }
1922 }
1923 else {
1924 timestr[0] = 0;
1925 }
1926
1927 strncpy( &out[ind], timestr, size - ind );
1928 ind += strlen(timestr);
1929 ++i;
1930 continue;
1931 }
1932
1933 // Check if next char in fmt is e
1934 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1935 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1936 strncpy( &out[ind], eventstr, size - ind );
1937 ind += strlen(eventstr);
1938 i += 2;
1939 continue;
1940 }
1941
1942 // OK, this wasn't a special format character, just output it as normal
1943 if ( ind < MAX_STRLEN ) out[ind++] = '%';
1944 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1945 ++i;
1946 }
1947 out[ind] = 0;
1948
1949 return ind - 1;
1950 }
1951
1961 void inotifytools_set_printf_timefmt( char * fmt ) {
1962 timefmt = fmt;
1963 }
1964
1973 int inotifytools_get_max_queued_events() {
1974 int ret;
1975 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1976 return ret;
1977 }
1978
1988 int inotifytools_get_max_user_instances() {
1989 int ret;
1990 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1991 return ret;
1992 }
1993
2003 int inotifytools_get_max_user_watches() {
2004 int ret;
2005 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2006 return ret;
2007 }
2008
2022 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2023 if (!pattern) {
2024 if (regex) {
2025 regfree(regex);
2026 free(regex);
2027 regex = 0;
2028 }
2029 return 1;
2030 }
2031
2032 if (regex) { regfree(regex); }
2033 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2034
2035 invert_regexp = invert;
2036 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2037 if (0 == ret) return 1;
2038
2039 regfree(regex);
2040 free(regex);
2041 regex = 0;
2042 error = EINVAL;
2043 return 0;
2044 }
2045
2057 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2058 return do_ignore_events_by_regex(pattern, flags, 0);
2059 }
2060
2072 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2073 return do_ignore_events_by_regex(pattern, flags, 1);
2074 }
2075
2076 int event_compare(const void *p1, const void *p2, const void *config)
2077 {
2078 if (!p1 || !p2) return p1 - p2;
2079 char asc = 1;
2080 int sort_event = *(int*)config;
2081 if (sort_event == -1) {
2082 sort_event = 0;
2083 asc = 0;
2084 } else if (sort_event < 0) {
2085 sort_event = -sort_event;
2086 asc = 0;
2087 }
2088 unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2089 unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2090 if (0 == *i1 - *i2) {
2091 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2092 }
2093 if (asc)
2094 return *i1 - *i2;
2095 else
2096 return *i2 - *i1;
2097 }
2098
2099 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2100 {
2101 struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2102 RBLIST *all = rbopenlist(tree_wd);
2103 void const *p = rbreadlist(all);
2104 while (p) {
2105 void const *r = rbsearch(p, ret);
2106 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2107 p = rbreadlist(all);
2108 }
2109 rbcloselist(all);
2110 return ret;
2111 }
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:924
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1108
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1592
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1961
inotifytools_sprintf
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1783
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:670
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:1302
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1680
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:2003
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:843
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1725
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1538
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:821
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1634
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1515
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:642
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:970
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1608
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:800
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:555
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:992
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2057
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:947
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:873
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1055
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:777
inotifytools_snprintf
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1834
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:2072
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1266
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:1973
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:1988