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