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