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 <string.h>
21 #include <strings.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <sys/select.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/ioctl.h>
30 #include <unistd.h>
31 #include <dirent.h>
32 #include <time.h>
33 #include <regex.h>
34 #include <setjmp.h>
35 
36 #include "inotifytools/inotify.h"
37 
38 #ifdef __FreeBSD__
39 struct fanotify_event_fid;
40 
41 #define FAN_EVENT_INFO_TYPE_FID 1
42 #define FAN_EVENT_INFO_TYPE_DFID_NAME 2
43 #define FAN_EVENT_INFO_TYPE_DFID 3
44 
45 #else
46 // Linux only
47 #define LINUX_FANOTIFY
48 
49 #include <fcntl.h>
50 #include <sys/vfs.h>
51 #include "inotifytools/fanotify.h"
52 
53 struct fanotify_event_fid {
54  struct fanotify_event_info_fid info;
55  struct file_handle handle;
56 };
57 #endif
58 
145 #define MAX_EVENTS 4096
146 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
147 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
148 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
149 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
150 
151 static int inotify_fd = -1;
152 
153 int collect_stats = 0;
154 
155 struct rbtree *tree_wd = 0;
156 struct rbtree* tree_fid = 0;
157 struct rbtree *tree_filename = 0;
158 static int error = 0;
159 int init = 0;
160 int verbosity = 0;
161 int fanotify_mode = 0;
162 int fanotify_mark_type = 0;
163 static char* timefmt = 0;
164 static regex_t* regex = 0;
165 /* 0: --exclude[i], 1: --include[i] */
166 static int invert_regexp = 0;
167 
168 static int isdir( char const * path );
169 void record_stats( struct inotify_event const * event );
170 int onestr_to_event(char const * event);
171 
172 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
173 
191 void _niceassert( long cond, int line, char const * file,
192  char const * condstr, char const * mesg ) {
193  if ( cond ) return;
194 
195  if ( mesg ) {
196  fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
197  condstr, mesg );
198  }
199  else {
200  fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
201  }
202 }
203 
204 static void charcat(char* s, const char c) {
205  size_t l = strlen(s);
206  s[l] = c;
207  s[++l] = 0;
208 }
209 
213 static int read_num_from_file(char* filename, int* num) {
214  FILE * file = fopen( filename, "r" );
215  if ( !file ) {
216  error = errno;
217  return 0;
218  }
219 
220  if ( EOF == fscanf( file, "%d", num ) ) {
221  error = errno;
222  const int fclose_ret = fclose(file);
223  niceassert(!fclose_ret, 0);
224  return 0;
225  }
226 
227  const int fclose_ret = fclose(file);
228  niceassert(!fclose_ret, 0);
229 
230  return 1;
231 }
232 
233 static int wd_compare(const void* d1, const void* d2, const void* config) {
234  if (!d1 || !d2) return d1 - d2;
235  return ((watch*)d1)->wd - ((watch*)d2)->wd;
236 }
237 
238 static int fid_compare(const void* d1, const void* d2, const void* config) {
239 #ifdef LINUX_FANOTIFY
240  if (!d1 || !d2)
241  return d1 - d2;
242  watch* w1 = (watch*)d1;
243  watch* w2 = (watch*)d2;
244  int n1, n2;
245  n1 = w1->fid->info.hdr.len;
246  n2 = w2->fid->info.hdr.len;
247  if (n1 != n2)
248  return n1 - n2;
249  return memcmp(w1->fid, w2->fid, n1);
250 #else
251  return d1 - d2;
252 #endif
253 }
254 
255 static int filename_compare(const void* d1,
256  const void* d2,
257  const void* config) {
258  if (!d1 || !d2) return d1 - d2;
259  return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
260 }
261 
265 watch *watch_from_wd( int wd ) {
266  watch w;
267  w.wd = wd;
268  return (watch*)rbfind(&w, tree_wd);
269 }
270 
274 watch* watch_from_fid(struct fanotify_event_fid* fid) {
275  watch w;
276  w.fid = fid;
277  return (watch*)rbfind(&w, tree_fid);
278 }
279 
283 watch *watch_from_filename( char const *filename ) {
284  watch w;
285  w.filename = (char*)filename;
286  return (watch*)rbfind(&w, tree_filename);
287 }
288 
299 int inotifytools_init(int fanotify, int watch_filesystem, int verbose) {
300  if (init) return 1;
301 
302  error = 0;
303  verbosity = verbose;
304  // Try to initialise inotify/fanotify
305  if (fanotify) {
306 #ifdef LINUX_FANOTIFY
307  fanotify_mode = 1;
308  fanotify_mark_type =
309  watch_filesystem ? FAN_MARK_FILESYSTEM : FAN_MARK_INODE;
310  inotify_fd =
311  fanotify_init(FAN_REPORT_FID | FAN_REPORT_DFID_NAME, 0);
312 #endif
313  } else {
314  fanotify_mode = 0;
315  inotify_fd = inotify_init();
316  }
317  if (inotify_fd < 0) {
318  error = errno;
319  return 0;
320  }
321 
322  collect_stats = 0;
323  init = 1;
324  tree_wd = rbinit(wd_compare, 0);
325  tree_fid = rbinit(fid_compare, 0);
326  tree_filename = rbinit(filename_compare, 0);
327  timefmt = 0;
328 
329  return 1;
330 }
331 
332 int inotifytools_initialize() {
333  return inotifytools_init(0, 0, 0);
334 }
335 
339 void destroy_watch(watch *w) {
340  if (w->filename) free(w->filename);
341  if (w->fid)
342  free(w->fid);
343  if (w->dirf)
344  close(w->dirf);
345  free(w);
346 }
347 
351 void cleanup_tree(const void *nodep,
352  const VISIT which,
353  const int depth, void* arg) {
354  if (which != endorder && which != leaf) return;
355  watch *w = (watch*)nodep;
356  destroy_watch(w);
357 }
358 
366  if (!init) return;
367 
368  init = 0;
369  close(inotify_fd);
370  collect_stats = 0;
371  error = 0;
372  timefmt = 0;
373 
374  if (regex) {
375  regfree(regex);
376  free(regex);
377  regex = 0;
378  }
379 
380  rbwalk(tree_wd, cleanup_tree, 0);
381  rbdestroy(tree_wd);
382  rbdestroy(tree_fid);
383  rbdestroy(tree_filename);
384  tree_wd = 0;
385  tree_fid = 0;
386  tree_filename = 0;
387 }
388 
389 
390 
394 struct replace_filename_data {
395  char const *old_name;
396  char const *new_name;
397  size_t old_len;
398 };
399 
403 static void replace_filename(const void* nodep,
404  const VISIT which,
405  const int depth,
406  const struct replace_filename_data* data) {
407  if (which != endorder && which != leaf)
408  return;
409  watch *w = (watch*)nodep;
410  char *name;
411  if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
412  nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
413  if (!strcmp( w->filename, data->new_name )) {
414  free(name);
415  } else {
416  rbdelete(w, tree_filename);
417  free( w->filename );
418  w->filename = name;
419  rbsearch(w, tree_filename);
420  }
421  }
422 }
423 
427 static void get_num(const void* nodep,
428  const VISIT which,
429  const int depth,
430  void* arg) {
431  if (which != endorder && which != leaf)
432  return;
433  ++(*((int*)arg));
434 }
435 
463 int inotifytools_str_to_event_sep(char const * event, char sep) {
464  if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
465  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
466  return -1;
467  }
468 
469  int ret, len;
470  char * event1, * event2;
471  static const size_t eventstr_size = 4096;
472  char eventstr[eventstr_size];
473  ret = 0;
474 
475  if ( !event || !event[0] ) return 0;
476 
477  event1 = (char *)event;
478  event2 = strchr( event1, sep );
479  while ( event1 && event1[0] ) {
480  if ( event2 ) {
481  len = event2 - event1;
482  niceassert(len < eventstr_size,
483  "malformed event string (very long)");
484  }
485  else {
486  len = strlen(event1);
487  }
488  if (len > eventstr_size - 1)
489  len = eventstr_size - 1;
490 
491  strncpy(eventstr, event1, len);
492 
493  eventstr[len] = 0;
494 
495  int ret1 = onestr_to_event(eventstr);
496  if ( 0 == ret1 || -1 == ret1 ) {
497  ret = ret1;
498  break;
499  }
500  ret |= ret1;
501 
502  event1 = event2;
503  if ( event1 && event1[0] ) {
504  // jump over 'sep' character
505  ++event1;
506  // if last character was 'sep'...
507  if ( !event1[0] ) return 0;
508  event2 = strchr( event1, sep );
509  }
510  }
511 
512  return ret;
513 }
514 
538 int inotifytools_str_to_event(char const * event) {
539  return inotifytools_str_to_event_sep( event, ',' );
540 }
541 
553 int onestr_to_event(char const * event)
554 {
555  static int ret;
556  ret = -1;
557 
558  if ( !event || !event[0] )
559  ret = 0;
560  else if ( 0 == strcasecmp(event, "ACCESS") )
561  ret = IN_ACCESS;
562  else if ( 0 == strcasecmp(event, "MODIFY") )
563  ret = IN_MODIFY;
564  else if ( 0 == strcasecmp(event, "ATTRIB") )
565  ret = IN_ATTRIB;
566  else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
567  ret = IN_CLOSE_WRITE;
568  else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
569  ret = IN_CLOSE_NOWRITE;
570  else if ( 0 == strcasecmp(event, "OPEN") )
571  ret = IN_OPEN;
572  else if ( 0 == strcasecmp(event, "MOVED_FROM") )
573  ret = IN_MOVED_FROM;
574  else if ( 0 == strcasecmp(event, "MOVED_TO") )
575  ret = IN_MOVED_TO;
576  else if ( 0 == strcasecmp(event, "CREATE") )
577  ret = IN_CREATE;
578  else if ( 0 == strcasecmp(event, "DELETE") )
579  ret = IN_DELETE;
580  else if ( 0 == strcasecmp(event, "DELETE_SELF") )
581  ret = IN_DELETE_SELF;
582  else if ( 0 == strcasecmp(event, "UNMOUNT") )
583  ret = IN_UNMOUNT;
584  else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
585  ret = IN_Q_OVERFLOW;
586  else if ( 0 == strcasecmp(event, "IGNORED") )
587  ret = IN_IGNORED;
588  else if ( 0 == strcasecmp(event, "CLOSE") )
589  ret = IN_CLOSE;
590  else if ( 0 == strcasecmp(event, "MOVE_SELF") )
591  ret = IN_MOVE_SELF;
592  else if ( 0 == strcasecmp(event, "MOVE") )
593  ret = IN_MOVE;
594  else if ( 0 == strcasecmp(event, "ISDIR") )
595  ret = IN_ISDIR;
596  else if ( 0 == strcasecmp(event, "ONESHOT") )
597  ret = IN_ONESHOT;
598  else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
599  ret = IN_ALL_EVENTS;
600 
601  return ret;
602 }
603 
625 char * inotifytools_event_to_str(int events) {
626  return inotifytools_event_to_str_sep(events, ',');
627 }
628 
653 char * inotifytools_event_to_str_sep(int events, char sep)
654 {
655  static char ret[1024];
656  ret[0] = '\0';
657  ret[1] = '\0';
658 
659  if ( IN_ACCESS & events ) {
660  charcat(ret, sep);
661  strncat(ret, "ACCESS", 7);
662  }
663  if ( IN_MODIFY & events ) {
664  charcat(ret, sep);
665  strncat(ret, "MODIFY", 7);
666  }
667  if ( IN_ATTRIB & events ) {
668  charcat(ret, sep);
669  strncat(ret, "ATTRIB", 7);
670  }
671  if ( IN_CLOSE_WRITE & events ) {
672  charcat(ret, sep);
673  strncat(ret, "CLOSE_WRITE", 12);
674  }
675  if ( IN_CLOSE_NOWRITE & events ) {
676  charcat(ret, sep);
677  strncat(ret, "CLOSE_NOWRITE", 14);
678  }
679  if ( IN_OPEN & events ) {
680  charcat(ret, sep);
681  strncat(ret, "OPEN", 5);
682  }
683  if ( IN_MOVED_FROM & events ) {
684  charcat(ret, sep);
685  strncat(ret, "MOVED_FROM", 11);
686  }
687  if ( IN_MOVED_TO & events ) {
688  charcat(ret, sep);
689  strncat(ret, "MOVED_TO", 9);
690  }
691  if ( IN_CREATE & events ) {
692  charcat(ret, sep);
693  strncat(ret, "CREATE", 7);
694  }
695  if ( IN_DELETE & events ) {
696  charcat(ret, sep);
697  strncat(ret, "DELETE", 7);
698  }
699  if ( IN_DELETE_SELF & events ) {
700  charcat(ret, sep);
701  strncat(ret, "DELETE_SELF", 12);
702  }
703  if ( IN_UNMOUNT & events ) {
704  charcat(ret, sep);
705  strncat(ret, "UNMOUNT", 8);
706  }
707  if ( IN_Q_OVERFLOW & events ) {
708  charcat(ret, sep);
709  strncat(ret, "Q_OVERFLOW", 11);
710  }
711  if ( IN_IGNORED & events ) {
712  charcat(ret, sep);
713  strncat(ret, "IGNORED", 8);
714  }
715  if ( IN_CLOSE & events ) {
716  charcat(ret, sep);
717  strncat(ret, "CLOSE", 6);
718  }
719  if ( IN_MOVE_SELF & events ) {
720  charcat(ret, sep);
721  strncat(ret, "MOVE_SELF", 10);
722  }
723  if ( IN_ISDIR & events ) {
724  charcat(ret, sep);
725  strncat(ret, "ISDIR", 6);
726  }
727  if ( IN_ONESHOT & events ) {
728  charcat(ret, sep);
729  strncat(ret, "ONESHOT", 8);
730  }
731 
732  // Maybe we didn't match any... ?
733  if (ret[0] == '\0') {
734  niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
735  }
736 
737  return &ret[1];
738 }
739 
746 static const char* inotifytools_filename_from_fid(
747  struct fanotify_event_fid* fid) {
748 #ifdef LINUX_FANOTIFY
749  static char filename[PATH_MAX];
750  struct fanotify_event_fid fsid = {};
751  int dirf = 0, mount_fd = AT_FDCWD;
752  int len = 0, name_len = 0;
753 
754  // Match mount_fd from fid->fsid (and null fhandle)
755  fsid.info.fsid.val[0] = fid->info.fsid.val[0];
756  fsid.info.fsid.val[1] = fid->info.fsid.val[1];
757  fsid.info.hdr.info_type = FAN_EVENT_INFO_TYPE_FID;
758  fsid.info.hdr.len = sizeof(fsid);
759  watch* mnt = watch_from_fid(&fsid);
760  if (mnt)
761  mount_fd = mnt->dirf;
762 
763  if (fid->info.hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
764  int fid_len = sizeof(*fid) + fid->handle.handle_bytes;
765 
766  name_len = fid->info.hdr.len - fid_len;
767  if (name_len && !fid->handle.f_handle[fid->handle.handle_bytes])
768  name_len = 0; // empty name??
769  }
770 
771  // Try to get path from file handle
772  dirf = open_by_handle_at(mount_fd, &fid->handle, 0);
773  if (dirf > 0) {
774  // Got path by handle
775  } else if (fanotify_mark_type == FAN_MARK_FILESYSTEM) {
776  fprintf(stderr, "Failed to decode directory fid.\n");
777  return NULL;
778  } else if (name_len) {
779  // For recursive watch look for watch by fid without the name
780  fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID;
781  fid->info.hdr.len -= name_len;
782 
783  watch* w = watch_from_fid(fid);
784 
785  fid->info.hdr.info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
786  fid->info.hdr.len += name_len;
787 
788  if (!w) {
789  fprintf(stderr,
790  "Failed to lookup path by directory fid.\n");
791  return NULL;
792  }
793 
794  dirf = w->dirf ? dup(w->dirf) : -1;
795  if (dirf < 0) {
796  fprintf(stderr, "Failed to get directory fd.\n");
797  return NULL;
798  }
799  } else {
800  // Fallthrough to stored filename
801  return NULL;
802  }
803  char sym[30];
804  sprintf(sym, "/proc/self/fd/%d", dirf);
805 
806  // PATH_MAX - 2 because we have to append two characters to this path,
807  // '/' and 0
808  len = readlink(sym, filename, PATH_MAX - 2);
809  if (len < 0) {
810  close(dirf);
811  fprintf(stderr, "Failed to resolve path from directory fd.\n");
812  return NULL;
813  }
814 
815  filename[len++] = '/';
816  filename[len] = 0;
817 
818  if (name_len > 0) {
819  const char* name = (const char*)fid->handle.f_handle +
820  fid->handle.handle_bytes;
821  int deleted = faccessat(dirf, name, F_OK, AT_SYMLINK_NOFOLLOW);
822  if (deleted && errno != ENOENT) {
823  fprintf(stderr, "Failed to access file %s (%s).\n",
824  name, strerror(errno));
825  close(dirf);
826  return NULL;
827  }
828  memcpy(filename + len, name, name_len);
829  if (deleted)
830  strncat(filename, " (deleted)", 11);
831  }
832  close(dirf);
833  return filename;
834 #else
835  return NULL;
836 #endif
837 }
838 
845 const char* inotifytools_filename_from_watch(watch* w) {
846  if (!w)
847  return "";
848  if (!w->fid || !fanotify_mark_type)
849  return w->filename;
850 
851  return inotifytools_filename_from_fid(w->fid) ?: w->filename;
852 }
853 
874 const char* inotifytools_filename_from_wd(int wd) {
875  niceassert( init, "inotifytools_initialize not called yet" );
876  if (!wd)
877  return "";
878  watch *w = watch_from_wd(wd);
879  if (!w)
880  return "";
881 
883 }
884 
893 const char* inotifytools_dirname_from_event(struct inotify_event* event,
894  size_t* dirnamelen) {
895  const char* filename = inotifytools_filename_from_wd(event->wd);
896  char* dirsep;
897 
898  if (!filename) {
899  return NULL;
900  }
901 
902  dirsep = strrchr(filename, '/');
903  if (!dirsep) {
904  return NULL;
905  }
906 
907  *dirnamelen = dirsep - filename + 1;
908  return filename;
909 }
910 
919 char* inotifytools_dirpath_from_event(struct inotify_event* event) {
920  const char* filename = inotifytools_filename_from_wd(event->wd);
921 
922  if (!filename || !*filename || !(event->mask & IN_ISDIR)) {
923  return NULL;
924  }
925 
926  /*
927  * fanotify watch->filename includes the name, so no need to add the
928  * event->name again.
929  */
930  char* path;
931  nasprintf(&path, "%s%s/", filename, fanotify_mode ? "" : event->name);
932 
933  return path;
934 }
935 
950 int inotifytools_wd_from_filename( char const * filename ) {
951  niceassert( init, "inotifytools_initialize not called yet" );
952  if (!filename || !*filename)
953  return -1;
954  watch *w = watch_from_filename(filename);
955  if (!w) return -1;
956  return w->wd;
957 }
958 
973 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
974  niceassert( init, "inotifytools_initialize not called yet" );
975  watch *w = watch_from_wd(wd);
976  if (!w) return;
977  if (w->filename) free(w->filename);
978  w->filename = strdup(filename);
979 }
980 
995 void inotifytools_set_filename_by_filename( char const * oldname,
996  char const * newname ) {
997  watch *w = watch_from_filename(oldname);
998  if (!w) return;
999  if (w->filename) free(w->filename);
1000  w->filename = strdup(newname);
1001 }
1002 
1025 void inotifytools_replace_filename( char const * oldname,
1026  char const * newname ) {
1027  if (!oldname || !newname)
1028  return;
1029  if (!*oldname || !*newname)
1030  return;
1031  struct replace_filename_data data;
1032  data.old_name = oldname;
1033  data.new_name = newname;
1034  data.old_len = strlen(oldname);
1035  rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
1036 }
1037 
1041 int remove_inotify_watch(watch *w) {
1042  error = 0;
1043  // There is no kernel object representing the watch with fanotify
1044  if (w->fid)
1045  return 0;
1046  int status = inotify_rm_watch( inotify_fd, w->wd );
1047  if ( status < 0 ) {
1048  fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
1049  strerror(status) );
1050  error = status;
1051  return 0;
1052  }
1053  return 1;
1054 }
1055 
1059 watch* create_watch(int wd,
1060  struct fanotify_event_fid* fid,
1061  const char* filename,
1062  int dirf) {
1063  if (wd < 0 || !filename)
1064  return 0;
1065 
1066  watch *w = (watch*)calloc(1, sizeof(watch));
1067  if (!w) {
1068  fprintf(stderr, "Failed to allocate watch.\n");
1069  return NULL;
1070  }
1071  w->wd = wd ?: (unsigned long)fid;
1072  w->fid = fid;
1073  w->dirf = dirf;
1074  w->filename = strdup(filename);
1075  rbsearch(w, tree_wd);
1076  if (fid)
1077  rbsearch(w, tree_fid);
1078 
1079  rbsearch(w, tree_filename);
1080  return w;
1081 }
1082 
1096  niceassert( init, "inotifytools_initialize not called yet" );
1097  watch *w = watch_from_wd(wd);
1098  if (!w) return 1;
1099 
1100  if (!remove_inotify_watch(w)) return 0;
1101  rbdelete(w, tree_wd);
1102  if (w->fid)
1103  rbdelete(w, tree_fid);
1104  rbdelete(w, tree_filename);
1105  destroy_watch(w);
1106  return 1;
1107 }
1108 
1120 int inotifytools_remove_watch_by_filename( char const * filename ) {
1121  niceassert( init, "inotifytools_initialize not called yet" );
1122  watch *w = watch_from_filename(filename);
1123  if (!w) return 1;
1124 
1125  if (!remove_inotify_watch(w)) return 0;
1126  rbdelete(w, tree_wd);
1127  if (w->fid)
1128  rbdelete(w, tree_fid);
1129  rbdelete(w, tree_filename);
1130  destroy_watch(w);
1131  return 1;
1132 }
1133 
1145 int inotifytools_watch_file(char const* filename, int events) {
1146  static char const* filenames[2];
1147  filenames[0] = filename;
1148  filenames[1] = NULL;
1149  return inotifytools_watch_files( filenames, events );
1150 }
1151 
1167 int inotifytools_watch_files(char const* filenames[], int events) {
1168  niceassert( init, "inotifytools_initialize not called yet" );
1169  error = 0;
1170 
1171  static int i;
1172  for ( i = 0; filenames[i]; ++i ) {
1173  int wd = -1;
1174  if (fanotify_mode) {
1175 #ifdef LINUX_FANOTIFY
1176  unsigned int flags = FAN_MARK_ADD | fanotify_mark_type;
1177 
1178  if (events & IN_DONT_FOLLOW) {
1179  events &= ~IN_DONT_FOLLOW;
1180  flags |= FAN_MARK_DONT_FOLLOW;
1181  }
1182 
1183  wd = fanotify_mark(inotify_fd, flags,
1184  events | FAN_EVENT_ON_CHILD,
1185  AT_FDCWD, filenames[i]);
1186 #endif
1187  } else {
1188  wd =
1189  inotify_add_watch(inotify_fd, filenames[i], events);
1190  }
1191  if ( wd < 0 ) {
1192  if ( wd == -1 ) {
1193  error = errno;
1194  return 0;
1195  } // if ( wd == -1 )
1196  else {
1197  fprintf( stderr, "Failed to watch %s: returned wd was %d "
1198  "(expected -1 or >0 )", filenames[i], wd );
1199  // no appropriate value for error
1200  return 0;
1201  } // else
1202  } // if ( wd < 0 )
1203 
1204  const char* filename = filenames[i];
1205  size_t filenamelen = strlen(filename);
1206  char* dirname;
1207  int dirf = 0;
1208  // Always end filename with / if it is a directory
1209  if (!isdir(filename)) {
1210  dirname = NULL;
1211  } else if (filename[filenamelen - 1] == '/') {
1212  dirname = strdup(filename);
1213  } else {
1214  nasprintf(&dirname, "%s/", filename);
1215  filename = dirname;
1216  filenamelen++;
1217  }
1218 
1219  struct fanotify_event_fid* fid = NULL;
1220 #ifdef LINUX_FANOTIFY
1221  if (!wd) {
1222  fid = calloc(1, sizeof(*fid) + MAX_FID_LEN);
1223  if (!fid) {
1224  fprintf(stderr, "Failed to allocate fid");
1225  free(dirname);
1226  return 0;
1227  }
1228 
1229  struct statfs buf;
1230  if (statfs(filenames[i], &buf)) {
1231  free(fid);
1232  fprintf(stderr, "Statfs failed on %s: %s\n",
1233  filenames[i], strerror(errno));
1234  free(dirname);
1235  return 0;
1236  }
1237  memcpy(&fid->info.fsid, &buf.f_fsid,
1238  sizeof(__kernel_fsid_t));
1239 
1240  // Hash mount_fd with fid->fsid (and null fhandle)
1241  int ret, mntid;
1242  watch* mnt = dirname ? watch_from_fid(fid) : NULL;
1243  if (dirname && !mnt) {
1244  struct fanotify_event_fid* fsid;
1245 
1246  fsid = calloc(1, sizeof(*fsid));
1247  if (!fsid) {
1248  free(fid);
1249  fprintf(stderr,
1250  "Failed to allocate fsid");
1251  free(dirname);
1252  return 0;
1253  }
1254  fsid->info.fsid.val[0] = fid->info.fsid.val[0];
1255  fsid->info.fsid.val[1] = fid->info.fsid.val[1];
1256  fsid->info.hdr.info_type =
1257  FAN_EVENT_INFO_TYPE_FID;
1258  fsid->info.hdr.len = sizeof(*fsid);
1259  mntid = open(dirname, O_RDONLY);
1260  if (mntid < 0) {
1261  free(fid);
1262  free(fsid);
1263  fprintf(stderr,
1264  "Failed to open %s: %s\n",
1265  dirname, strerror(errno));
1266  free(dirname);
1267  return 0;
1268  }
1269  // Hash mount_fd without terminating /
1270  dirname[filenamelen - 1] = 0;
1271  create_watch(0, fsid, dirname, mntid);
1272  dirname[filenamelen - 1] = '/';
1273  }
1274 
1275  fid->handle.handle_bytes = MAX_FID_LEN;
1276  ret = name_to_handle_at(AT_FDCWD, filenames[i],
1277  (void*)&fid->handle, &mntid, 0);
1278  if (ret || fid->handle.handle_bytes > MAX_FID_LEN) {
1279  free(fid);
1280  fprintf(stderr, "Encode fid failed on %s: %s\n",
1281  filenames[i], strerror(errno));
1282  free(dirname);
1283  return 0;
1284  }
1285  fid->info.hdr.info_type = dirname
1286  ? FAN_EVENT_INFO_TYPE_DFID
1287  : FAN_EVENT_INFO_TYPE_FID;
1288  fid->info.hdr.len =
1289  sizeof(*fid) + fid->handle.handle_bytes;
1290  if (dirname) {
1291  dirf = open(dirname, O_PATH);
1292  if (dirf < 0) {
1293  free(fid);
1294  fprintf(stderr,
1295  "Failed to open %s: %s\n",
1296  dirname, strerror(errno));
1297  free(dirname);
1298  return 0;
1299  }
1300  }
1301  }
1302 #endif
1303  create_watch(wd, fid, filename, dirf);
1304  free(dirname);
1305  } // for
1306 
1307  return 1;
1308 }
1309 
1336 struct inotify_event * inotifytools_next_event( long int timeout ) {
1337  if (!timeout) {
1338  timeout = -1;
1339  }
1340 
1341  return inotifytools_next_events( timeout, 1 );
1342 }
1343 
1344 
1393 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1394  niceassert( init, "inotifytools_initialize not called yet" );
1395  niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1396 
1397  if ( num_events < 1 ) return NULL;
1398 
1399  // second half of event[] buffer is for fanotify->inotify conversion
1400  static struct inotify_event event[2 * MAX_EVENTS];
1401  static struct inotify_event * ret;
1402  static int first_byte = 0;
1403  static ssize_t bytes;
1404  static ssize_t this_bytes;
1405  static jmp_buf jmp;
1406  static struct nstring match_name;
1407  static char match_name_string[MAX_STRLEN+1];
1408 
1409  setjmp(jmp);
1410 
1411  error = 0;
1412 
1413  // first_byte is index into event buffer
1414  if ( first_byte != 0
1415  && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1416 
1417  ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1418  if (!fanotify_mode &&
1419  first_byte + sizeof(*ret) + ret->len > bytes) {
1420  // oh... no. this can't be happening. An incomplete event.
1421  // Copy what we currently have into first element, call self to
1422  // read remainder.
1423  // oh, and they BETTER NOT overlap.
1424  // Boy I hope this code works.
1425  // But I think this can never happen due to how inotify is written.
1426  niceassert( (long)((char *)&event[0] +
1427  sizeof(struct inotify_event) +
1428  event[0].len) <= (long)ret,
1429  "extremely unlucky user, death imminent" );
1430  // how much of the event do we have?
1431  bytes = (char *)&event[0] + bytes - (char *)ret;
1432  memcpy( &event[0], ret, bytes );
1433  return inotifytools_next_events( timeout, num_events );
1434  }
1435  this_bytes = 0;
1436  goto more_events;
1437 
1438  }
1439 
1440  else if ( first_byte == 0 ) {
1441  bytes = 0;
1442  }
1443 
1444 
1445  static unsigned int bytes_to_read;
1446  static int rc;
1447  static fd_set read_fds;
1448 
1449  static struct timeval read_timeout;
1450  read_timeout.tv_sec = timeout;
1451  read_timeout.tv_usec = 0;
1452  static struct timeval * read_timeout_ptr;
1453  read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1454 
1455  FD_ZERO(&read_fds);
1456  FD_SET(inotify_fd, &read_fds);
1457  rc = select(inotify_fd + 1, &read_fds,
1458  NULL, NULL, read_timeout_ptr);
1459  if ( rc < 0 ) {
1460  // error
1461  error = errno;
1462  return NULL;
1463  }
1464  else if ( rc == 0 ) {
1465  // timeout
1466  return NULL;
1467  }
1468 
1469  // wait until we have enough bytes to read
1470  do {
1471  rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1472  } while ( !rc &&
1473  bytes_to_read < sizeof(struct inotify_event)*num_events );
1474 
1475  if ( rc == -1 ) {
1476  error = errno;
1477  return NULL;
1478  }
1479 
1480  this_bytes = read(inotify_fd, &event[0] + bytes,
1481  sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1482  if ( this_bytes < 0 ) {
1483  error = errno;
1484  return NULL;
1485  }
1486  if ( this_bytes == 0 ) {
1487  fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1488  "events occurred at once.\n");
1489  return NULL;
1490  }
1491 more_events:
1492  ret = (struct inotify_event*)((char*)&event[0] + first_byte);
1493 #ifdef LINUX_FANOTIFY
1494  // convert fanotify events to inotify events
1495  if (fanotify_mode) {
1496  struct fanotify_event_metadata* meta = (void*)ret;
1497  struct fanotify_event_info_fid* info = (void*)(meta + 1);
1498  struct fanotify_event_fid* fid = NULL;
1499  const char* name = "";
1500  int fid_len = 0;
1501  int name_len = 0;
1502 
1503  first_byte += meta->event_len;
1504 
1505  if (meta->event_len > sizeof(*meta)) {
1506  switch (info->hdr.info_type) {
1507  case FAN_EVENT_INFO_TYPE_FID:
1508  case FAN_EVENT_INFO_TYPE_DFID:
1509  case FAN_EVENT_INFO_TYPE_DFID_NAME:
1510  fid = (void*)info;
1511  fid_len = sizeof(*fid) +
1512  fid->handle.handle_bytes;
1513  if (info->hdr.info_type ==
1514  FAN_EVENT_INFO_TYPE_DFID_NAME) {
1515  name_len =
1516  info->hdr.len - fid_len;
1517  }
1518  if (name_len > 0) {
1519  name =
1520  (const char*)
1521  fid->handle.f_handle +
1522  fid->handle.handle_bytes;
1523  }
1524  // Convert zero padding to zero
1525  // name_len. For some events on
1526  // directories, the fid is that of the
1527  // dir and name is ".". Do not include
1528  // "." name in fid hash, but keep it for
1529  // debug print.
1530  if (name_len &&
1531  (!*name || !strcmp(name, "."))) {
1532  info->hdr.len -= name_len;
1533  name_len = 0;
1534  }
1535  break;
1536  }
1537  }
1538  if (!fid) {
1539  fprintf(stderr, "No fid in fanotify event.\n");
1540  return NULL;
1541  }
1542  if (verbosity > 1) {
1543  printf(
1544  "fanotify_event: bytes=%zd, first_byte=%d, "
1545  "this_bytes=%zd, event_len=%u, fid_len=%d, "
1546  "name_len=%d, name=%s\n",
1547  bytes, first_byte, this_bytes, meta->event_len,
1548  fid_len, name_len, name);
1549  }
1550 
1551  ret = &event[MAX_EVENTS];
1552  watch* w = watch_from_fid(fid);
1553  if (!w) {
1554  struct fanotify_event_fid* newfid =
1555  calloc(1, info->hdr.len);
1556  if (!newfid) {
1557  fprintf(stderr, "Failed to allocate fid.\n");
1558  return NULL;
1559  }
1560  memcpy(newfid, fid, info->hdr.len);
1561  const char* filename =
1562  inotifytools_filename_from_fid(fid);
1563  if (filename) {
1564  w = create_watch(0, newfid, filename, 0);
1565  if (!w) {
1566  free(newfid);
1567  return NULL;
1568  }
1569  }
1570 
1571  if (verbosity) {
1572  unsigned long id;
1573  memcpy((void*)&id, fid->handle.f_handle,
1574  sizeof(id));
1575  printf("[fid=%x.%x.%lx;name='%s'] %s\n",
1576  fid->info.fsid.val[0],
1577  fid->info.fsid.val[1], id, name,
1578  filename ?: "");
1579  }
1580  }
1581  ret->wd = w ? w->wd : 0;
1582  ret->mask = (uint32_t)meta->mask;
1583  ret->len = name_len;
1584  if (name_len > 0)
1585  memcpy(ret->name, name, name_len);
1586  } else {
1587  first_byte += sizeof(struct inotify_event) + ret->len;
1588  }
1589 #endif
1590 
1591  bytes += this_bytes;
1592  niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1593  "almost certainly screw up." );
1594  if ( first_byte == bytes ) {
1595  first_byte = 0;
1596  }
1597 
1598  if (regex) {
1599  inotifytools_snprintf(&match_name, MAX_STRLEN, ret, "%w%f");
1600  memcpy(&match_name_string, &match_name.buf, match_name.len);
1601  match_name_string[match_name.len] = '\0';
1602  if (0 == regexec(regex, match_name_string, 0, 0, 0)) {
1603  if (!invert_regexp)
1604  longjmp(jmp, 0);
1605  } else {
1606  if (invert_regexp)
1607  longjmp(jmp, 0);
1608  }
1609  }
1610 
1611  if (collect_stats) {
1612  record_stats(ret);
1613  }
1614 
1615  return ret;
1616 }
1617 
1643 int inotifytools_watch_recursively(char const* path, int events) {
1644  return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1645 }
1646 
1680  int events,
1681  char const** exclude_list) {
1682  niceassert( init, "inotifytools_initialize not called yet" );
1683 
1684  DIR * dir;
1685  char * my_path;
1686  error = 0;
1687  dir = opendir( path );
1688  if ( !dir ) {
1689  // If not a directory, don't need to do anything special
1690  if ( errno == ENOTDIR ) {
1691  return inotifytools_watch_file( path, events );
1692  }
1693  else {
1694  error = errno;
1695  return 0;
1696  }
1697  }
1698 
1699  if ( path[strlen(path)-1] != '/' ) {
1700  nasprintf( &my_path, "%s/", path );
1701  }
1702  else {
1703  my_path = (char *)path;
1704  }
1705 
1706  static struct dirent * ent;
1707  char * next_file;
1708  static struct stat64 my_stat;
1709  ent = readdir( dir );
1710  // Watch each directory within this directory
1711  while ( ent ) {
1712  if ( (0 != strcmp( ent->d_name, "." )) &&
1713  (0 != strcmp( ent->d_name, ".." )) ) {
1714  nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1715  if ( -1 == lstat64( next_file, &my_stat ) ) {
1716  error = errno;
1717  free( next_file );
1718  if ( errno != EACCES ) {
1719  error = errno;
1720  if ( my_path != path ) free( my_path );
1721  closedir( dir );
1722  return 0;
1723  }
1724  }
1725  else if ( S_ISDIR( my_stat.st_mode ) &&
1726  !S_ISLNK( my_stat.st_mode )) {
1727  free( next_file );
1728  nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1729  static unsigned int no_watch;
1730  static char const** exclude_entry;
1731 
1732  no_watch = 0;
1733  for (exclude_entry = exclude_list;
1734  exclude_entry && *exclude_entry && !no_watch;
1735  ++exclude_entry) {
1736  static int exclude_length;
1737 
1738  exclude_length = strlen(*exclude_entry);
1739  if ((*exclude_entry)[exclude_length-1] == '/') {
1740  --exclude_length;
1741  }
1742  if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1743  !strncmp(*exclude_entry, next_file, exclude_length)) {
1744  // directory found in exclude list
1745  no_watch = 1;
1746  }
1747  }
1748  if (!no_watch) {
1749  static int status;
1751  next_file,
1752  events,
1753  exclude_list );
1754  // For some errors, we will continue.
1755  if ( !status && (EACCES != error) && (ENOENT != error) &&
1756  (ELOOP != error) ) {
1757  free( next_file );
1758  if ( my_path != path ) free( my_path );
1759  closedir( dir );
1760  return 0;
1761  }
1762  } // if !no_watch
1763  free( next_file );
1764  } // if isdir and not islnk
1765  else {
1766  free( next_file );
1767  }
1768  }
1769  ent = readdir( dir );
1770  error = 0;
1771  }
1772 
1773  closedir( dir );
1774 
1775  int ret = inotifytools_watch_file( my_path, events );
1776  if ( my_path != path ) free( my_path );
1777  return ret;
1778 }
1779 
1791  return error;
1792 }
1793 
1797 static int isdir( char const * path ) {
1798  static struct stat64 my_stat;
1799 
1800  if ( -1 == lstat64( path, &my_stat ) ) {
1801  if (errno == ENOENT) return 0;
1802  fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1803  return 0;
1804  }
1805 
1806  return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1807 }
1808 
1809 
1817  int ret = 0;
1818  rbwalk(tree_filename, get_num, (void*)&ret);
1819  return ret;
1820 }
1821 
1866 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1867  return inotifytools_fprintf( stdout, event, fmt );
1868 }
1869 
1915 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1916  static struct nstring out;
1917  static int ret;
1918  ret = inotifytools_sprintf( &out, event, fmt );
1919  if ( -1 != ret ) fwrite( out.buf, sizeof(char), out.len, file );
1920  return ret;
1921 }
1922 
1975 int inotifytools_sprintf( struct nstring * out, struct inotify_event* event, char* fmt ) {
1976  return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1977 }
1978 
1979 
2030 int inotifytools_snprintf( struct nstring * out, int size,
2031  struct inotify_event* event, char* fmt ) {
2032  static const char* filename;
2033  static char *eventname, *eventstr;
2034  static unsigned int i, ind;
2035  static char ch1;
2036  static char timestr[MAX_STRLEN];
2037  static time_t now;
2038 
2039  if ( event->len > 0 ) {
2040  eventname = event->name;
2041  }
2042  else {
2043  eventname = NULL;
2044  }
2045 
2046  size_t dirnamelen = 0;
2047  filename = inotifytools_dirname_from_event(event, &dirnamelen);
2048 
2049  if ( !fmt || 0 == strlen(fmt) ) {
2050  error = EINVAL;
2051  return -1;
2052  }
2053  if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
2054  error = EMSGSIZE;
2055  return -1;
2056  }
2057 
2058  ind = 0;
2059  for ( i = 0; i < strlen(fmt) &&
2060  (int)ind < size - 1; ++i ) {
2061  if ( fmt[i] != '%' ) {
2062  out->buf[ind++] = fmt[i];
2063  continue;
2064  }
2065 
2066  if ( i == strlen(fmt) - 1 ) {
2067  // last character is %, invalid
2068  error = EINVAL;
2069  return ind;
2070  }
2071 
2072  ch1 = fmt[i+1];
2073 
2074  if ( ch1 == '%' ) {
2075  out->buf[ind++] = '%';
2076  ++i;
2077  continue;
2078  }
2079 
2080  if ( ch1 == '0' ) {
2081  out->buf[ind++] = '\0';
2082  ++i;
2083  continue;
2084  }
2085 
2086  if ( ch1 == 'n' ) {
2087  out->buf[ind++] = '\n';
2088  ++i;
2089  continue;
2090  }
2091 
2092  if ( ch1 == 'w' ) {
2093  if (filename && dirnamelen <= size - ind) {
2094  strncpy(&out->buf[ind], filename, dirnamelen);
2095  ind += dirnamelen;
2096  }
2097  ++i;
2098  continue;
2099  }
2100 
2101  if ( ch1 == 'f' ) {
2102  if ( eventname ) {
2103  strncpy( &out->buf[ind], eventname, size - ind );
2104  ind += strlen(eventname);
2105  }
2106  ++i;
2107  continue;
2108  }
2109 
2110  if ( ch1 == 'c' ) {
2111  ind += snprintf( &out->buf[ind], size-ind, "%x", event->cookie);
2112  ++i;
2113  continue;
2114  }
2115 
2116  if ( ch1 == 'e' ) {
2117  eventstr = inotifytools_event_to_str( event->mask );
2118  strncpy( &out->buf[ind], eventstr, size - ind );
2119  ind += strlen(eventstr);
2120  ++i;
2121  continue;
2122  }
2123 
2124  if ( ch1 == 'T' ) {
2125 
2126  if ( timefmt ) {
2127  now = time(0);
2128  struct tm now_tm;
2129  if (!strftime(timestr, MAX_STRLEN - 1, timefmt,
2130  localtime_r(&now, &now_tm))) {
2131  // time format probably invalid
2132  error = EINVAL;
2133  return ind;
2134  }
2135  }
2136  else {
2137  timestr[0] = 0;
2138  }
2139 
2140  strncpy( &out->buf[ind], timestr, size - ind );
2141  ind += strlen(timestr);
2142  ++i;
2143  continue;
2144  }
2145 
2146  // Check if next char in fmt is e
2147  if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
2148  eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
2149  strncpy( &out->buf[ind], eventstr, size - ind );
2150  ind += strlen(eventstr);
2151  i += 2;
2152  continue;
2153  }
2154 
2155  // OK, this wasn't a special format character, just output it as normal
2156  if ( ind < MAX_STRLEN ) out->buf[ind++] = '%';
2157  if ( ind < MAX_STRLEN ) out->buf[ind++] = ch1;
2158  ++i;
2159  }
2160  out->len = ind;
2161 
2162  return ind - 1;
2163 }
2164 
2175  timefmt = fmt;
2176 }
2177 
2187  int ret;
2188  if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
2189  return ret;
2190 }
2191 
2202  int ret;
2203  if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
2204  return ret;
2205 }
2206 
2217  int ret;
2218  if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2219  return ret;
2220 }
2221 
2235 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2236  if (!pattern) {
2237  if (regex) {
2238  regfree(regex);
2239  free(regex);
2240  regex = 0;
2241  }
2242  return 1;
2243  }
2244 
2245  if (regex) { regfree(regex); }
2246  else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2247 
2248  invert_regexp = invert;
2249  int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2250  if (0 == ret) return 1;
2251 
2252  regfree(regex);
2253  free(regex);
2254  regex = 0;
2255  error = EINVAL;
2256  return 0;
2257 }
2258 
2270 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2271  return do_ignore_events_by_regex(pattern, flags, 0);
2272 }
2273 
2285 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2286  return do_ignore_events_by_regex(pattern, flags, 1);
2287 }
2288 
2289 int event_compare(const void *p1, const void *p2, const void *config)
2290 {
2291  if (!p1 || !p2) return p1 - p2;
2292  char asc = 1;
2293  long sort_event = (long)config;
2294  if (sort_event == -1) {
2295  sort_event = 0;
2296  asc = 0;
2297  } else if (sort_event < 0) {
2298  sort_event = -sort_event;
2299  asc = 0;
2300  }
2301  unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2302  unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2303  if (0 == *i1 - *i2) {
2304  return ((watch*)p1)->wd - ((watch*)p2)->wd;
2305  }
2306  if (asc)
2307  return *i1 - *i2;
2308  else
2309  return *i2 - *i1;
2310 }
2311 
2312 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2313 {
2314  struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2315  RBLIST *all = rbopenlist(tree_wd);
2316  void const *p = rbreadlist(all);
2317  while (p) {
2318  void const *r = rbsearch(p, ret);
2319  niceassert((int)(r == p), "Couldn't insert watch into new tree");
2320  p = rbreadlist(all);
2321  }
2322  rbcloselist(all);
2323  return ret;
2324 }
inotifytools library public interface.
int inotifytools_init(int fanotify, int watch_filesystem, int verbose)
Definition: inotifytools.c:299
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:893
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:653
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:950
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:625
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:995
int inotifytools_remove_watch_by_wd(int wd)
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:463
void inotifytools_cleanup()
Definition: inotifytools.c:365
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:845
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:538
void inotifytools_replace_filename(char const *oldname, char const *newname)
const char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:874
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:919
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, char *fmt)
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:973
int inotifytools_watch_file(char const *filename, int events)
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