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__
40 struct 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 
59 struct fanotify_event_info_fid_wo_handle {
60  struct fanotify_event_info_header hdr;
61  __kernel_fsid_t fsid;
62 };
63 
64 struct 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 
163 static int inotify_fd = -1;
164 
165 int collect_stats = 0;
166 
167 struct rbtree* tree_wd = 0;
168 struct rbtree* tree_fid = 0;
169 struct rbtree* tree_filename = 0;
170 static int error = 0;
171 int initialized = 0;
172 int verbosity = 0;
173 int fanotify_mode = 0;
174 int fanotify_mark_type = 0;
175 static pid_t self_pid = 0;
176 
177 struct 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 
200 static str timefmt;
201 static regex_t* regex = 0;
202 /* 0: --exclude[i], 1: --include[i] */
203 static int invert_regexp = 0;
204 
205 static int isdir(char const* path);
206 void record_stats(struct inotify_event const* event);
207 int onestr_to_event(char const* event);
208 
209 #define nasprintf(...) niceassert(-1 != asprintf(__VA_ARGS__), "out of memory")
210 
228 long _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 
247 static void charcat(char* s, const char c) {
248  size_t l = strlen(s);
249  s[l] = c;
250  s[++l] = 0;
251 }
252 
256 static 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 
276 static 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 
282 static 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 
299 static 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 
310 watch* watch_from_wd(int wd) {
311  watch w;
312  w.wd = wd;
313  return (watch*)rbfind(&w, tree_wd);
314 }
315 
319 watch* watch_from_fid(struct fanotify_event_fid* fid) {
320  watch w;
321  w.fid = fid;
322  return (watch*)rbfind(&w, tree_fid);
323 }
324 
328 watch* watch_from_filename(char const* filename) {
329  watch w;
330  w.filename = (char*)filename;
331  return (watch*)rbfind(&w, tree_filename);
332 }
333 
344 int 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 
379 int inotifytools_initialize() {
380  return inotifytools_init(0, 0, 0);
381 }
382 
386 void 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 
399 void 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 
443 struct replace_filename_data {
444  char const* old_name;
445  char const* new_name;
446  size_t old_len;
447 };
448 
452 static 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 
477 static 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 
488 static 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 
524 int 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 
602 int inotifytools_str_to_event(char const* event) {
603  return inotifytools_str_to_event_sep(event, ',');
604 }
605 
617 int 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 
688 char* inotifytools_event_to_str(int events) {
689  return inotifytools_event_to_str_sep(events, ',');
690 }
691 
716 char* 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 
808 static 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 
907 const 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 
936 const 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 
955 const 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 
984 const 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 
1010 char* 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 
1041 int 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 
1065 void 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 
1089 void inotifytools_set_filename_by_filename(char const* oldname,
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 
1121 void 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 
1136 int 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 
1154 watch* 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 
1217 int 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 
1244 int 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 
1266 int 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 
1440 struct 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 
1496 struct 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  }
1594 more_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 
1756 int 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 
1918 static 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 
1988 int inotifytools_printf(struct inotify_event* event, const char* fmt) {
1989  return inotifytools_fprintf(stdout, event, fmt);
1990 }
1991 
2037 int inotifytools_fprintf(FILE* file,
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 
2299 void inotifytools_set_printf_timefmt(const char* fmt) {
2300  timefmt.set_size(nasprintf(&timefmt.c_str_, "%s", fmt));
2301 }
2302 
2303 void 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 
2367 static 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 
2408 int 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 
2428 int 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 
2451 struct 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 }
unsigned int len
Definition: inotifytools.h:22
int inotifytools_remove_watch_by_wd(int wd)
inotifytools library public interface.
int inotifytools_get_max_user_watches()
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, const char *fmt)
const char * inotifytools_filename_from_wd(int wd)
int inotifytools_printf(struct inotify_event *event, const char *fmt)
const char * inotifytools_filename_from_event(struct inotify_event *event, char const **eventname, size_t *dirnamelen)
char * inotifytools_event_to_str(int events)
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
This structure holds string that can contain any character including NULL.
Definition: inotifytools.h:20
int inotifytools_fprintf(FILE *file, struct inotify_event *event, const char *fmt)
int inotifytools_wd_from_filename(char const *filename)
void inotifytools_set_filename_by_wd(int wd, char const *filename)
int inotifytools_watch_files(char const *filenames[], int events)
int inotifytools_get_num_watches()
char * inotifytools_dirpath_from_event(struct inotify_event *event)
int inotifytools_error()
char buf[MAX_STRLEN]
Definition: inotifytools.h:21
int inotifytools_init(int fanotify, int watch_filesystem, int verbose)
int inotifytools_remove_watch_by_filename(char const *filename)
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
void inotifytools_set_printf_timefmt(const char *fmt)
int inotifytools_watch_file(char const *filename, int events)
const char * inotifytools_dirname_from_event(struct inotify_event *event, size_t *dirnamelen)
const char * inotifytools_filename_from_watch(struct watch *w)
int inotifytools_get_max_user_instances()
void inotifytools_cleanup()
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
void inotifytools_replace_filename(char const *oldname, char const *newname)
struct inotify_event * inotifytools_next_event(long int timeout)
int inotifytools_str_to_event(char const *event)
int inotifytools_snprintf(struct nstring *out, int size, struct inotify_event *event, const char *fmt)
int inotifytools_str_to_event_sep(char const *event, char sep)
int inotifytools_get_max_queued_events()
char * inotifytools_event_to_str_sep(int events, char sep)
int inotifytools_watch_recursively(char const *path, int events)