libinotifytools
inotifytools.cpp
1// kate: replace-tabs off; space-indent off;
2
14
16#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#elif !defined __ANDROID__
47// Linux only
48#define LINUX_FANOTIFY
49
50#include <fcntl.h>
51#include <sys/vfs.h>
52#include "inotifytools/fanotify.h"
53
54#ifndef __GLIBC__
55#define val __val
56#define __kernel_fsid_t fsid_t
57#endif
58
59struct fanotify_event_info_fid_wo_handle {
60 struct fanotify_event_info_header hdr;
61 __kernel_fsid_t fsid;
62};
63
64struct fanotify_event_fid {
65 struct fanotify_event_info_fid_wo_handle info;
66 struct file_handle handle;
67};
68
69#endif
70
156
157#define MAX_EVENTS 4096
158#define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
159#define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
160#define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
161#define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
162
163static int inotify_fd = -1;
164
165static int recursive_watch = 0;
166int collect_stats = 0;
167
168struct rbtree* tree_wd = 0;
169struct rbtree* tree_fid = 0;
170struct rbtree* tree_filename = 0;
171static int error = 0;
172int initialized = 0;
173int verbosity = 0;
174int fanotify_mode = 0;
175int fanotify_mark_type = 0;
176static pid_t self_pid = 0;
177
178struct str {
179 char* c_str_ = 0;
180 int size_ = 0;
181 int capacity_ = 0;
182
183 bool empty() { return !size_; }
184
185 void clear() {
186 if (c_str_) {
187 c_str_[0] = 0;
188 size_ = 0;
189 }
190 }
191
192 void set_size(int size) {
193 size_ = size;
194 if (size > capacity_)
195 capacity_ = size;
196 }
197
198 ~str() { free(c_str_); }
199};
200
201static str timefmt;
202static regex_t* regex = 0;
203/* 0: --exclude[i], 1: --include[i] */
204static int invert_regexp = 0;
205
206static int isdir(char const* path);
207void record_stats(struct inotify_event const* event);
208int onestr_to_event(char const* event);
209
210#define nasprintf(...) niceassert(-1 != asprintf(__VA_ARGS__), "out of memory")
211
229long _niceassert(long cond,
230 int line,
231 char const* file,
232 char const* condstr,
233 char const* mesg) {
234 if (cond)
235 return cond;
236
237 if (mesg) {
238 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file,
239 line, condstr, mesg);
240 } else {
241 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line,
242 condstr);
243 }
244
245 return cond;
246}
247
248static void charcat(char* s, const char c) {
249 size_t l = strlen(s);
250 s[l] = c;
251 s[++l] = 0;
252}
253
257static int read_num_from_file(const char* filename, int* num) {
258 FILE* file = fopen(filename, "r");
259 if (!file) {
260 error = errno;
261 return 0;
262 }
263
264 if (EOF == fscanf(file, "%d", num)) {
265 error = errno;
266 const int fclose_ret = fclose(file);
267 niceassert(!fclose_ret, 0);
268 return 0;
269 }
270
271 const int fclose_ret = fclose(file);
272 niceassert(!fclose_ret, 0);
273
274 return 1;
275}
276
277static int wd_compare(const char* d1, const char* d2, const void* config) {
278 if (!d1 || !d2)
279 return d1 - d2;
280 return ((watch*)d1)->wd - ((watch*)d2)->wd;
281}
282
283static int fid_compare(const char* d1, const char* d2, const void* config) {
284#ifdef LINUX_FANOTIFY
285 if (!d1 || !d2)
286 return d1 - d2;
287 watch* w1 = (watch*)d1;
288 watch* w2 = (watch*)d2;
289 int n1, n2;
290 n1 = w1->fid->info.hdr.len;
291 n2 = w2->fid->info.hdr.len;
292 if (n1 != n2)
293 return n1 - n2;
294 return memcmp(w1->fid, w2->fid, n1);
295#else
296 return d1 - d2;
297#endif
298}
299
300static int filename_compare(const char* d1,
301 const char* d2,
302 const void* config) {
303 if (!d1 || !d2)
304 return d1 - d2;
305 return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
306}
307
311watch* watch_from_wd(int wd) {
312 watch w;
313 w.wd = wd;
314 return (watch*)rbfind(&w, tree_wd);
315}
316
320watch* watch_from_fid(struct fanotify_event_fid* fid) {
321 watch w;
322 w.fid = fid;
323 return (watch*)rbfind(&w, tree_fid);
324}
325
329watch* watch_from_filename(char const* filename) {
330 watch w;
331 w.filename = (char*)filename;
332 return (watch*)rbfind(&w, tree_filename);
333}
334
345int inotifytools_init(int fanotify, int watch_filesystem, int verbose) {
346 if (initialized)
347 return 1;
348
349 error = 0;
350 verbosity = verbose;
351 // Try to initialise inotify/fanotify
352 if (fanotify) {
353#ifdef LINUX_FANOTIFY
354 self_pid = getpid();
355 fanotify_mode = 1;
356 fanotify_mark_type =
357 watch_filesystem ? FAN_MARK_FILESYSTEM : FAN_MARK_INODE;
358 inotify_fd =
359 fanotify_init(FAN_REPORT_FID | FAN_REPORT_DFID_NAME, 0);
360#endif
361 } else {
362 fanotify_mode = 0;
363 inotify_fd = inotify_init();
364 }
365 if (inotify_fd < 0) {
366 error = errno;
367 return 0;
368 }
369
370 collect_stats = 0;
371 initialized = 1;
372 tree_wd = rbinit(wd_compare, 0);
373 tree_fid = rbinit(fid_compare, 0);
374 tree_filename = rbinit(filename_compare, 0);
375 timefmt.clear();
376
377 return 1;
378}
379
380int inotifytools_initialize() {
381 return inotifytools_init(0, 0, 0);
382}
383
387void destroy_watch(watch* w) {
388 if (w->filename)
389 free(w->filename);
390 if (w->fid)
391 free(w->fid);
392 if (w->dirf)
393 close(w->dirf);
394 free(w);
395}
396
400void cleanup_tree(const void* nodep,
401 const VISIT which,
402 const int depth,
403 void* arg) {
404 if (which != endorder && which != leaf)
405 return;
406 watch* w = (watch*)nodep;
407 destroy_watch(w);
408}
409
417 if (!initialized)
418 return;
419
420 initialized = 0;
421 close(inotify_fd);
422 collect_stats = 0;
423 error = 0;
424 timefmt.clear();
425
426 if (regex) {
427 regfree(regex);
428 free(regex);
429 regex = 0;
430 }
431
432 rbwalk(tree_wd, cleanup_tree, 0);
433 rbdestroy(tree_wd);
434 rbdestroy(tree_fid);
435 rbdestroy(tree_filename);
436 tree_wd = 0;
437 tree_fid = 0;
438 tree_filename = 0;
439}
440
444struct replace_filename_data {
445 char const* old_name;
446 char const* new_name;
447 size_t old_len;
448};
449
453static void replace_filename_impl(const void* nodep,
454 const VISIT which,
455 const int depth,
456 const struct replace_filename_data* data) {
457 if (which != endorder && which != leaf)
458 return;
459 watch* w = (watch*)nodep;
460 char* name;
461 if (0 == strncmp(data->old_name, w->filename, data->old_len)) {
462 nasprintf(&name, "%s%s", data->new_name,
463 &(w->filename[data->old_len]));
464 if (!strcmp(w->filename, data->new_name)) {
465 free(name);
466 } else {
467 rbdelete(w, tree_filename);
468 free(w->filename);
469 w->filename = name;
470 rbsearch(w, tree_filename);
471 }
472 }
473}
474
478static void replace_filename(const void* nodep,
479 const VISIT which,
480 const int depth,
481 void* data) {
482 replace_filename_impl(nodep, which, depth,
483 (const struct replace_filename_data*)data);
484}
485
489static void get_num(const void* nodep,
490 const VISIT which,
491 const int depth,
492 void* arg) {
493 if (which != endorder && which != leaf)
494 return;
495 ++(*((int*)arg));
496}
497
525int inotifytools_str_to_event_sep(char const* event, char sep) {
526 if (strchr("_"
527 "abcdefghijklmnopqrstuvwxyz"
528 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
529 sep)) {
530 return -1;
531 }
532
533 int ret, len;
534 char *event1, *event2;
535 static const size_t eventstr_size = 4096;
536 char eventstr[eventstr_size];
537 ret = 0;
538
539 if (!event || !event[0])
540 return 0;
541
542 event1 = (char*)event;
543 event2 = strchr(event1, sep);
544 while (event1 && event1[0]) {
545 if (event2) {
546 len = event2 - event1;
547 niceassert(len < eventstr_size,
548 "malformed event string (very long)");
549 } else {
550 len = strlen(event1);
551 }
552 if (len > eventstr_size - 1)
553 len = eventstr_size - 1;
554
555 strncpy(eventstr, event1, len);
556
557 eventstr[len] = 0;
558
559 int ret1 = onestr_to_event(eventstr);
560 if (0 == ret1 || -1 == ret1) {
561 ret = ret1;
562 break;
563 }
564 ret |= ret1;
565
566 event1 = event2;
567 if (event1 && event1[0]) {
568 // jump over 'sep' character
569 ++event1;
570 // if last character was 'sep'...
571 if (!event1[0])
572 return 0;
573 event2 = strchr(event1, sep);
574 }
575 }
576
577 return ret;
578}
579
603int inotifytools_str_to_event(char const* event) {
604 return inotifytools_str_to_event_sep(event, ',');
605}
606
618int onestr_to_event(char const* event) {
619 static int ret;
620 ret = -1;
621
622 if (!event || !event[0])
623 ret = 0;
624 else if (0 == strcasecmp(event, "ACCESS"))
625 ret = IN_ACCESS;
626 else if (0 == strcasecmp(event, "MODIFY"))
627 ret = IN_MODIFY;
628 else if (0 == strcasecmp(event, "ATTRIB"))
629 ret = IN_ATTRIB;
630 else if (0 == strcasecmp(event, "CLOSE_WRITE"))
631 ret = IN_CLOSE_WRITE;
632 else if (0 == strcasecmp(event, "CLOSE_NOWRITE"))
633 ret = IN_CLOSE_NOWRITE;
634 else if (0 == strcasecmp(event, "OPEN"))
635 ret = IN_OPEN;
636 else if (0 == strcasecmp(event, "MOVED_FROM"))
637 ret = IN_MOVED_FROM;
638 else if (0 == strcasecmp(event, "MOVED_TO"))
639 ret = IN_MOVED_TO;
640 else if (0 == strcasecmp(event, "CREATE"))
641 ret = IN_CREATE;
642 else if (0 == strcasecmp(event, "DELETE"))
643 ret = IN_DELETE;
644 else if (0 == strcasecmp(event, "DELETE_SELF"))
645 ret = IN_DELETE_SELF;
646 else if (0 == strcasecmp(event, "UNMOUNT"))
647 ret = IN_UNMOUNT;
648 else if (0 == strcasecmp(event, "Q_OVERFLOW"))
649 ret = IN_Q_OVERFLOW;
650 else if (0 == strcasecmp(event, "IGNORED"))
651 ret = IN_IGNORED;
652 else if (0 == strcasecmp(event, "CLOSE"))
653 ret = IN_CLOSE;
654 else if (0 == strcasecmp(event, "MOVE_SELF"))
655 ret = IN_MOVE_SELF;
656 else if (0 == strcasecmp(event, "MOVE"))
657 ret = IN_MOVE;
658 else if (0 == strcasecmp(event, "ISDIR"))
659 ret = IN_ISDIR;
660 else if (0 == strcasecmp(event, "ONESHOT"))
661 ret = IN_ONESHOT;
662 else if (0 == strcasecmp(event, "ALL_EVENTS"))
663 ret = IN_ALL_EVENTS;
664
665 return ret;
666}
667
689char* inotifytools_event_to_str(int events) {
690 return inotifytools_event_to_str_sep(events, ',');
691}
692
717char* inotifytools_event_to_str_sep(int events, char sep) {
718 static char ret[1024];
719 ret[0] = '\0';
720 ret[1] = '\0';
721
722 if (IN_ACCESS & events) {
723 charcat(ret, sep);
724 strncat(ret, "ACCESS", 7);
725 }
726 if (IN_MODIFY & events) {
727 charcat(ret, sep);
728 strncat(ret, "MODIFY", 7);
729 }
730 if (IN_ATTRIB & events) {
731 charcat(ret, sep);
732 strncat(ret, "ATTRIB", 7);
733 }
734 if (IN_CLOSE_WRITE & events) {
735 charcat(ret, sep);
736 strncat(ret, "CLOSE_WRITE", 12);
737 }
738 if (IN_CLOSE_NOWRITE & events) {
739 charcat(ret, sep);
740 strncat(ret, "CLOSE_NOWRITE", 14);
741 }
742 if (IN_OPEN & events) {
743 charcat(ret, sep);
744 strncat(ret, "OPEN", 5);
745 }
746 if (IN_MOVED_FROM & events) {
747 charcat(ret, sep);
748 strncat(ret, "MOVED_FROM", 11);
749 }
750 if (IN_MOVED_TO & events) {
751 charcat(ret, sep);
752 strncat(ret, "MOVED_TO", 9);
753 }
754 if (IN_CREATE & events) {
755 charcat(ret, sep);
756 strncat(ret, "CREATE", 7);
757 }
758 if (IN_DELETE & events) {
759 charcat(ret, sep);
760 strncat(ret, "DELETE", 7);
761 }
762 if (IN_DELETE_SELF & events) {
763 charcat(ret, sep);
764 strncat(ret, "DELETE_SELF", 12);
765 }
766 if (IN_UNMOUNT & events) {
767 charcat(ret, sep);
768 strncat(ret, "UNMOUNT", 8);
769 }
770 if (IN_Q_OVERFLOW & events) {
771 charcat(ret, sep);
772 strncat(ret, "Q_OVERFLOW", 11);
773 }
774 if (IN_IGNORED & events) {
775 charcat(ret, sep);
776 strncat(ret, "IGNORED", 8);
777 }
778 if (IN_CLOSE & events) {
779 charcat(ret, sep);
780 strncat(ret, "CLOSE", 6);
781 }
782 if (IN_MOVE_SELF & events) {
783 charcat(ret, sep);
784 strncat(ret, "MOVE_SELF", 10);
785 }
786 if (IN_ISDIR & events) {
787 charcat(ret, sep);
788 strncat(ret, "ISDIR", 6);
789 }
790 if (IN_ONESHOT & events) {
791 charcat(ret, sep);
792 strncat(ret, "ONESHOT", 8);
793 }
794
795 // Maybe we didn't match any... ?
796 if (ret[0] == '\0') {
797 niceassert(-1 != sprintf(ret, "%c0x%08x", sep, events), 0);
798 }
799
800 return &ret[1];
801}
802
809static const char* inotifytools_filename_from_fid(
810 struct fanotify_event_fid* fid) {
811#ifdef LINUX_FANOTIFY
812 static char filename[PATH_MAX];
813 struct fanotify_event_fid fsid = {};
814 int dirf = 0, mount_fd = AT_FDCWD;
815 int len = 0, name_len = 0;
816
817 // Match mount_fd from fid->fsid (and null fhandle)
818 fsid.info.fsid.val[0] = fid->info.fsid.val[0];
819 fsid.info.fsid.val[1] = fid->info.fsid.val[1];
820 fsid.info.hdr.info_type = FAN_EVENT_INFO_TYPE_FID;
821 fsid.info.hdr.len = sizeof(fsid);
822 watch* mnt = watch_from_fid(&fsid);
823 if (mnt)
824 mount_fd = mnt->dirf;
825
826 if (fid->info.hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
827 int fid_len = sizeof(*fid) + fid->handle.handle_bytes;
828
829 name_len = fid->info.hdr.len - fid_len;
830 if (name_len && !fid->handle.f_handle[fid->handle.handle_bytes])
831 name_len = 0; // empty name??
832 }
833
834 // Try to get path from file handle
835 dirf = open_by_handle_at(mount_fd, &fid->handle, 0);
836 if (dirf > 0) {
837 // Got path by handle
838 } else if (fanotify_mark_type == FAN_MARK_FILESYSTEM) {
839 fprintf(stderr, "Failed to decode directory fid.\n");
840 return NULL;
841 } else if (name_len) {
842 // For recursive watch look for watch by fid without the name
843 fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID;
844 fid->info.hdr.len -= name_len;
845
846 watch* w = watch_from_fid(fid);
847
848 fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
849 fid->info.hdr.len += name_len;
850
851 if (!w) {
852 fprintf(stderr,
853 "Failed to lookup path by directory fid.\n");
854 return NULL;
855 }
856
857 dirf = w->dirf ? dup(w->dirf) : -1;
858 if (dirf < 0) {
859 fprintf(stderr, "Failed to get directory fd.\n");
860 return NULL;
861 }
862 } else {
863 // Fallthrough to stored filename
864 return NULL;
865 }
866 char sym[30];
867 sprintf(sym, "/proc/self/fd/%d", dirf);
868
869 // PATH_MAX - 2 because we have to append two characters to this path,
870 // '/' and 0
871 len = readlink(sym, filename, PATH_MAX - 2);
872 if (len < 0) {
873 close(dirf);
874 fprintf(stderr, "Failed to resolve path from directory fd.\n");
875 return NULL;
876 }
877
878 filename[len++] = '/';
879 filename[len] = 0;
880
881 if (name_len > 0) {
882 const char* name = (const char*)fid->handle.f_handle +
883 fid->handle.handle_bytes;
884 int deleted = faccessat(dirf, name, F_OK, AT_SYMLINK_NOFOLLOW);
885 if (deleted && errno != ENOENT) {
886 fprintf(stderr, "Failed to access file %s (%s).\n",
887 name, strerror(errno));
888 close(dirf);
889 return NULL;
890 }
891 memcpy(filename + len, name, name_len);
892 if (deleted)
893 strncat(filename, " (deleted)", 11);
894 }
895 close(dirf);
896 return filename;
897#else
898 return NULL;
899#endif
900}
901
908const char* inotifytools_filename_from_watch(watch* w) {
909 if (!w)
910 return "";
911 if (!w->fid || !fanotify_mark_type)
912 return w->filename;
913
914 return inotifytools_filename_from_fid(w->fid) ?: w->filename;
915}
916
937const char* inotifytools_filename_from_wd(int wd) {
938 niceassert(initialized, "inotifytools_initialize not called yet");
939 if (!wd)
940 return "";
941 watch* w = watch_from_wd(wd);
942 if (!w)
943 return "";
944
946}
947
956const char* inotifytools_dirname_from_event(struct inotify_event* event,
957 size_t* dirnamelen) {
958 const char* filename = inotifytools_filename_from_wd(event->wd);
959 const char* dirsep = NULL;
960
961 if (!filename) {
962 return NULL;
963 }
964
965 /* Split dirname from filename for fanotify event */
966 if (fanotify_mode)
967 dirsep = strrchr(filename, '/');
968 if (!dirsep) {
969 *dirnamelen = strlen(filename);
970 return filename;
971 }
972
973 *dirnamelen = dirsep - filename + 1;
974 return filename;
975}
976
985const char* inotifytools_filename_from_event(struct inotify_event* event,
986 char const** eventname,
987 size_t* dirnamelen) {
988 if (event->len > 0)
989 *eventname = event->name;
990 else
991 *eventname = "";
992
993 const char* filename =
994 inotifytools_dirname_from_event(event, dirnamelen);
995
996 /* On fanotify watch, filename includes event->name */
997 if (filename && filename[*dirnamelen])
998 *eventname = filename + *dirnamelen;
999
1000 return filename;
1001}
1002
1011char* inotifytools_dirpath_from_event(struct inotify_event* event) {
1012 const char* filename = inotifytools_filename_from_wd(event->wd);
1013
1014 if (!filename || !*filename || !(event->mask & IN_ISDIR)) {
1015 return NULL;
1016 }
1017
1018 /*
1019 * fanotify watch->filename includes the name, so no need to add the
1020 * event->name again.
1021 */
1022 char* path;
1023 nasprintf(&path, "%s%s/", filename, fanotify_mode ? "" : event->name);
1024
1025 return path;
1026}
1027
1042int inotifytools_wd_from_filename(char const* filename) {
1043 niceassert(initialized, "inotifytools_initialize not called yet");
1044 if (!filename || !*filename)
1045 return -1;
1046 watch* w = watch_from_filename(filename);
1047 if (!w)
1048 return -1;
1049 return w->wd;
1050}
1051
1066void inotifytools_set_filename_by_wd(int wd, char const* filename) {
1067 niceassert(initialized, "inotifytools_initialize not called yet");
1068 watch* w = watch_from_wd(wd);
1069 if (!w)
1070 return;
1071 if (w->filename)
1072 free(w->filename);
1073 w->filename = strdup(filename);
1074}
1075
1091 char const* newname) {
1092 watch* w = watch_from_filename(oldname);
1093 if (!w)
1094 return;
1095 if (w->filename)
1096 free(w->filename);
1097 w->filename = strdup(newname);
1098}
1099
1122void inotifytools_replace_filename(char const* oldname, char const* newname) {
1123 if (!oldname || !newname)
1124 return;
1125 if (!*oldname || !*newname)
1126 return;
1127 struct replace_filename_data data;
1128 data.old_name = oldname;
1129 data.new_name = newname;
1130 data.old_len = strlen(oldname);
1131 rbwalk(tree_filename, replace_filename, (void*)&data);
1132}
1133
1137int remove_inotify_watch(watch* w) {
1138 error = 0;
1139 // There is no kernel object representing the watch with fanotify
1140 if (w->fid)
1141 return 0;
1142 int status = inotify_rm_watch(inotify_fd, w->wd);
1143 if (status < 0) {
1144 fprintf(stderr, "Failed to remove watch on %s: %s\n",
1145 w->filename, strerror(status));
1146 error = status;
1147 return 0;
1148 }
1149 return 1;
1150}
1151
1155watch* create_watch(int wd,
1156 struct fanotify_event_fid* fid,
1157 const char* filename,
1158 int dirf) {
1159 if (wd < 0 || !filename)
1160 return 0;
1161
1162 watch* w = (watch*)calloc(1, sizeof(watch));
1163 if (!w) {
1164 fprintf(stderr, "Failed to allocate watch.\n");
1165 return NULL;
1166 }
1167 w->wd = wd ?: (unsigned long)fid;
1168 w->fid = fid;
1169 w->dirf = dirf;
1170 w->filename = strdup(filename);
1171 rbsearch(w, tree_wd);
1172 if (fid)
1173 rbsearch(w, tree_fid);
1174
1175 rbsearch(w, tree_filename);
1176 return w;
1177}
1178
1192 niceassert(initialized, "inotifytools_initialize not called yet");
1193 watch* w = watch_from_wd(wd);
1194 if (!w)
1195 return 1;
1196
1197 if (!remove_inotify_watch(w))
1198 return 0;
1199 rbdelete(w, tree_wd);
1200 if (w->fid)
1201 rbdelete(w, tree_fid);
1202 rbdelete(w, tree_filename);
1203 destroy_watch(w);
1204 return 1;
1205}
1206
1218int inotifytools_remove_watch_by_filename(char const* filename) {
1219 niceassert(initialized, "inotifytools_initialize not called yet");
1220 watch* w = watch_from_filename(filename);
1221 if (!w)
1222 return 1;
1223
1224 if (!remove_inotify_watch(w))
1225 return 0;
1226 rbdelete(w, tree_wd);
1227 if (w->fid)
1228 rbdelete(w, tree_fid);
1229 rbdelete(w, tree_filename);
1230 destroy_watch(w);
1231 return 1;
1232}
1233
1245int inotifytools_watch_file(char const* filename, int events) {
1246 static char const* filenames[2];
1247 filenames[0] = filename;
1248 filenames[1] = NULL;
1249 return inotifytools_watch_files(filenames, events);
1250}
1251
1267int inotifytools_watch_files(char const* filenames[], int events) {
1268 niceassert(initialized, "inotifytools_initialize not called yet");
1269 error = 0;
1270
1271 static int i;
1272 for (i = 0; filenames[i]; ++i) {
1273 int wd = -1;
1274 if (fanotify_mode) {
1275#ifdef LINUX_FANOTIFY
1276 unsigned int flags = FAN_MARK_ADD | fanotify_mark_type;
1277
1278 if (events & IN_DONT_FOLLOW) {
1279 events &= ~IN_DONT_FOLLOW;
1280 flags |= FAN_MARK_DONT_FOLLOW;
1281 }
1282
1283 wd = fanotify_mark(inotify_fd, flags,
1284 events | FAN_EVENT_ON_CHILD,
1285 AT_FDCWD, filenames[i]);
1286#endif
1287 } else {
1288 wd =
1289 inotify_add_watch(inotify_fd, filenames[i], events);
1290 }
1291 if (wd < 0) {
1292 if (wd == -1) {
1293 error = errno;
1294 return 0;
1295 } // if ( wd == -1 )
1296 else {
1297 fprintf(
1298 stderr,
1299 "Failed to watch %s: returned wd was %d "
1300 "(expected -1 or >0 )",
1301 filenames[i], wd);
1302 // no appropriate value for error
1303 return 0;
1304 } // else
1305 } // if ( wd < 0 )
1306
1307 const char* filename = filenames[i];
1308 size_t filenamelen = strlen(filename);
1309 char* dirname;
1310 int dirf = 0;
1311 // Always end filename with / if it is a directory
1312 if (!isdir(filename)) {
1313 dirname = NULL;
1314 } else if (filename[filenamelen - 1] == '/') {
1315 dirname = strdup(filename);
1316 } else {
1317 nasprintf(&dirname, "%s/", filename);
1318 filename = dirname;
1319 filenamelen++;
1320 }
1321
1322 struct fanotify_event_fid* fid = NULL;
1323#ifdef LINUX_FANOTIFY
1324 if (!wd) {
1325 fid = (fanotify_event_fid*)calloc(
1326 1, sizeof(*fid) + MAX_FID_LEN);
1327 if (!fid) {
1328 fprintf(stderr, "Failed to allocate fid");
1329 free(dirname);
1330 return 0;
1331 }
1332
1333 struct statfs buf;
1334 if (statfs(filenames[i], &buf)) {
1335 free(fid);
1336 fprintf(stderr, "Statfs failed on %s: %s\n",
1337 filenames[i], strerror(errno));
1338 free(dirname);
1339 return 0;
1340 }
1341 memcpy(&fid->info.fsid, &buf.f_fsid,
1342 sizeof(__kernel_fsid_t));
1343
1344 // Hash mount_fd with fid->fsid (and null fhandle)
1345 int ret, mntid;
1346 watch* mnt = dirname ? watch_from_fid(fid) : NULL;
1347 if (dirname && !mnt) {
1348 struct fanotify_event_fid* fsid;
1349
1350 fsid = (fanotify_event_fid*)calloc(
1351 1, sizeof(*fsid));
1352 if (!fsid) {
1353 free(fid);
1354 fprintf(stderr,
1355 "Failed to allocate fsid");
1356 free(dirname);
1357 return 0;
1358 }
1359 fsid->info.fsid.val[0] = fid->info.fsid.val[0];
1360 fsid->info.fsid.val[1] = fid->info.fsid.val[1];
1361 fsid->info.hdr.info_type =
1362 FAN_EVENT_INFO_TYPE_FID;
1363 fsid->info.hdr.len = sizeof(*fsid);
1364 mntid = open(dirname, O_RDONLY);
1365 if (mntid < 0) {
1366 free(fid);
1367 free(fsid);
1368 fprintf(stderr,
1369 "Failed to open %s: %s\n",
1370 dirname, strerror(errno));
1371 free(dirname);
1372 return 0;
1373 }
1374 // Hash mount_fd without terminating /
1375 dirname[filenamelen - 1] = 0;
1376 create_watch(0, fsid, dirname, mntid);
1377 dirname[filenamelen - 1] = '/';
1378 }
1379
1380 fid->handle.handle_bytes = MAX_FID_LEN;
1381 ret = name_to_handle_at(AT_FDCWD, filenames[i],
1382 &fid->handle, &mntid, 0);
1383 if (ret || fid->handle.handle_bytes > MAX_FID_LEN) {
1384 free(fid);
1385 fprintf(stderr, "Encode fid failed on %s: %s\n",
1386 filenames[i], strerror(errno));
1387 free(dirname);
1388 return 0;
1389 }
1390 fid->info.hdr.info_type = dirname
1391 ? FAN_EVENT_INFO_TYPE_DFID
1392 : FAN_EVENT_INFO_TYPE_FID;
1393 fid->info.hdr.len =
1394 sizeof(*fid) + fid->handle.handle_bytes;
1395 if (dirname) {
1396 dirf = open(dirname, O_PATH);
1397 if (dirf < 0) {
1398 free(fid);
1399 fprintf(stderr,
1400 "Failed to open %s: %s\n",
1401 dirname, strerror(errno));
1402 free(dirname);
1403 return 0;
1404 }
1405 }
1406 }
1407#endif
1408 create_watch(wd, fid, filename, dirf);
1409 free(dirname);
1410 } // for
1411
1412 return 1;
1413}
1414
1441struct inotify_event* inotifytools_next_event(long int timeout) {
1442 if (!timeout) {
1443 timeout = -1;
1444 }
1445
1446 return inotifytools_next_events(timeout, 1);
1447}
1448
1497struct inotify_event* inotifytools_next_events(long int timeout,
1498 int num_events) {
1499 niceassert(initialized, "inotifytools_initialize not called yet");
1500 niceassert(num_events <= MAX_EVENTS, "too many events requested");
1501
1502 if (num_events < 1)
1503 return NULL;
1504
1505 // second half of event[] buffer is for fanotify->inotify conversion
1506 static struct inotify_event event[2 * MAX_EVENTS];
1507 static struct inotify_event* ret;
1508 static int first_byte = 0;
1509 static ssize_t bytes;
1510 static ssize_t this_bytes;
1511 static jmp_buf jmp;
1512 static struct nstring match_name;
1513 static char match_name_string[MAX_STRLEN + 1];
1514
1515 setjmp(jmp);
1516
1517 pid_t event_pid = 0;
1518 error = 0;
1519
1520 // first_byte is index into event buffer
1521 if (first_byte != 0 &&
1522 first_byte <= (int)(bytes - sizeof(struct inotify_event))) {
1523 ret = (struct inotify_event*)((char*)&event[0] + first_byte);
1524 if (!fanotify_mode &&
1525 first_byte + sizeof(*ret) + ret->len > bytes) {
1526 // oh... no. this can't be happening. An incomplete
1527 // event. Copy what we currently have into first
1528 // element, call self to read remainder. oh, and they
1529 // BETTER NOT overlap. Boy I hope this code works. But I
1530 // think this can never happen due to how inotify is
1531 // written.
1532 niceassert((long)((char*)&event[0] +
1533 sizeof(struct inotify_event) +
1534 event[0].len) <= (long)ret,
1535 "extremely unlucky user, death imminent");
1536 // how much of the event do we have?
1537 bytes = (char*)&event[0] + bytes - (char*)ret;
1538 memcpy(&event[0], ret, bytes);
1539 return inotifytools_next_events(timeout, num_events);
1540 }
1541 this_bytes = 0;
1542 goto more_events;
1543
1544 }
1545
1546 else if (first_byte == 0) {
1547 bytes = 0;
1548 }
1549
1550 static unsigned int bytes_to_read;
1551 static int rc;
1552 static fd_set read_fds;
1553
1554 static struct timeval read_timeout;
1555 read_timeout.tv_sec = timeout;
1556 read_timeout.tv_usec = 0;
1557 static struct timeval* read_timeout_ptr;
1558 read_timeout_ptr = (timeout < 0 ? NULL : &read_timeout);
1559
1560 FD_ZERO(&read_fds);
1561 FD_SET(inotify_fd, &read_fds);
1562 rc = select(inotify_fd + 1, &read_fds, NULL, NULL, read_timeout_ptr);
1563 if (rc < 0) {
1564 // error
1565 error = errno;
1566 return NULL;
1567 } else if (rc == 0) {
1568 // timeout
1569 return NULL;
1570 }
1571
1572 // wait until we have enough bytes to read
1573 do {
1574 rc = ioctl(inotify_fd, FIONREAD, &bytes_to_read);
1575 } while (!rc &&
1576 bytes_to_read < sizeof(struct inotify_event) * num_events);
1577
1578 if (rc == -1) {
1579 error = errno;
1580 return NULL;
1581 }
1582
1583 this_bytes = read(inotify_fd, (char*)&event[0] + bytes,
1584 sizeof(struct inotify_event) * MAX_EVENTS - bytes);
1585 if (this_bytes < 0) {
1586 error = errno;
1587 return NULL;
1588 }
1589 if (this_bytes == 0) {
1590 fprintf(stderr,
1591 "Inotify reported end-of-file. Possibly too many "
1592 "events occurred at once.\n");
1593 return NULL;
1594 }
1595more_events:
1596 ret = (struct inotify_event*)((char*)&event[0] + first_byte);
1597#ifdef LINUX_FANOTIFY
1598 // convert fanotify events to inotify events
1599 if (fanotify_mode) {
1600 struct fanotify_event_metadata* meta =
1601 (fanotify_event_metadata*)ret;
1602 struct fanotify_event_info_fid* info =
1603 (fanotify_event_info_fid*)(meta + 1);
1604 struct fanotify_event_fid* fid = NULL;
1605 const char* name = "";
1606 int fid_len = 0;
1607 int name_len = 0;
1608
1609 first_byte += meta->event_len;
1610
1611 if (meta->event_len > sizeof(*meta)) {
1612 switch (info->hdr.info_type) {
1613 case FAN_EVENT_INFO_TYPE_FID:
1614 case FAN_EVENT_INFO_TYPE_DFID:
1615 case FAN_EVENT_INFO_TYPE_DFID_NAME:
1616 fid = (fanotify_event_fid*)info;
1617 fid_len = sizeof(*fid) +
1618 fid->handle.handle_bytes;
1619 if (info->hdr.info_type ==
1620 FAN_EVENT_INFO_TYPE_DFID_NAME) {
1621 name_len =
1622 info->hdr.len - fid_len;
1623 }
1624 if (name_len > 0) {
1625 name =
1626 (const char*)
1627 fid->handle.f_handle +
1628 fid->handle.handle_bytes;
1629 }
1630 // Convert zero padding to zero
1631 // name_len. For some events on
1632 // directories, the fid is that of the
1633 // dir and name is ".". Do not include
1634 // "." name in fid hash, but keep it for
1635 // debug print.
1636 if (name_len &&
1637 (!*name ||
1638 (name[0] == '.' && !name[1]))) {
1639 info->hdr.len -= name_len;
1640 name_len = 0;
1641 }
1642 break;
1643 }
1644 }
1645 if (!fid) {
1646 fprintf(stderr, "No fid in fanotify event.\n");
1647 return NULL;
1648 }
1649 if (verbosity > 1) {
1650 printf(
1651 "fanotify_event: bytes=%zd, first_byte=%d, "
1652 "this_bytes=%zd, event_len=%u, fid_len=%d, "
1653 "name_len=%d, name=%s\n",
1654 bytes, first_byte, this_bytes, meta->event_len,
1655 fid_len, name_len, name);
1656 }
1657
1658 ret = &event[MAX_EVENTS];
1659 watch* w = watch_from_fid(fid);
1660 if (!w) {
1661 struct fanotify_event_fid* newfid =
1662 (fanotify_event_fid*)calloc(1, info->hdr.len);
1663 if (!newfid) {
1664 fprintf(stderr, "Failed to allocate fid.\n");
1665 return NULL;
1666 }
1667 memcpy(newfid, fid, info->hdr.len);
1668 const char* filename =
1669 inotifytools_filename_from_fid(fid);
1670 if (filename) {
1671 w = create_watch(0, newfid, filename, 0);
1672 if (!w) {
1673 free(newfid);
1674 return NULL;
1675 }
1676 }
1677
1678 if (verbosity) {
1679 unsigned long id;
1680 memcpy((void*)&id, fid->handle.f_handle,
1681 sizeof(id));
1682 printf("[fid=%x.%x.%lx;name='%s'] %s\n",
1683 fid->info.fsid.val[0],
1684 fid->info.fsid.val[1], id, name,
1685 filename ?: "");
1686 }
1687 }
1688 ret->wd = w ? w->wd : 0;
1689 ret->mask = (uint32_t)meta->mask;
1690 ret->len = name_len;
1691 if (name_len > 0)
1692 memcpy(ret->name, name, name_len);
1693 event_pid = meta->pid;
1694 } else {
1695 first_byte += sizeof(struct inotify_event) + ret->len;
1696 }
1697#endif
1698
1699 bytes += this_bytes;
1700 niceassert(first_byte <= bytes,
1701 "ridiculously long filename, things will "
1702 "almost certainly screw up.");
1703 if (first_byte == bytes) {
1704 first_byte = 0;
1705 }
1706
1707 /* Skip events from self due to open_by_handle_at() */
1708 if (self_pid && self_pid == event_pid) {
1709 longjmp(jmp, 0);
1710 }
1711
1712 if (regex) {
1713 // Skip regex filtering for directories in recursive mode
1714 if (recursive_watch && (ret->mask & IN_ISDIR) &&
1715 (ret->mask & (IN_CREATE | IN_MOVED_TO))) {
1716 // Allow directory events through when watching recursively
1717 } else {
1718 inotifytools_snprintf(&match_name, MAX_STRLEN, ret, "%w%f");
1719 memcpy(&match_name_string, &match_name.buf, match_name.len);
1720 match_name_string[match_name.len] = '\0';
1721 if (0 == regexec(regex, match_name_string, 0, 0, 0)) {
1722 if (!invert_regexp)
1723 longjmp(jmp, 0);
1724 } else {
1725 if (invert_regexp)
1726 longjmp(jmp, 0);
1727 }
1728 }
1729 }
1730
1731 if (collect_stats) {
1732 record_stats(ret);
1733 }
1734
1735 return ret;
1736}
1737
1763int inotifytools_watch_recursively(char const* path, int events) {
1764 return inotifytools_watch_recursively_with_exclude(path, events, 0);
1765}
1766
1800 int events,
1801 char const** exclude_list) {
1802 niceassert(initialized, "inotifytools_initialize not called yet");
1803
1804 DIR* dir;
1805 char* my_path;
1806 error = 0;
1807 dir = opendir(path);
1808 if (!dir) {
1809 // If not a directory, don't need to do anything special
1810 if (errno == ENOTDIR) {
1811 return inotifytools_watch_file(path, events);
1812 } else {
1813 error = errno;
1814 return 0;
1815 }
1816 }
1817
1818 if (path[strlen(path) - 1] != '/') {
1819 nasprintf(&my_path, "%s/", path);
1820 } else {
1821 my_path = (char*)path;
1822 }
1823
1824 static struct dirent* ent;
1825 char* next_file;
1826 static struct stat my_stat;
1827 ent = readdir(dir);
1828 // Watch each directory within this directory
1829 while (ent) {
1830 if ((0 != strcmp(ent->d_name, ".")) &&
1831 (0 != strcmp(ent->d_name, ".."))) {
1832 nasprintf(&next_file, "%s%s", my_path, ent->d_name);
1833 if (-1 == lstat(next_file, &my_stat)) {
1834 error = errno;
1835 free(next_file);
1836 if (errno != EACCES) {
1837 error = errno;
1838 if (my_path != path)
1839 free(my_path);
1840 closedir(dir);
1841 return 0;
1842 }
1843 } else if (S_ISDIR(my_stat.st_mode) &&
1844 !S_ISLNK(my_stat.st_mode)) {
1845 free(next_file);
1846 nasprintf(&next_file, "%s%s/", my_path,
1847 ent->d_name);
1848 static unsigned int no_watch;
1849 static char const** exclude_entry;
1850
1851 no_watch = 0;
1852 for (exclude_entry = exclude_list;
1853 exclude_entry && *exclude_entry &&
1854 !no_watch;
1855 ++exclude_entry) {
1856 static int exclude_length;
1857
1858 exclude_length = strlen(*exclude_entry);
1859 if ((*exclude_entry)[exclude_length -
1860 1] == '/') {
1861 --exclude_length;
1862 }
1863 if (strlen(next_file) ==
1864 (unsigned)(exclude_length +
1865 1) &&
1866 !strncmp(*exclude_entry, next_file,
1867 exclude_length)) {
1868 // directory found in exclude
1869 // list
1870 no_watch = 1;
1871 }
1872 }
1873 if (!no_watch) {
1874 static int status;
1875 status =
1877 next_file, events,
1878 exclude_list);
1879 // For some errors, we will continue.
1880 if (!status && (EACCES != error) &&
1881 (ENOENT != error) &&
1882 (ELOOP != error)) {
1883 free(next_file);
1884 if (my_path != path)
1885 free(my_path);
1886 closedir(dir);
1887 return 0;
1888 }
1889 } // if !no_watch
1890 free(next_file);
1891 } // if isdir and not islnk
1892 else {
1893 free(next_file);
1894 }
1895 }
1896 ent = readdir(dir);
1897 error = 0;
1898 }
1899
1900 closedir(dir);
1901
1902 int ret = inotifytools_watch_file(my_path, events);
1903 if (my_path != path)
1904 free(my_path);
1905 return ret;
1906}
1907
1919 return error;
1920}
1921
1925static int isdir(char const* path) {
1926 static struct stat my_stat;
1927
1928 if (-1 == lstat(path, &my_stat)) {
1929 if (errno == ENOENT)
1930 return 0;
1931 fprintf(stderr, "Stat failed on %s: %s\n", path,
1932 strerror(errno));
1933 return 0;
1934 }
1935
1936 return S_ISDIR(my_stat.st_mode) && !S_ISLNK(my_stat.st_mode);
1937}
1938
1946 int ret = 0;
1947 rbwalk(tree_filename, get_num, (void*)&ret);
1948 return ret;
1949}
1950
1995int inotifytools_printf(struct inotify_event* event, const char* fmt) {
1996 return inotifytools_fprintf(stdout, event, fmt);
1997}
1998
2045 struct inotify_event* event,
2046 const char* fmt) {
2047 static struct nstring out;
2048 static int ret;
2049 ret = inotifytools_sprintf(&out, event, fmt);
2050 if (-1 != ret)
2051 fwrite(out.buf, sizeof(char), out.len, file);
2052 return ret;
2053}
2054
2108 struct inotify_event* event,
2109 const char* fmt) {
2110 return inotifytools_snprintf(out, MAX_STRLEN, event, fmt);
2111}
2112
2164 int size,
2165 struct inotify_event* event,
2166 const char* fmt) {
2167 const char* eventstr;
2168 static unsigned int i, ind;
2169 static char ch1;
2170 static char timestr[MAX_STRLEN];
2171 static time_t now;
2172
2173 size_t dirnamelen = 0;
2174 const char* eventname;
2175 const char* filename =
2176 inotifytools_filename_from_event(event, &eventname, &dirnamelen);
2177
2178 if (!fmt || 0 == strlen(fmt)) {
2179 error = EINVAL;
2180 return -1;
2181 }
2182 if (strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
2183 error = EMSGSIZE;
2184 return -1;
2185 }
2186
2187 ind = 0;
2188 for (i = 0; i < strlen(fmt) && (int)ind < size - 1; ++i) {
2189 if (fmt[i] != '%') {
2190 out->buf[ind++] = fmt[i];
2191 continue;
2192 }
2193
2194 if (i == strlen(fmt) - 1) {
2195 // last character is %, invalid
2196 error = EINVAL;
2197 return ind;
2198 }
2199
2200 ch1 = fmt[i + 1];
2201
2202 if (ch1 == '%') {
2203 out->buf[ind++] = '%';
2204 ++i;
2205 continue;
2206 }
2207
2208 if (ch1 == '0') {
2209 out->buf[ind++] = '\0';
2210 ++i;
2211 continue;
2212 }
2213
2214 if (ch1 == 'n') {
2215 out->buf[ind++] = '\n';
2216 ++i;
2217 continue;
2218 }
2219
2220 if (ch1 == 'w') {
2221 if (filename && dirnamelen <= size - ind) {
2222 strncpy(&out->buf[ind], filename, dirnamelen);
2223 ind += dirnamelen;
2224 }
2225 ++i;
2226 continue;
2227 }
2228
2229 if (ch1 == 'f') {
2230 if (eventname) {
2231 strncpy(&out->buf[ind], eventname, size - ind);
2232 ind += strlen(eventname);
2233 }
2234 ++i;
2235 continue;
2236 }
2237
2238 if (ch1 == 'c') {
2239 ind += snprintf(&out->buf[ind], size - ind, "%x",
2240 event->cookie);
2241 ++i;
2242 continue;
2243 }
2244
2245 if (ch1 == 'e') {
2246 eventstr = inotifytools_event_to_str(event->mask);
2247 strncpy(&out->buf[ind], eventstr, size - ind);
2248 ind += strlen(eventstr);
2249 ++i;
2250 continue;
2251 }
2252
2253 if (ch1 == 'T') {
2254 if (!timefmt.empty()) {
2255 now = time(0);
2256 struct tm now_tm;
2257 if (!strftime(timestr, MAX_STRLEN - 1,
2258 timefmt.c_str_,
2259 localtime_r(&now, &now_tm))) {
2260 // time format probably invalid
2261 error = EINVAL;
2262 return ind;
2263 }
2264 } else {
2265 timestr[0] = 0;
2266 }
2267
2268 strncpy(&out->buf[ind], timestr, size - ind);
2269 ind += strlen(timestr);
2270 ++i;
2271 continue;
2272 }
2273
2274 // Check if next char in fmt is e
2275 if (i < strlen(fmt) - 2 && fmt[i + 2] == 'e') {
2276 eventstr =
2277 inotifytools_event_to_str_sep(event->mask, ch1);
2278 strncpy(&out->buf[ind], eventstr, size - ind);
2279 ind += strlen(eventstr);
2280 i += 2;
2281 continue;
2282 }
2283
2284 // OK, this wasn't a special format character, just output it as
2285 // normal
2286 if (ind < MAX_STRLEN)
2287 out->buf[ind++] = '%';
2288 if (ind < MAX_STRLEN)
2289 out->buf[ind++] = ch1;
2290 ++i;
2291 }
2292 out->len = ind;
2293
2294 return ind - 1;
2295}
2296
2306void inotifytools_set_printf_timefmt(const char* fmt) {
2307 timefmt.set_size(nasprintf(&timefmt.c_str_, "%s", fmt));
2308}
2309
2310void inotifytools_clear_timefmt() {
2311 timefmt.clear();
2312}
2313
2323 int ret;
2324 if (!read_num_from_file(QUEUE_SIZE_PATH, &ret))
2325 return -1;
2326 return ret;
2327}
2328
2339 int ret;
2340 if (!read_num_from_file(INSTANCES_PATH, &ret))
2341 return -1;
2342 return ret;
2343}
2344
2355 int ret;
2356 if (!read_num_from_file(WATCHES_SIZE_PATH, &ret))
2357 return -1;
2358 return ret;
2359}
2360
2374static int do_ignore_events_by_regex(char const* pattern,
2375 int flags,
2376 int invert,
2377 int recursive) {
2378 if (!pattern) {
2379 if (regex) {
2380 regfree(regex);
2381 free(regex);
2382 regex = 0;
2383 }
2384 return 1;
2385 }
2386
2387 if (regex) {
2388 regfree(regex);
2389 } else {
2390 regex = (regex_t*)malloc(sizeof(regex_t));
2391 }
2392
2393 invert_regexp = invert;
2394 recursive_watch = recursive;
2395
2396 int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2397 if (0 == ret)
2398 return 1;
2399
2400 regfree(regex);
2401 free(regex);
2402 regex = 0;
2403 error = EINVAL;
2404 return 0;
2405}
2406
2418int inotifytools_ignore_events_by_regex(char const* pattern, int flags, int recursive) {
2419 return do_ignore_events_by_regex(pattern, flags, 0, recursive);
2420}
2421
2433int inotifytools_ignore_events_by_inverted_regex(char const* pattern, int flags, int recursive) {
2434 return do_ignore_events_by_regex(pattern, flags, 1, recursive);
2435}
2436
2437int event_compare(const char* p1, const char* p2, const void* config) {
2438 if (!p1 || !p2)
2439 return p1 - p2;
2440 char asc = 1;
2441 long sort_event = (long)config;
2442 if (sort_event == -1) {
2443 sort_event = 0;
2444 asc = 0;
2445 } else if (sort_event < 0) {
2446 sort_event = -sort_event;
2447 asc = 0;
2448 }
2449 unsigned int* i1 = stat_ptr((watch*)p1, sort_event);
2450 unsigned int* i2 = stat_ptr((watch*)p2, sort_event);
2451 if (0 == *i1 - *i2) {
2452 return ((watch*)p1)->wd - ((watch*)p2)->wd;
2453 }
2454 if (asc)
2455 return *i1 - *i2;
2456 else
2457 return *i2 - *i1;
2458}
2459
2460struct rbtree* inotifytools_wd_sorted_by_event(int sort_event) {
2461 struct rbtree* ret =
2462 rbinit(event_compare, (void*)(uintptr_t)sort_event);
2463 RBLIST* all = rbopenlist(tree_wd);
2464 void const* p = rbreadlist(all);
2465 while (p) {
2466 void const* r = rbsearch(p, ret);
2467 niceassert((int)(r == p),
2468 "Couldn't insert watch into new tree");
2469 p = rbreadlist(all);
2470 }
2471 rbcloselist(all);
2472 return ret;
2473}
inotifytools library public interface.
int inotifytools_init(int fanotify, int watch_filesystem, int verbose)
const char * inotifytools_filename_from_wd(int wd)
void inotifytools_set_printf_timefmt(const char *fmt)
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_fprintf(FILE *file, struct inotify_event *event, const char *fmt)
int inotifytools_ignore_events_by_regex(char const *pattern, int flags, int recursive)
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)
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)
char * inotifytools_dirpath_from_event(struct inotify_event *event)
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags, int recursive)
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_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()
int inotifytools_snprintf(struct nstring *out, int size, struct inotify_event *event, const char *fmt)
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
int inotifytools_printf(struct inotify_event *event, const char *fmt)
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, const 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]