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