libinotifytools
inotifytools.c
1// kate: replace-tabs off; space-indent off;
2
15#include "../../config.h"
17#include "inotifytools_p.h"
18#include "stats.h"
19
20#include <dirent.h>
21#include <errno.h>
22#include <limits.h>
23#include <regex.h>
24#include <setjmp.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <strings.h>
30#include <sys/ioctl.h>
31#include <sys/select.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <time.h>
35#include <unistd.h>
36
37#include "inotifytools/inotify.h"
38
39#ifdef __FreeBSD__
40struct fanotify_event_fid;
41
42#define FAN_EVENT_INFO_TYPE_FID 1
43#define FAN_EVENT_INFO_TYPE_DFID_NAME 2
44#define FAN_EVENT_INFO_TYPE_DFID 3
45
46#else
47// Linux only
48#define LINUX_FANOTIFY
49
50#include <fcntl.h>
51#include <sys/vfs.h>
52#include "inotifytools/fanotify.h"
53
54struct fanotify_event_fid {
55 struct fanotify_event_info_fid info;
56 struct file_handle handle;
57};
58#endif
59
146#define MAX_EVENTS 4096
147#define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
148#define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
149#define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
150#define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
151
152static int inotify_fd = -1;
153
154int collect_stats = 0;
155
156struct rbtree *tree_wd = 0;
157struct rbtree* tree_fid = 0;
158struct rbtree *tree_filename = 0;
159static int error = 0;
160int init = 0;
161int verbosity = 0;
162int fanotify_mode = 0;
163int fanotify_mark_type = 0;
164static pid_t self_pid = 0;
165static char* timefmt = 0;
166static regex_t* regex = 0;
167/* 0: --exclude[i], 1: --include[i] */
168static int invert_regexp = 0;
169
170static int isdir( char const * path );
171void record_stats( struct inotify_event const * event );
172int onestr_to_event(char const * event);
173
174#define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
175
193void _niceassert( long cond, int line, char const * file,
194 char const * condstr, char const * mesg ) {
195 if ( cond ) return;
196
197 if ( mesg ) {
198 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
199 condstr, mesg );
200 }
201 else {
202 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
203 }
204}
205
206static void charcat(char* s, const char c) {
207 size_t l = strlen(s);
208 s[l] = c;
209 s[++l] = 0;
210}
211
215static int read_num_from_file(char* filename, int* num) {
216 FILE * file = fopen( filename, "r" );
217 if ( !file ) {
218 error = errno;
219 return 0;
220 }
221
222 if ( EOF == fscanf( file, "%d", num ) ) {
223 error = errno;
224 const int fclose_ret = fclose(file);
225 niceassert(!fclose_ret, 0);
226 return 0;
227 }
228
229 const int fclose_ret = fclose(file);
230 niceassert(!fclose_ret, 0);
231
232 return 1;
233}
234
235static int wd_compare(const void* d1, const void* d2, const void* config) {
236 if (!d1 || !d2) return d1 - d2;
237 return ((watch*)d1)->wd - ((watch*)d2)->wd;
238}
239
240static int fid_compare(const void* d1, const void* d2, const void* config) {
241#ifdef LINUX_FANOTIFY
242 if (!d1 || !d2)
243 return d1 - d2;
244 watch* w1 = (watch*)d1;
245 watch* w2 = (watch*)d2;
246 int n1, n2;
247 n1 = w1->fid->info.hdr.len;
248 n2 = w2->fid->info.hdr.len;
249 if (n1 != n2)
250 return n1 - n2;
251 return memcmp(w1->fid, w2->fid, n1);
252#else
253 return d1 - d2;
254#endif
255}
256
257static int filename_compare(const void* d1,
258 const void* d2,
259 const void* config) {
260 if (!d1 || !d2) return d1 - d2;
261 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
262}
263
267watch *watch_from_wd( int wd ) {
268 watch w;
269 w.wd = wd;
270 return (watch*)rbfind(&w, tree_wd);
271}
272
276watch* watch_from_fid(struct fanotify_event_fid* fid) {
277 watch w;
278 w.fid = fid;
279 return (watch*)rbfind(&w, tree_fid);
280}
281
285watch *watch_from_filename( char const *filename ) {
286 watch w;
287 w.filename = (char*)filename;
288 return (watch*)rbfind(&w, tree_filename);
289}
290
301int inotifytools_init(int fanotify, int watch_filesystem, int verbose) {
302 if (init) return 1;
303
304 error = 0;
305 verbosity = verbose;
306 // Try to initialise inotify/fanotify
307 if (fanotify) {
308#ifdef LINUX_FANOTIFY
309 self_pid = getpid();
310 fanotify_mode = 1;
311 fanotify_mark_type =
312 watch_filesystem ? FAN_MARK_FILESYSTEM : FAN_MARK_INODE;
313 inotify_fd =
314 fanotify_init(FAN_REPORT_FID | FAN_REPORT_DFID_NAME, 0);
315#endif
316 } else {
317 fanotify_mode = 0;
318 inotify_fd = inotify_init();
319 }
320 if (inotify_fd < 0) {
321 error = errno;
322 return 0;
323 }
324
325 collect_stats = 0;
326 init = 1;
327 tree_wd = rbinit(wd_compare, 0);
328 tree_fid = rbinit(fid_compare, 0);
329 tree_filename = rbinit(filename_compare, 0);
330 timefmt = 0;
331
332 return 1;
333}
334
335int inotifytools_initialize() {
336 return inotifytools_init(0, 0, 0);
337}
338
342void destroy_watch(watch *w) {
343 if (w->filename) free(w->filename);
344 if (w->fid)
345 free(w->fid);
346 if (w->dirf)
347 close(w->dirf);
348 free(w);
349}
350
354void cleanup_tree(const void *nodep,
355 const VISIT which,
356 const int depth, void* arg) {
357 if (which != endorder && which != leaf) return;
358 watch *w = (watch*)nodep;
359 destroy_watch(w);
360}
361
369 if (!init) return;
370
371 init = 0;
372 close(inotify_fd);
373 collect_stats = 0;
374 error = 0;
375 timefmt = 0;
376
377 if (regex) {
378 regfree(regex);
379 free(regex);
380 regex = 0;
381 }
382
383 rbwalk(tree_wd, cleanup_tree, 0);
384 rbdestroy(tree_wd);
385 rbdestroy(tree_fid);
386 rbdestroy(tree_filename);
387 tree_wd = 0;
388 tree_fid = 0;
389 tree_filename = 0;
390}
391
392
393
397struct replace_filename_data {
398 char const *old_name;
399 char const *new_name;
400 size_t old_len;
401};
402
406static void replace_filename(const void* nodep,
407 const VISIT which,
408 const int depth,
409 const struct replace_filename_data* data) {
410 if (which != endorder && which != leaf)
411 return;
412 watch *w = (watch*)nodep;
413 char *name;
414 if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
415 nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
416 if (!strcmp( w->filename, data->new_name )) {
417 free(name);
418 } else {
419 rbdelete(w, tree_filename);
420 free( w->filename );
421 w->filename = name;
422 rbsearch(w, tree_filename);
423 }
424 }
425}
426
430static void get_num(const void* nodep,
431 const VISIT which,
432 const int depth,
433 void* arg) {
434 if (which != endorder && which != leaf)
435 return;
436 ++(*((int*)arg));
437}
438
466int inotifytools_str_to_event_sep(char const * event, char sep) {
467 if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
468 "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
469 return -1;
470 }
471
472 int ret, len;
473 char * event1, * event2;
474 static const size_t eventstr_size = 4096;
475 char eventstr[eventstr_size];
476 ret = 0;
477
478 if ( !event || !event[0] ) return 0;
479
480 event1 = (char *)event;
481 event2 = strchr( event1, sep );
482 while ( event1 && event1[0] ) {
483 if ( event2 ) {
484 len = event2 - event1;
485 niceassert(len < eventstr_size,
486 "malformed event string (very long)");
487 }
488 else {
489 len = strlen(event1);
490 }
491 if (len > eventstr_size - 1)
492 len = eventstr_size - 1;
493
494 strncpy(eventstr, event1, len);
495
496 eventstr[len] = 0;
497
498 int ret1 = onestr_to_event(eventstr);
499 if ( 0 == ret1 || -1 == ret1 ) {
500 ret = ret1;
501 break;
502 }
503 ret |= ret1;
504
505 event1 = event2;
506 if ( event1 && event1[0] ) {
507 // jump over 'sep' character
508 ++event1;
509 // if last character was 'sep'...
510 if ( !event1[0] ) return 0;
511 event2 = strchr( event1, sep );
512 }
513 }
514
515 return ret;
516}
517
541int inotifytools_str_to_event(char const * event) {
542 return inotifytools_str_to_event_sep( event, ',' );
543}
544
556int onestr_to_event(char const * event)
557{
558 static int ret;
559 ret = -1;
560
561 if ( !event || !event[0] )
562 ret = 0;
563 else if ( 0 == strcasecmp(event, "ACCESS") )
564 ret = IN_ACCESS;
565 else if ( 0 == strcasecmp(event, "MODIFY") )
566 ret = IN_MODIFY;
567 else if ( 0 == strcasecmp(event, "ATTRIB") )
568 ret = IN_ATTRIB;
569 else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
570 ret = IN_CLOSE_WRITE;
571 else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
572 ret = IN_CLOSE_NOWRITE;
573 else if ( 0 == strcasecmp(event, "OPEN") )
574 ret = IN_OPEN;
575 else if ( 0 == strcasecmp(event, "MOVED_FROM") )
576 ret = IN_MOVED_FROM;
577 else if ( 0 == strcasecmp(event, "MOVED_TO") )
578 ret = IN_MOVED_TO;
579 else if ( 0 == strcasecmp(event, "CREATE") )
580 ret = IN_CREATE;
581 else if ( 0 == strcasecmp(event, "DELETE") )
582 ret = IN_DELETE;
583 else if ( 0 == strcasecmp(event, "DELETE_SELF") )
584 ret = IN_DELETE_SELF;
585 else if ( 0 == strcasecmp(event, "UNMOUNT") )
586 ret = IN_UNMOUNT;
587 else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
588 ret = IN_Q_OVERFLOW;
589 else if ( 0 == strcasecmp(event, "IGNORED") )
590 ret = IN_IGNORED;
591 else if ( 0 == strcasecmp(event, "CLOSE") )
592 ret = IN_CLOSE;
593 else if ( 0 == strcasecmp(event, "MOVE_SELF") )
594 ret = IN_MOVE_SELF;
595 else if ( 0 == strcasecmp(event, "MOVE") )
596 ret = IN_MOVE;
597 else if ( 0 == strcasecmp(event, "ISDIR") )
598 ret = IN_ISDIR;
599 else if ( 0 == strcasecmp(event, "ONESHOT") )
600 ret = IN_ONESHOT;
601 else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
602 ret = IN_ALL_EVENTS;
603
604 return ret;
605}
606
628char * inotifytools_event_to_str(int events) {
629 return inotifytools_event_to_str_sep(events, ',');
630}
631
656char * inotifytools_event_to_str_sep(int events, char sep)
657{
658 static char ret[1024];
659 ret[0] = '\0';
660 ret[1] = '\0';
661
662 if ( IN_ACCESS & events ) {
663 charcat(ret, sep);
664 strncat(ret, "ACCESS", 7);
665 }
666 if ( IN_MODIFY & events ) {
667 charcat(ret, sep);
668 strncat(ret, "MODIFY", 7);
669 }
670 if ( IN_ATTRIB & events ) {
671 charcat(ret, sep);
672 strncat(ret, "ATTRIB", 7);
673 }
674 if ( IN_CLOSE_WRITE & events ) {
675 charcat(ret, sep);
676 strncat(ret, "CLOSE_WRITE", 12);
677 }
678 if ( IN_CLOSE_NOWRITE & events ) {
679 charcat(ret, sep);
680 strncat(ret, "CLOSE_NOWRITE", 14);
681 }
682 if ( IN_OPEN & events ) {
683 charcat(ret, sep);
684 strncat(ret, "OPEN", 5);
685 }
686 if ( IN_MOVED_FROM & events ) {
687 charcat(ret, sep);
688 strncat(ret, "MOVED_FROM", 11);
689 }
690 if ( IN_MOVED_TO & events ) {
691 charcat(ret, sep);
692 strncat(ret, "MOVED_TO", 9);
693 }
694 if ( IN_CREATE & events ) {
695 charcat(ret, sep);
696 strncat(ret, "CREATE", 7);
697 }
698 if ( IN_DELETE & events ) {
699 charcat(ret, sep);
700 strncat(ret, "DELETE", 7);
701 }
702 if ( IN_DELETE_SELF & events ) {
703 charcat(ret, sep);
704 strncat(ret, "DELETE_SELF", 12);
705 }
706 if ( IN_UNMOUNT & events ) {
707 charcat(ret, sep);
708 strncat(ret, "UNMOUNT", 8);
709 }
710 if ( IN_Q_OVERFLOW & events ) {
711 charcat(ret, sep);
712 strncat(ret, "Q_OVERFLOW", 11);
713 }
714 if ( IN_IGNORED & events ) {
715 charcat(ret, sep);
716 strncat(ret, "IGNORED", 8);
717 }
718 if ( IN_CLOSE & events ) {
719 charcat(ret, sep);
720 strncat(ret, "CLOSE", 6);
721 }
722 if ( IN_MOVE_SELF & events ) {
723 charcat(ret, sep);
724 strncat(ret, "MOVE_SELF", 10);
725 }
726 if ( IN_ISDIR & events ) {
727 charcat(ret, sep);
728 strncat(ret, "ISDIR", 6);
729 }
730 if ( IN_ONESHOT & events ) {
731 charcat(ret, sep);
732 strncat(ret, "ONESHOT", 8);
733 }
734
735 // Maybe we didn't match any... ?
736 if (ret[0] == '\0') {
737 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
738 }
739
740 return &ret[1];
741}
742
749static const char* inotifytools_filename_from_fid(
750 struct fanotify_event_fid* fid) {
751#ifdef LINUX_FANOTIFY
752 static char filename[PATH_MAX];
753 struct fanotify_event_fid fsid = {};
754 int dirf = 0, mount_fd = AT_FDCWD;
755 int len = 0, name_len = 0;
756
757 // Match mount_fd from fid->fsid (and null fhandle)
758 fsid.info.fsid.val[0] = fid->info.fsid.val[0];
759 fsid.info.fsid.val[1] = fid->info.fsid.val[1];
760 fsid.info.hdr.info_type = FAN_EVENT_INFO_TYPE_FID;
761 fsid.info.hdr.len = sizeof(fsid);
762 watch* mnt = watch_from_fid(&fsid);
763 if (mnt)
764 mount_fd = mnt->dirf;
765
766 if (fid->info.hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
767 int fid_len = sizeof(*fid) + fid->handle.handle_bytes;
768
769 name_len = fid->info.hdr.len - fid_len;
770 if (name_len && !fid->handle.f_handle[fid->handle.handle_bytes])
771 name_len = 0; // empty name??
772 }
773
774 // Try to get path from file handle
775 dirf = open_by_handle_at(mount_fd, &fid->handle, 0);
776 if (dirf > 0) {
777 // Got path by handle
778 } else if (fanotify_mark_type == FAN_MARK_FILESYSTEM) {
779 fprintf(stderr, "Failed to decode directory fid.\n");
780 return NULL;
781 } else if (name_len) {
782 // For recursive watch look for watch by fid without the name
783 fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID;
784 fid->info.hdr.len -= name_len;
785
786 watch* w = watch_from_fid(fid);
787
788 fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
789 fid->info.hdr.len += name_len;
790
791 if (!w) {
792 fprintf(stderr,
793 "Failed to lookup path by directory fid.\n");
794 return NULL;
795 }
796
797 dirf = w->dirf ? dup(w->dirf) : -1;
798 if (dirf < 0) {
799 fprintf(stderr, "Failed to get directory fd.\n");
800 return NULL;
801 }
802 } else {
803 // Fallthrough to stored filename
804 return NULL;
805 }
806 char sym[30];
807 sprintf(sym, "/proc/self/fd/%d", dirf);
808
809 // PATH_MAX - 2 because we have to append two characters to this path,
810 // '/' and 0
811 len = readlink(sym, filename, PATH_MAX - 2);
812 if (len < 0) {
813 close(dirf);
814 fprintf(stderr, "Failed to resolve path from directory fd.\n");
815 return NULL;
816 }
817
818 filename[len++] = '/';
819 filename[len] = 0;
820
821 if (name_len > 0) {
822 const char* name = (const char*)fid->handle.f_handle +
823 fid->handle.handle_bytes;
824 int deleted = faccessat(dirf, name, F_OK, AT_SYMLINK_NOFOLLOW);
825 if (deleted && errno != ENOENT) {
826 fprintf(stderr, "Failed to access file %s (%s).\n",
827 name, strerror(errno));
828 close(dirf);
829 return NULL;
830 }
831 memcpy(filename + len, name, name_len);
832 if (deleted)
833 strncat(filename, " (deleted)", 11);
834 }
835 close(dirf);
836 return filename;
837#else
838 return NULL;
839#endif
840}
841
848const char* inotifytools_filename_from_watch(watch* w) {
849 if (!w)
850 return "";
851 if (!w->fid || !fanotify_mark_type)
852 return w->filename;
853
854 return inotifytools_filename_from_fid(w->fid) ?: w->filename;
855}
856
877const char* inotifytools_filename_from_wd(int wd) {
878 niceassert( init, "inotifytools_initialize not called yet" );
879 if (!wd)
880 return "";
881 watch *w = watch_from_wd(wd);
882 if (!w)
883 return "";
884
886}
887
896const char* inotifytools_dirname_from_event(struct inotify_event* event,
897 size_t* dirnamelen) {
898 const char* filename = inotifytools_filename_from_wd(event->wd);
899 const char* dirsep = NULL;
900
901 if (!filename) {
902 return NULL;
903 }
904
905 /* Split dirname from filename for fanotify event */
906 if (fanotify_mode)
907 dirsep = strrchr(filename, '/');
908 if (!dirsep) {
909 *dirnamelen = strlen(filename);
910 return filename;
911 }
912
913 *dirnamelen = dirsep - filename + 1;
914 return filename;
915}
916
925const char* inotifytools_filename_from_event(struct inotify_event* event,
926 char const** eventname,
927 size_t* dirnamelen) {
928 if (event->len > 0)
929 *eventname = event->name;
930 else
931 *eventname = "";
932
933 const char* filename =
934 inotifytools_dirname_from_event(event, dirnamelen);
935
936 /* On fanotify watch, filename includes event->name */
937 if (filename && filename[*dirnamelen])
938 *eventname = filename + *dirnamelen;
939
940 return filename;
941}
942
951char* inotifytools_dirpath_from_event(struct inotify_event* event) {
952 const char* filename = inotifytools_filename_from_wd(event->wd);
953
954 if (!filename || !*filename || !(event->mask & IN_ISDIR)) {
955 return NULL;
956 }
957
958 /*
959 * fanotify watch->filename includes the name, so no need to add the
960 * event->name again.
961 */
962 char* path;
963 nasprintf(&path, "%s%s/", filename, fanotify_mode ? "" : event->name);
964
965 return path;
966}
967
982int inotifytools_wd_from_filename( char const * filename ) {
983 niceassert( init, "inotifytools_initialize not called yet" );
984 if (!filename || !*filename)
985 return -1;
986 watch *w = watch_from_filename(filename);
987 if (!w) return -1;
988 return w->wd;
989}
990
1005void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
1006 niceassert( init, "inotifytools_initialize not called yet" );
1007 watch *w = watch_from_wd(wd);
1008 if (!w) return;
1009 if (w->filename) free(w->filename);
1010 w->filename = strdup(filename);
1011}
1012
1027void inotifytools_set_filename_by_filename( char const * oldname,
1028 char const * newname ) {
1029 watch *w = watch_from_filename(oldname);
1030 if (!w) return;
1031 if (w->filename) free(w->filename);
1032 w->filename = strdup(newname);
1033}
1034
1057void inotifytools_replace_filename( char const * oldname,
1058 char const * newname ) {
1059 if (!oldname || !newname)
1060 return;
1061 if (!*oldname || !*newname)
1062 return;
1063 struct replace_filename_data data;
1064 data.old_name = oldname;
1065 data.new_name = newname;
1066 data.old_len = strlen(oldname);
1067 rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
1068}
1069
1073int remove_inotify_watch(watch *w) {
1074 error = 0;
1075 // There is no kernel object representing the watch with fanotify
1076 if (w->fid)
1077 return 0;
1078 int status = inotify_rm_watch( inotify_fd, w->wd );
1079 if ( status < 0 ) {
1080 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
1081 strerror(status) );
1082 error = status;
1083 return 0;
1084 }
1085 return 1;
1086}
1087
1091watch* create_watch(int wd,
1092 struct fanotify_event_fid* fid,
1093 const char* filename,
1094 int dirf) {
1095 if (wd < 0 || !filename)
1096 return 0;
1097
1098 watch *w = (watch*)calloc(1, sizeof(watch));
1099 if (!w) {
1100 fprintf(stderr, "Failed to allocate watch.\n");
1101 return NULL;
1102 }
1103 w->wd = wd ?: (unsigned long)fid;
1104 w->fid = fid;
1105 w->dirf = dirf;
1106 w->filename = strdup(filename);
1107 rbsearch(w, tree_wd);
1108 if (fid)
1109 rbsearch(w, tree_fid);
1110
1111 rbsearch(w, tree_filename);
1112 return w;
1113}
1114
1128 niceassert( init, "inotifytools_initialize not called yet" );
1129 watch *w = watch_from_wd(wd);
1130 if (!w) return 1;
1131
1132 if (!remove_inotify_watch(w)) return 0;
1133 rbdelete(w, tree_wd);
1134 if (w->fid)
1135 rbdelete(w, tree_fid);
1136 rbdelete(w, tree_filename);
1137 destroy_watch(w);
1138 return 1;
1139}
1140
1152int inotifytools_remove_watch_by_filename( char const * filename ) {
1153 niceassert( init, "inotifytools_initialize not called yet" );
1154 watch *w = watch_from_filename(filename);
1155 if (!w) return 1;
1156
1157 if (!remove_inotify_watch(w)) return 0;
1158 rbdelete(w, tree_wd);
1159 if (w->fid)
1160 rbdelete(w, tree_fid);
1161 rbdelete(w, tree_filename);
1162 destroy_watch(w);
1163 return 1;
1164}
1165
1177int inotifytools_watch_file(char const* filename, int events) {
1178 static char const* filenames[2];
1179 filenames[0] = filename;
1180 filenames[1] = NULL;
1181 return inotifytools_watch_files( filenames, events );
1182}
1183
1199int inotifytools_watch_files(char const* filenames[], int events) {
1200 niceassert( init, "inotifytools_initialize not called yet" );
1201 error = 0;
1202
1203 static int i;
1204 for ( i = 0; filenames[i]; ++i ) {
1205 int wd = -1;
1206 if (fanotify_mode) {
1207#ifdef LINUX_FANOTIFY
1208 unsigned int flags = FAN_MARK_ADD | fanotify_mark_type;
1209
1210 if (events & IN_DONT_FOLLOW) {
1211 events &= ~IN_DONT_FOLLOW;
1212 flags |= FAN_MARK_DONT_FOLLOW;
1213 }
1214
1215 wd = fanotify_mark(inotify_fd, flags,
1216 events | FAN_EVENT_ON_CHILD,
1217 AT_FDCWD, filenames[i]);
1218#endif
1219 } else {
1220 wd =
1221 inotify_add_watch(inotify_fd, filenames[i], events);
1222 }
1223 if ( wd < 0 ) {
1224 if ( wd == -1 ) {
1225 error = errno;
1226 return 0;
1227 } // if ( wd == -1 )
1228 else {
1229 fprintf( stderr, "Failed to watch %s: returned wd was %d "
1230 "(expected -1 or >0 )", filenames[i], wd );
1231 // no appropriate value for error
1232 return 0;
1233 } // else
1234 } // if ( wd < 0 )
1235
1236 const char* filename = filenames[i];
1237 size_t filenamelen = strlen(filename);
1238 char* dirname;
1239 int dirf = 0;
1240 // Always end filename with / if it is a directory
1241 if (!isdir(filename)) {
1242 dirname = NULL;
1243 } else if (filename[filenamelen - 1] == '/') {
1244 dirname = strdup(filename);
1245 } else {
1246 nasprintf(&dirname, "%s/", filename);
1247 filename = dirname;
1248 filenamelen++;
1249 }
1250
1251 struct fanotify_event_fid* fid = NULL;
1252#ifdef LINUX_FANOTIFY
1253 if (!wd) {
1254 fid = calloc(1, sizeof(*fid) + MAX_FID_LEN);
1255 if (!fid) {
1256 fprintf(stderr, "Failed to allocate fid");
1257 free(dirname);
1258 return 0;
1259 }
1260
1261 struct statfs buf;
1262 if (statfs(filenames[i], &buf)) {
1263 free(fid);
1264 fprintf(stderr, "Statfs failed on %s: %s\n",
1265 filenames[i], strerror(errno));
1266 free(dirname);
1267 return 0;
1268 }
1269 memcpy(&fid->info.fsid, &buf.f_fsid,
1270 sizeof(__kernel_fsid_t));
1271
1272 // Hash mount_fd with fid->fsid (and null fhandle)
1273 int ret, mntid;
1274 watch* mnt = dirname ? watch_from_fid(fid) : NULL;
1275 if (dirname && !mnt) {
1276 struct fanotify_event_fid* fsid;
1277
1278 fsid = calloc(1, sizeof(*fsid));
1279 if (!fsid) {
1280 free(fid);
1281 fprintf(stderr,
1282 "Failed to allocate fsid");
1283 free(dirname);
1284 return 0;
1285 }
1286 fsid->info.fsid.val[0] = fid->info.fsid.val[0];
1287 fsid->info.fsid.val[1] = fid->info.fsid.val[1];
1288 fsid->info.hdr.info_type =
1289 FAN_EVENT_INFO_TYPE_FID;
1290 fsid->info.hdr.len = sizeof(*fsid);
1291 mntid = open(dirname, O_RDONLY);
1292 if (mntid < 0) {
1293 free(fid);
1294 free(fsid);
1295 fprintf(stderr,
1296 "Failed to open %s: %s\n",
1297 dirname, strerror(errno));
1298 free(dirname);
1299 return 0;
1300 }
1301 // Hash mount_fd without terminating /
1302 dirname[filenamelen - 1] = 0;
1303 create_watch(0, fsid, dirname, mntid);
1304 dirname[filenamelen - 1] = '/';
1305 }
1306
1307 fid->handle.handle_bytes = MAX_FID_LEN;
1308 ret = name_to_handle_at(AT_FDCWD, filenames[i],
1309 (void*)&fid->handle, &mntid, 0);
1310 if (ret || fid->handle.handle_bytes > MAX_FID_LEN) {
1311 free(fid);
1312 fprintf(stderr, "Encode fid failed on %s: %s\n",
1313 filenames[i], strerror(errno));
1314 free(dirname);
1315 return 0;
1316 }
1317 fid->info.hdr.info_type = dirname
1318 ? FAN_EVENT_INFO_TYPE_DFID
1319 : FAN_EVENT_INFO_TYPE_FID;
1320 fid->info.hdr.len =
1321 sizeof(*fid) + fid->handle.handle_bytes;
1322 if (dirname) {
1323 dirf = open(dirname, O_PATH);
1324 if (dirf < 0) {
1325 free(fid);
1326 fprintf(stderr,
1327 "Failed to open %s: %s\n",
1328 dirname, strerror(errno));
1329 free(dirname);
1330 return 0;
1331 }
1332 }
1333 }
1334#endif
1335 create_watch(wd, fid, filename, dirf);
1336 free(dirname);
1337 } // for
1338
1339 return 1;
1340}
1341
1368struct inotify_event * inotifytools_next_event( long int timeout ) {
1369 if (!timeout) {
1370 timeout = -1;
1371 }
1372
1373 return inotifytools_next_events( timeout, 1 );
1374}
1375
1376
1425struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1426 niceassert( init, "inotifytools_initialize not called yet" );
1427 niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1428
1429 if ( num_events < 1 ) return NULL;
1430
1431 // second half of event[] buffer is for fanotify->inotify conversion
1432 static struct inotify_event event[2 * MAX_EVENTS];
1433 static struct inotify_event * ret;
1434 static int first_byte = 0;
1435 static ssize_t bytes;
1436 static ssize_t this_bytes;
1437 static jmp_buf jmp;
1438 static struct nstring match_name;
1439 static char match_name_string[MAX_STRLEN+1];
1440
1441 setjmp(jmp);
1442
1443 pid_t event_pid = 0;
1444 error = 0;
1445
1446 // first_byte is index into event buffer
1447 if ( first_byte != 0
1448 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1449
1450 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1451 if (!fanotify_mode &&
1452 first_byte + sizeof(*ret) + ret->len > bytes) {
1453 // oh... no. this can't be happening. An incomplete event.
1454 // Copy what we currently have into first element, call self to
1455 // read remainder.
1456 // oh, and they BETTER NOT overlap.
1457 // Boy I hope this code works.
1458 // But I think this can never happen due to how inotify is written.
1459 niceassert( (long)((char *)&event[0] +
1460 sizeof(struct inotify_event) +
1461 event[0].len) <= (long)ret,
1462 "extremely unlucky user, death imminent" );
1463 // how much of the event do we have?
1464 bytes = (char *)&event[0] + bytes - (char *)ret;
1465 memcpy( &event[0], ret, bytes );
1466 return inotifytools_next_events( timeout, num_events );
1467 }
1468 this_bytes = 0;
1469 goto more_events;
1470
1471 }
1472
1473 else if ( first_byte == 0 ) {
1474 bytes = 0;
1475 }
1476
1477
1478 static unsigned int bytes_to_read;
1479 static int rc;
1480 static fd_set read_fds;
1481
1482 static struct timeval read_timeout;
1483 read_timeout.tv_sec = timeout;
1484 read_timeout.tv_usec = 0;
1485 static struct timeval * read_timeout_ptr;
1486 read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1487
1488 FD_ZERO(&read_fds);
1489 FD_SET(inotify_fd, &read_fds);
1490 rc = select(inotify_fd + 1, &read_fds,
1491 NULL, NULL, read_timeout_ptr);
1492 if ( rc < 0 ) {
1493 // error
1494 error = errno;
1495 return NULL;
1496 }
1497 else if ( rc == 0 ) {
1498 // timeout
1499 return NULL;
1500 }
1501
1502 // wait until we have enough bytes to read
1503 do {
1504 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1505 } while ( !rc &&
1506 bytes_to_read < sizeof(struct inotify_event)*num_events );
1507
1508 if ( rc == -1 ) {
1509 error = errno;
1510 return NULL;
1511 }
1512
1513 this_bytes = read(inotify_fd, &event[0] + bytes,
1514 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1515 if ( this_bytes < 0 ) {
1516 error = errno;
1517 return NULL;
1518 }
1519 if ( this_bytes == 0 ) {
1520 fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1521 "events occurred at once.\n");
1522 return NULL;
1523 }
1524more_events:
1525 ret = (struct inotify_event*)((char*)&event[0] + first_byte);
1526#ifdef LINUX_FANOTIFY
1527 // convert fanotify events to inotify events
1528 if (fanotify_mode) {
1529 struct fanotify_event_metadata* meta = (void*)ret;
1530 struct fanotify_event_info_fid* info = (void*)(meta + 1);
1531 struct fanotify_event_fid* fid = NULL;
1532 const char* name = "";
1533 int fid_len = 0;
1534 int name_len = 0;
1535
1536 first_byte += meta->event_len;
1537
1538 if (meta->event_len > sizeof(*meta)) {
1539 switch (info->hdr.info_type) {
1540 case FAN_EVENT_INFO_TYPE_FID:
1541 case FAN_EVENT_INFO_TYPE_DFID:
1542 case FAN_EVENT_INFO_TYPE_DFID_NAME:
1543 fid = (void*)info;
1544 fid_len = sizeof(*fid) +
1545 fid->handle.handle_bytes;
1546 if (info->hdr.info_type ==
1547 FAN_EVENT_INFO_TYPE_DFID_NAME) {
1548 name_len =
1549 info->hdr.len - fid_len;
1550 }
1551 if (name_len > 0) {
1552 name =
1553 (const char*)
1554 fid->handle.f_handle +
1555 fid->handle.handle_bytes;
1556 }
1557 // Convert zero padding to zero
1558 // name_len. For some events on
1559 // directories, the fid is that of the
1560 // dir and name is ".". Do not include
1561 // "." name in fid hash, but keep it for
1562 // debug print.
1563 if (name_len &&
1564 (!*name || !strcmp(name, "."))) {
1565 info->hdr.len -= name_len;
1566 name_len = 0;
1567 }
1568 break;
1569 }
1570 }
1571 if (!fid) {
1572 fprintf(stderr, "No fid in fanotify event.\n");
1573 return NULL;
1574 }
1575 if (verbosity > 1) {
1576 printf(
1577 "fanotify_event: bytes=%zd, first_byte=%d, "
1578 "this_bytes=%zd, event_len=%u, fid_len=%d, "
1579 "name_len=%d, name=%s\n",
1580 bytes, first_byte, this_bytes, meta->event_len,
1581 fid_len, name_len, name);
1582 }
1583
1584 ret = &event[MAX_EVENTS];
1585 watch* w = watch_from_fid(fid);
1586 if (!w) {
1587 struct fanotify_event_fid* newfid =
1588 calloc(1, info->hdr.len);
1589 if (!newfid) {
1590 fprintf(stderr, "Failed to allocate fid.\n");
1591 return NULL;
1592 }
1593 memcpy(newfid, fid, info->hdr.len);
1594 const char* filename =
1595 inotifytools_filename_from_fid(fid);
1596 if (filename) {
1597 w = create_watch(0, newfid, filename, 0);
1598 if (!w) {
1599 free(newfid);
1600 return NULL;
1601 }
1602 }
1603
1604 if (verbosity) {
1605 unsigned long id;
1606 memcpy((void*)&id, fid->handle.f_handle,
1607 sizeof(id));
1608 printf("[fid=%x.%x.%lx;name='%s'] %s\n",
1609 fid->info.fsid.val[0],
1610 fid->info.fsid.val[1], id, name,
1611 filename ?: "");
1612 }
1613 }
1614 ret->wd = w ? w->wd : 0;
1615 ret->mask = (uint32_t)meta->mask;
1616 ret->len = name_len;
1617 if (name_len > 0)
1618 memcpy(ret->name, name, name_len);
1619 event_pid = meta->pid;
1620 } else {
1621 first_byte += sizeof(struct inotify_event) + ret->len;
1622 }
1623#endif
1624
1625 bytes += this_bytes;
1626 niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1627 "almost certainly screw up." );
1628 if ( first_byte == bytes ) {
1629 first_byte = 0;
1630 }
1631
1632 /* Skip events from self due to open_by_handle_at() */
1633 if (self_pid && self_pid == event_pid) {
1634 longjmp(jmp, 0);
1635 }
1636
1637 if (regex) {
1638 inotifytools_snprintf(&match_name, MAX_STRLEN, ret, "%w%f");
1639 memcpy(&match_name_string, &match_name.buf, match_name.len);
1640 match_name_string[match_name.len] = '\0';
1641 if (0 == regexec(regex, match_name_string, 0, 0, 0)) {
1642 if (!invert_regexp)
1643 longjmp(jmp, 0);
1644 } else {
1645 if (invert_regexp)
1646 longjmp(jmp, 0);
1647 }
1648 }
1649
1650 if (collect_stats) {
1651 record_stats(ret);
1652 }
1653
1654 return ret;
1655}
1656
1682int inotifytools_watch_recursively(char const* path, int events) {
1683 return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1684}
1685
1719 int events,
1720 char const** exclude_list) {
1721 niceassert( init, "inotifytools_initialize not called yet" );
1722
1723 DIR * dir;
1724 char * my_path;
1725 error = 0;
1726 dir = opendir( path );
1727 if ( !dir ) {
1728 // If not a directory, don't need to do anything special
1729 if ( errno == ENOTDIR ) {
1730 return inotifytools_watch_file( path, events );
1731 }
1732 else {
1733 error = errno;
1734 return 0;
1735 }
1736 }
1737
1738 if ( path[strlen(path)-1] != '/' ) {
1739 nasprintf( &my_path, "%s/", path );
1740 }
1741 else {
1742 my_path = (char *)path;
1743 }
1744
1745 static struct dirent * ent;
1746 char * next_file;
1747 static struct stat64 my_stat;
1748 ent = readdir( dir );
1749 // Watch each directory within this directory
1750 while ( ent ) {
1751 if ( (0 != strcmp( ent->d_name, "." )) &&
1752 (0 != strcmp( ent->d_name, ".." )) ) {
1753 nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1754 if ( -1 == lstat64( next_file, &my_stat ) ) {
1755 error = errno;
1756 free( next_file );
1757 if ( errno != EACCES ) {
1758 error = errno;
1759 if ( my_path != path ) free( my_path );
1760 closedir( dir );
1761 return 0;
1762 }
1763 }
1764 else if ( S_ISDIR( my_stat.st_mode ) &&
1765 !S_ISLNK( my_stat.st_mode )) {
1766 free( next_file );
1767 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1768 static unsigned int no_watch;
1769 static char const** exclude_entry;
1770
1771 no_watch = 0;
1772 for (exclude_entry = exclude_list;
1773 exclude_entry && *exclude_entry && !no_watch;
1774 ++exclude_entry) {
1775 static int exclude_length;
1776
1777 exclude_length = strlen(*exclude_entry);
1778 if ((*exclude_entry)[exclude_length-1] == '/') {
1779 --exclude_length;
1780 }
1781 if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1782 !strncmp(*exclude_entry, next_file, exclude_length)) {
1783 // directory found in exclude list
1784 no_watch = 1;
1785 }
1786 }
1787 if (!no_watch) {
1788 static int status;
1790 next_file,
1791 events,
1792 exclude_list );
1793 // For some errors, we will continue.
1794 if ( !status && (EACCES != error) && (ENOENT != error) &&
1795 (ELOOP != error) ) {
1796 free( next_file );
1797 if ( my_path != path ) free( my_path );
1798 closedir( dir );
1799 return 0;
1800 }
1801 } // if !no_watch
1802 free( next_file );
1803 } // if isdir and not islnk
1804 else {
1805 free( next_file );
1806 }
1807 }
1808 ent = readdir( dir );
1809 error = 0;
1810 }
1811
1812 closedir( dir );
1813
1814 int ret = inotifytools_watch_file( my_path, events );
1815 if ( my_path != path ) free( my_path );
1816 return ret;
1817}
1818
1830 return error;
1831}
1832
1836static int isdir( char const * path ) {
1837 static struct stat64 my_stat;
1838
1839 if ( -1 == lstat64( path, &my_stat ) ) {
1840 if (errno == ENOENT) return 0;
1841 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1842 return 0;
1843 }
1844
1845 return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1846}
1847
1848
1856 int ret = 0;
1857 rbwalk(tree_filename, get_num, (void*)&ret);
1858 return ret;
1859}
1860
1905int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1906 return inotifytools_fprintf( stdout, event, fmt );
1907}
1908
1954int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1955 static struct nstring out;
1956 static int ret;
1957 ret = inotifytools_sprintf( &out, event, fmt );
1958 if ( -1 != ret ) fwrite( out.buf, sizeof(char), out.len, file );
1959 return ret;
1960}
1961
2014int inotifytools_sprintf( struct nstring * out, struct inotify_event* event, char* fmt ) {
2015 return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
2016}
2017
2018
2069int inotifytools_snprintf( struct nstring * out, int size,
2070 struct inotify_event* event, char* fmt ) {
2071 const char* eventstr;
2072 static unsigned int i, ind;
2073 static char ch1;
2074 static char timestr[MAX_STRLEN];
2075 static time_t now;
2076
2077 size_t dirnamelen = 0;
2078 const char* eventname;
2079 const char* filename =
2080 inotifytools_filename_from_event(event, &eventname, &dirnamelen);
2081
2082 if ( !fmt || 0 == strlen(fmt) ) {
2083 error = EINVAL;
2084 return -1;
2085 }
2086 if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
2087 error = EMSGSIZE;
2088 return -1;
2089 }
2090
2091 ind = 0;
2092 for ( i = 0; i < strlen(fmt) &&
2093 (int)ind < size - 1; ++i ) {
2094 if ( fmt[i] != '%' ) {
2095 out->buf[ind++] = fmt[i];
2096 continue;
2097 }
2098
2099 if ( i == strlen(fmt) - 1 ) {
2100 // last character is %, invalid
2101 error = EINVAL;
2102 return ind;
2103 }
2104
2105 ch1 = fmt[i+1];
2106
2107 if ( ch1 == '%' ) {
2108 out->buf[ind++] = '%';
2109 ++i;
2110 continue;
2111 }
2112
2113 if ( ch1 == '0' ) {
2114 out->buf[ind++] = '\0';
2115 ++i;
2116 continue;
2117 }
2118
2119 if ( ch1 == 'n' ) {
2120 out->buf[ind++] = '\n';
2121 ++i;
2122 continue;
2123 }
2124
2125 if ( ch1 == 'w' ) {
2126 if (filename && dirnamelen <= size - ind) {
2127 strncpy(&out->buf[ind], filename, dirnamelen);
2128 ind += dirnamelen;
2129 }
2130 ++i;
2131 continue;
2132 }
2133
2134 if ( ch1 == 'f' ) {
2135 if ( eventname ) {
2136 strncpy( &out->buf[ind], eventname, size - ind );
2137 ind += strlen(eventname);
2138 }
2139 ++i;
2140 continue;
2141 }
2142
2143 if ( ch1 == 'c' ) {
2144 ind += snprintf( &out->buf[ind], size-ind, "%x", event->cookie);
2145 ++i;
2146 continue;
2147 }
2148
2149 if ( ch1 == 'e' ) {
2150 eventstr = inotifytools_event_to_str( event->mask );
2151 strncpy( &out->buf[ind], eventstr, size - ind );
2152 ind += strlen(eventstr);
2153 ++i;
2154 continue;
2155 }
2156
2157 if ( ch1 == 'T' ) {
2158
2159 if ( timefmt ) {
2160 now = time(0);
2161 struct tm now_tm;
2162 if (!strftime(timestr, MAX_STRLEN - 1, timefmt,
2163 localtime_r(&now, &now_tm))) {
2164 // time format probably invalid
2165 error = EINVAL;
2166 return ind;
2167 }
2168 }
2169 else {
2170 timestr[0] = 0;
2171 }
2172
2173 strncpy( &out->buf[ind], timestr, size - ind );
2174 ind += strlen(timestr);
2175 ++i;
2176 continue;
2177 }
2178
2179 // Check if next char in fmt is e
2180 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
2181 eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
2182 strncpy( &out->buf[ind], eventstr, size - ind );
2183 ind += strlen(eventstr);
2184 i += 2;
2185 continue;
2186 }
2187
2188 // OK, this wasn't a special format character, just output it as normal
2189 if ( ind < MAX_STRLEN ) out->buf[ind++] = '%';
2190 if ( ind < MAX_STRLEN ) out->buf[ind++] = ch1;
2191 ++i;
2192 }
2193 out->len = ind;
2194
2195 return ind - 1;
2196}
2197
2208 timefmt = fmt;
2209}
2210
2220 int ret;
2221 if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
2222 return ret;
2223}
2224
2235 int ret;
2236 if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
2237 return ret;
2238}
2239
2250 int ret;
2251 if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2252 return ret;
2253}
2254
2268static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2269 if (!pattern) {
2270 if (regex) {
2271 regfree(regex);
2272 free(regex);
2273 regex = 0;
2274 }
2275 return 1;
2276 }
2277
2278 if (regex) { regfree(regex); }
2279 else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2280
2281 invert_regexp = invert;
2282 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2283 if (0 == ret) return 1;
2284
2285 regfree(regex);
2286 free(regex);
2287 regex = 0;
2288 error = EINVAL;
2289 return 0;
2290}
2291
2303int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2304 return do_ignore_events_by_regex(pattern, flags, 0);
2305}
2306
2318int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2319 return do_ignore_events_by_regex(pattern, flags, 1);
2320}
2321
2322int event_compare(const void *p1, const void *p2, const void *config)
2323{
2324 if (!p1 || !p2) return p1 - p2;
2325 char asc = 1;
2326 long sort_event = (long)config;
2327 if (sort_event == -1) {
2328 sort_event = 0;
2329 asc = 0;
2330 } else if (sort_event < 0) {
2331 sort_event = -sort_event;
2332 asc = 0;
2333 }
2334 unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2335 unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2336 if (0 == *i1 - *i2) {
2337 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2338 }
2339 if (asc)
2340 return *i1 - *i2;
2341 else
2342 return *i2 - *i1;
2343}
2344
2345struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2346{
2347 struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2348 RBLIST *all = rbopenlist(tree_wd);
2349 void const *p = rbreadlist(all);
2350 while (p) {
2351 void const *r = rbsearch(p, ret);
2352 niceassert((int)(r == p), "Couldn't insert watch into new tree");
2353 p = rbreadlist(all);
2354 }
2355 rbcloselist(all);
2356 return ret;
2357}
inotifytools library public interface.
int inotifytools_init(int fanotify, int watch_filesystem, int verbose)
Definition: inotifytools.c:301
const char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:877
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
int inotifytools_get_max_queued_events()
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
int inotifytools_remove_watch_by_filename(char const *filename)
int inotifytools_error()
int inotifytools_watch_recursively(char const *path, int events)
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:982
const char * inotifytools_filename_from_event(struct inotify_event *event, char const **eventname, size_t *dirnamelen)
Definition: inotifytools.c:925
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
int inotifytools_remove_watch_by_wd(int wd)
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:656
const char * inotifytools_dirname_from_event(struct inotify_event *event, size_t *dirnamelen)
Definition: inotifytools.c:896
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:466
void inotifytools_cleanup()
Definition: inotifytools.c:368
int inotifytools_watch_files(char const *filenames[], int events)
int inotifytools_snprintf(struct nstring *out, int size, struct inotify_event *event, char *fmt)
char * inotifytools_dirpath_from_event(struct inotify_event *event)
Definition: inotifytools.c:951
int inotifytools_get_max_user_instances()
struct inotify_event * inotifytools_next_event(long int timeout)
int inotifytools_get_num_watches()
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:628
int inotifytools_printf(struct inotify_event *event, char *fmt)
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:541
void inotifytools_replace_filename(char const *oldname, char const *newname)
const char * inotifytools_filename_from_watch(struct watch *w)
Definition: inotifytools.c:848
int inotifytools_get_max_user_watches()
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
void inotifytools_set_printf_timefmt(char *fmt)
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, char *fmt)
void inotifytools_set_filename_by_wd(int wd, char const *filename)
int inotifytools_watch_file(char const *filename, int events)
This structure holds string that can contain any character including NULL.
Definition: inotifytools.h:25
unsigned int len
Definition: inotifytools.h:27
char buf[MAX_STRLEN]
Definition: inotifytools.h:26