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 static int recursive_watch = 0;
166 int collect_stats = 0;
167 
168 struct rbtree* tree_wd = 0;
169 struct rbtree* tree_fid = 0;
170 struct rbtree* tree_filename = 0;
171 static int error = 0;
172 int initialized = 0;
173 int verbosity = 0;
174 int fanotify_mode = 0;
175 int fanotify_mark_type = 0;
176 static pid_t self_pid = 0;
177 
178 struct 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 
201 static str timefmt;
202 static regex_t* regex = 0;
203 /* 0: --exclude[i], 1: --include[i] */
204 static int invert_regexp = 0;
205 
206 static int isdir(char const* path);
207 void record_stats(struct inotify_event const* event);
208 int onestr_to_event(char const* event);
209 
210 #define nasprintf(...) niceassert(-1 != asprintf(__VA_ARGS__), "out of memory")
211 
229 long _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 
248 static void charcat(char* s, const char c) {
249  size_t l = strlen(s);
250  s[l] = c;
251  s[++l] = 0;
252 }
253 
257 static 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 
277 static 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 
283 static 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 
300 static 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 
311 watch* watch_from_wd(int wd) {
312  watch w;
313  w.wd = wd;
314  return (watch*)rbfind(&w, tree_wd);
315 }
316 
320 watch* watch_from_fid(struct fanotify_event_fid* fid) {
321  watch w;
322  w.fid = fid;
323  return (watch*)rbfind(&w, tree_fid);
324 }
325 
329 watch* watch_from_filename(char const* filename) {
330  watch w;
331  w.filename = (char*)filename;
332  return (watch*)rbfind(&w, tree_filename);
333 }
334 
345 int 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 
380 int inotifytools_initialize() {
381  return inotifytools_init(0, 0, 0);
382 }
383 
387 void 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 
400 void 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 
444 struct replace_filename_data {
445  char const* old_name;
446  char const* new_name;
447  size_t old_len;
448 };
449 
453 static 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 
478 static 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 
489 static 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 
525 int 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 
603 int inotifytools_str_to_event(char const* event) {
604  return inotifytools_str_to_event_sep(event, ',');
605 }
606 
618 int 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 
689 char* inotifytools_event_to_str(int events) {
690  return inotifytools_event_to_str_sep(events, ',');
691 }
692 
717 char* 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 
809 static 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 
908 const 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 
937 const 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 
956 const 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 
985 const 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 
1011 char* 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 
1042 int 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 
1066 void 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 
1090 void inotifytools_set_filename_by_filename(char const* oldname,
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 
1122 void 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 
1137 int 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 
1155 watch* 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 
1218 int 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 
1245 int 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 
1267 int 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 
1441 struct 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 
1497 struct 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  }
1595 more_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 
1763 int 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 
1925 static 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 
1995 int inotifytools_printf(struct inotify_event* event, const char* fmt) {
1996  return inotifytools_fprintf(stdout, event, fmt);
1997 }
1998 
2044 int inotifytools_fprintf(FILE* file,
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 
2306 void inotifytools_set_printf_timefmt(const char* fmt) {
2307  timefmt.set_size(nasprintf(&timefmt.c_str_, "%s", fmt));
2308 }
2309 
2310 void 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 
2374 static 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 
2418 int 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 
2433 int 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 
2437 int 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 
2460 struct 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 }
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_ignore_events_by_inverted_regex(char const *pattern, int flags, int recursive)
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)
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()
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_ignore_events_by_regex(char const *pattern, int flags, int recursive)
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)