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