libinotifytools
inotifytools.c
1 // kate: replace-tabs off; space-indent off;
2 
15 #include "../../config.h"
17 #include "inotifytools_p.h"
18 
19 #include <string.h>
20 #include <strings.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <sys/select.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <time.h>
31 #include <regex.h>
32 #include <setjmp.h>
33 
34 #include "inotifytools/inotify.h"
35 
122 #define MAX_EVENTS 4096
123 #define MAX_STRLEN 4096
124 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
125 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
126 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
127 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
128 
129 static int inotify_fd;
130 static unsigned num_access;
131 static unsigned num_modify;
132 static unsigned num_attrib;
133 static unsigned num_close_nowrite;
134 static unsigned num_close_write;
135 static unsigned num_open;
136 static unsigned num_move_self;
137 static unsigned num_moved_to;
138 static unsigned num_moved_from;
139 static unsigned num_create;
140 static unsigned num_delete;
141 static unsigned num_delete_self;
142 static unsigned num_unmount;
143 static unsigned num_total;
144 static int collect_stats = 0;
145 
146 struct rbtree *tree_wd = 0;
147 struct rbtree *tree_filename = 0;
148 static int error = 0;
149 static int init = 0;
150 static char* timefmt = 0;
151 static regex_t* regex = 0;
152 
153 int isdir( char const * path );
154 void record_stats( struct inotify_event const * event );
155 int onestr_to_event(char const * event);
156 
174 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
175  #cond, mesg)
176 
177 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
178 
196 void _niceassert( long cond, int line, char const * file, char const * condstr,
197  char const * mesg ) {
198  if ( cond ) return;
199 
200  if ( mesg ) {
201  fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
202  condstr, mesg );
203  }
204  else {
205  fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
206  }
207 }
208 
218 char * chrtostr(char ch) {
219  static char str[2] = { '\0', '\0' };
220  str[0] = ch;
221  return str;
222 }
223 
227 int read_num_from_file( char * filename, int * num ) {
228  FILE * file = fopen( filename, "r" );
229  if ( !file ) {
230  error = errno;
231  return 0;
232  }
233 
234  if ( EOF == fscanf( file, "%d", num ) ) {
235  error = errno;
236  return 0;
237  }
238 
239  niceassert( 0 == fclose( file ), 0 );
240 
241  return 1;
242 }
243 
244 int wd_compare(const void *d1, const void *d2, const void *config) {
245  if (!d1 || !d2) return d1 - d2;
246  return ((watch*)d1)->wd - ((watch*)d2)->wd;
247 }
248 
249 int filename_compare(const void *d1, const void *d2, const void *config) {
250  if (!d1 || !d2) return d1 - d2;
251  return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
252 }
253 
257 watch *watch_from_wd( int wd ) {
258  watch w;
259  w.wd = wd;
260  return (watch*)rbfind(&w, tree_wd);
261 }
262 
266 watch *watch_from_filename( char const *filename ) {
267  watch w;
268  w.filename = (char*)filename;
269  return (watch*)rbfind(&w, tree_filename);
270 }
271 
282  if (init) return 1;
283 
284  error = 0;
285  // Try to initialise inotify
286  inotify_fd = inotify_init();
287  if (inotify_fd < 0) {
288  error = inotify_fd;
289  return 0;
290  }
291 
292  collect_stats = 0;
293  init = 1;
294  tree_wd = rbinit(wd_compare, 0);
295  tree_filename = rbinit(filename_compare, 0);
296  timefmt = 0;
297 
298  return 1;
299 }
300 
304 void destroy_watch(watch *w) {
305  if (w->filename) free(w->filename);
306  free(w);
307 }
308 
312 void cleanup_tree(const void *nodep,
313  const VISIT which,
314  const int depth, void* arg) {
315  if (which != endorder && which != leaf) return;
316  watch *w = (watch*)nodep;
317  destroy_watch(w);
318 }
319 
327  if (!init) return;
328 
329  init = 0;
330  close(inotify_fd);
331  collect_stats = 0;
332  error = 0;
333  timefmt = 0;
334 
335  if (regex) {
336  regfree(regex);
337  free(regex);
338  regex = 0;
339  }
340 
341  rbwalk(tree_wd, cleanup_tree, 0);
342  rbdestroy(tree_wd); tree_wd = 0;
343  rbdestroy(tree_filename); tree_filename = 0;
344 }
345 
349 void empty_stats(const void *nodep,
350  const VISIT which,
351  const int depth, void *arg) {
352  if (which != endorder && which != leaf) return;
353  watch *w = (watch*)nodep;
354  w->hit_access = 0;
355  w->hit_modify = 0;
356  w->hit_attrib = 0;
357  w->hit_close_nowrite = 0;
358  w->hit_close_write = 0;
359  w->hit_open = 0;
360  w->hit_move_self = 0;
361  w->hit_moved_from = 0;
362  w->hit_moved_to = 0;
363  w->hit_create = 0;
364  w->hit_delete = 0;
365  w->hit_delete_self = 0;
366  w->hit_unmount = 0;
367  w->hit_total = 0;
368 }
369 
373 void replace_filename(const void *nodep,
374  const VISIT which,
375  const int depth, void *arg) {
376  if (which != endorder && which != leaf) return;
377  watch *w = (watch*)nodep;
378  char *old_name = ((char**)arg)[0];
379  char *new_name = ((char**)arg)[1];
380  int old_len = *((int*)&((char**)arg)[2]);
381  char *name;
382  if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
383  nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
384  if (!strcmp( w->filename, new_name )) {
385  free(name);
386  } else {
387  rbdelete(w, tree_filename);
388  free( w->filename );
389  w->filename = name;
390  rbsearch(w, tree_filename);
391  }
392  }
393 }
394 
398 void get_num(const void *nodep,
399  const VISIT which,
400  const int depth, void *arg) {
401  if (which != endorder && which != leaf) return;
402  ++(*((int*)arg));
403 }
404 
405 
419  niceassert( init, "inotifytools_initialize not called yet" );
420 
421  // if already collecting stats, reset stats
422  if (collect_stats) {
423  rbwalk(tree_wd, empty_stats, 0);
424  }
425 
426  num_access = 0;
427  num_modify = 0;
428  num_attrib = 0;
429  num_close_nowrite = 0;
430  num_close_write = 0;
431  num_open = 0;
432  num_move_self = 0;
433  num_moved_from = 0;
434  num_moved_to = 0;
435  num_create = 0;
436  num_delete = 0;
437  num_delete_self = 0;
438  num_unmount = 0;
439  num_total = 0;
440 
441  collect_stats = 1;
442 }
443 
471 int inotifytools_str_to_event_sep(char const * event, char sep) {
472  if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
473  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
474  return -1;
475  }
476 
477  int ret, ret1, len;
478  char * event1, * event2;
479  char eventstr[4096];
480  ret = 0;
481 
482  if ( !event || !event[0] ) return 0;
483 
484  event1 = (char *)event;
485  event2 = strchr( event1, sep );
486  while ( event1 && event1[0] ) {
487  if ( event2 ) {
488  len = event2 - event1;
489  niceassert( len < 4096, "malformed event string (very long)" );
490  }
491  else {
492  len = strlen(event1);
493  }
494  if ( len > 4095 ) len = 4095;
495  strncpy( eventstr, event1, len );
496  eventstr[len] = 0;
497 
498  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  strcat( ret, chrtostr(sep) );
664  strcat( ret, "ACCESS" );
665  }
666  if ( IN_MODIFY & events ) {
667  strcat( ret, chrtostr(sep) );
668  strcat( ret, "MODIFY" );
669  }
670  if ( IN_ATTRIB & events ) {
671  strcat( ret, chrtostr(sep) );
672  strcat( ret, "ATTRIB" );
673  }
674  if ( IN_CLOSE_WRITE & events ) {
675  strcat( ret, chrtostr(sep) );
676  strcat( ret, "CLOSE_WRITE" );
677  }
678  if ( IN_CLOSE_NOWRITE & events ) {
679  strcat( ret, chrtostr(sep) );
680  strcat( ret, "CLOSE_NOWRITE" );
681  }
682  if ( IN_OPEN & events ) {
683  strcat( ret, chrtostr(sep) );
684  strcat( ret, "OPEN" );
685  }
686  if ( IN_MOVED_FROM & events ) {
687  strcat( ret, chrtostr(sep) );
688  strcat( ret, "MOVED_FROM" );
689  }
690  if ( IN_MOVED_TO & events ) {
691  strcat( ret, chrtostr(sep) );
692  strcat( ret, "MOVED_TO" );
693  }
694  if ( IN_CREATE & events ) {
695  strcat( ret, chrtostr(sep) );
696  strcat( ret, "CREATE" );
697  }
698  if ( IN_DELETE & events ) {
699  strcat( ret, chrtostr(sep) );
700  strcat( ret, "DELETE" );
701  }
702  if ( IN_DELETE_SELF & events ) {
703  strcat( ret, chrtostr(sep) );
704  strcat( ret, "DELETE_SELF" );
705  }
706  if ( IN_UNMOUNT & events ) {
707  strcat( ret, chrtostr(sep) );
708  strcat( ret, "UNMOUNT" );
709  }
710  if ( IN_Q_OVERFLOW & events ) {
711  strcat( ret, chrtostr(sep) );
712  strcat( ret, "Q_OVERFLOW" );
713  }
714  if ( IN_IGNORED & events ) {
715  strcat( ret, chrtostr(sep) );
716  strcat( ret, "IGNORED" );
717  }
718  if ( IN_CLOSE & events ) {
719  strcat( ret, chrtostr(sep) );
720  strcat( ret, "CLOSE" );
721  }
722  if ( IN_MOVE_SELF & events ) {
723  strcat( ret, chrtostr(sep) );
724  strcat( ret, "MOVE_SELF" );
725  }
726  if ( IN_ISDIR & events ) {
727  strcat( ret, chrtostr(sep) );
728  strcat( ret, "ISDIR" );
729  }
730  if ( IN_ONESHOT & events ) {
731  strcat( ret, chrtostr(sep) );
732  strcat( ret, "ONESHOT" );
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 
764  niceassert( init, "inotifytools_initialize not called yet" );
765  watch *w = watch_from_wd(wd);
766  if (!w)
767  return NULL;
768 
769  return w->filename;
770 }
771 
786 int inotifytools_wd_from_filename( char const * filename ) {
787  niceassert( init, "inotifytools_initialize not called yet" );
788  watch *w = watch_from_filename(filename);
789  if (!w) return -1;
790  return w->wd;
791 }
792 
807 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
808  niceassert( init, "inotifytools_initialize not called yet" );
809  watch *w = watch_from_wd(wd);
810  if (!w) return;
811  if (w->filename) free(w->filename);
812  w->filename = strdup(filename);
813 }
814 
829 void inotifytools_set_filename_by_filename( char const * oldname,
830  char const * newname ) {
831  watch *w = watch_from_filename(oldname);
832  if (!w) return;
833  if (w->filename) free(w->filename);
834  w->filename = strdup(newname);
835 }
836 
859 void inotifytools_replace_filename( char const * oldname,
860  char const * newname ) {
861  if ( !oldname || !newname ) return;
862  char *names[2+sizeof(int)/sizeof(char*)];
863  names[0] = (char*)oldname;
864  names[1] = (char*)newname;
865  *((int*)&names[2]) = strlen(oldname);
866  rbwalk(tree_filename, replace_filename, (void*)names);
867 }
868 
872 int remove_inotify_watch(watch *w) {
873  error = 0;
874  int status = inotify_rm_watch( inotify_fd, w->wd );
875  if ( status < 0 ) {
876  fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
877  strerror(status) );
878  error = status;
879  return 0;
880  }
881  return 1;
882 }
883 
887 watch *create_watch(int wd, char *filename) {
888  if ( wd <= 0 || !filename) return 0;
889 
890  watch *w = (watch*)calloc(1, sizeof(watch));
891  w->wd = wd;
892  w->filename = strdup(filename);
893  rbsearch(w, tree_wd);
894  rbsearch(w, tree_filename);
895  return w;
896 }
897 
911  niceassert( init, "inotifytools_initialize not called yet" );
912  watch *w = watch_from_wd(wd);
913  if (!w) return 1;
914 
915  if (!remove_inotify_watch(w)) return 0;
916  rbdelete(w, tree_wd);
917  rbdelete(w, tree_filename);
918  destroy_watch(w);
919  return 1;
920 }
921 
933 int inotifytools_remove_watch_by_filename( char const * filename ) {
934  niceassert( init, "inotifytools_initialize not called yet" );
935  watch *w = watch_from_filename(filename);
936  if (!w) return 1;
937 
938  if (!remove_inotify_watch(w)) return 0;
939  rbdelete(w, tree_wd);
940  rbdelete(w, tree_filename);
941  destroy_watch(w);
942  return 1;
943 }
944 
956 int inotifytools_watch_file( char const * filename, int events ) {
957  static char const * filenames[2];
958  filenames[0] = filename;
959  filenames[1] = NULL;
960  return inotifytools_watch_files( filenames, events );
961 }
962 
978 int inotifytools_watch_files( char const * filenames[], int events ) {
979  niceassert( init, "inotifytools_initialize not called yet" );
980  error = 0;
981 
982  static int i;
983  for ( i = 0; filenames[i]; ++i ) {
984  static int wd;
985  wd = inotify_add_watch( inotify_fd, filenames[i], events );
986  if ( wd < 0 ) {
987  if ( wd == -1 ) {
988  error = errno;
989  return 0;
990  } // if ( wd == -1 )
991  else {
992  fprintf( stderr, "Failed to watch %s: returned wd was %d "
993  "(expected -1 or >0 )", filenames[i], wd );
994  // no appropriate value for error
995  return 0;
996  } // else
997  } // if ( wd < 0 )
998 
999  char *filename;
1000  // Always end filename with / if it is a directory
1001  if ( !isdir(filenames[i])
1002  || filenames[i][strlen(filenames[i])-1] == '/') {
1003  filename = strdup(filenames[i]);
1004  }
1005  else {
1006  nasprintf( &filename, "%s/", filenames[i] );
1007  }
1008  create_watch(wd, filename);
1009  free(filename);
1010  } // for
1011 
1012  return 1;
1013 }
1014 
1041 struct inotify_event * inotifytools_next_event( int timeout ) {
1042  return inotifytools_next_events( timeout, 1 );
1043 }
1044 
1045 
1095 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
1096  niceassert( init, "inotifytools_initialize not called yet" );
1097  niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1098 
1099  if ( num_events < 1 ) return NULL;
1100 
1101  static struct inotify_event event[MAX_EVENTS];
1102  static struct inotify_event * ret;
1103  static int first_byte = 0;
1104  static ssize_t bytes;
1105  static jmp_buf jmp;
1106  static char match_name[MAX_STRLEN];
1107 
1108 #define RETURN(A) {\
1109  if (regex) {\
1110  inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1111  if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1112  longjmp(jmp,0);\
1113  }\
1114  }\
1115  if ( collect_stats ) {\
1116  record_stats( A );\
1117  }\
1118  return A;\
1119 }
1120 
1121  setjmp(jmp);
1122 
1123  error = 0;
1124 
1125  // first_byte is index into event buffer
1126  if ( first_byte != 0
1127  && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1128 
1129  ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1130  first_byte += sizeof(struct inotify_event) + ret->len;
1131 
1132  // if the pointer to the next event exactly hits end of bytes read,
1133  // that's good. next time we're called, we'll read.
1134  if ( first_byte == bytes ) {
1135  first_byte = 0;
1136  }
1137  else if ( first_byte > bytes ) {
1138  // oh... no. this can't be happening. An incomplete event.
1139  // Copy what we currently have into first element, call self to
1140  // read remainder.
1141  // oh, and they BETTER NOT overlap.
1142  // Boy I hope this code works.
1143  // But I think this can never happen due to how inotify is written.
1144  niceassert( (long)((char *)&event[0] +
1145  sizeof(struct inotify_event) +
1146  event[0].len) <= (long)ret,
1147  "extremely unlucky user, death imminent" );
1148  // how much of the event do we have?
1149  bytes = (char *)&event[0] + bytes - (char *)ret;
1150  memcpy( &event[0], ret, bytes );
1151  return inotifytools_next_events( timeout, num_events );
1152  }
1153  RETURN(ret);
1154 
1155  }
1156 
1157  else if ( first_byte == 0 ) {
1158  bytes = 0;
1159  }
1160 
1161 
1162  static ssize_t this_bytes;
1163  static unsigned int bytes_to_read;
1164  static int rc;
1165  static fd_set read_fds;
1166 
1167  static struct timeval read_timeout;
1168  read_timeout.tv_sec = timeout;
1169  read_timeout.tv_usec = 0;
1170  static struct timeval * read_timeout_ptr;
1171  read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
1172 
1173  FD_ZERO(&read_fds);
1174  FD_SET(inotify_fd, &read_fds);
1175  rc = select(inotify_fd + 1, &read_fds,
1176  NULL, NULL, read_timeout_ptr);
1177  if ( rc < 0 ) {
1178  // error
1179  error = errno;
1180  return NULL;
1181  }
1182  else if ( rc == 0 ) {
1183  // timeout
1184  return NULL;
1185  }
1186 
1187  // wait until we have enough bytes to read
1188  do {
1189  rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1190  } while ( !rc &&
1191  bytes_to_read < sizeof(struct inotify_event)*num_events );
1192 
1193  if ( rc == -1 ) {
1194  error = errno;
1195  return NULL;
1196  }
1197 
1198  this_bytes = read(inotify_fd, &event[0] + bytes,
1199  sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1200  if ( this_bytes < 0 ) {
1201  error = errno;
1202  return NULL;
1203  }
1204  if ( this_bytes == 0 ) {
1205  fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1206  "events occurred at once.\n");
1207  return NULL;
1208  }
1209  bytes += this_bytes;
1210 
1211  ret = &event[0];
1212  first_byte = sizeof(struct inotify_event) + ret->len;
1213  niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1214  "almost certainly screw up." );
1215  if ( first_byte == bytes ) {
1216  first_byte = 0;
1217  }
1218 
1219  RETURN(ret);
1220 
1221 #undef RETURN
1222 }
1223 
1249 int inotifytools_watch_recursively( char const * path, int events ) {
1250  return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1251 }
1252 
1285 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1286  char const ** exclude_list ) {
1287  niceassert( init, "inotifytools_initialize not called yet" );
1288 
1289  DIR * dir;
1290  char * my_path;
1291  error = 0;
1292  dir = opendir( path );
1293  if ( !dir ) {
1294  // If not a directory, don't need to do anything special
1295  if ( errno == ENOTDIR ) {
1296  return inotifytools_watch_file( path, events );
1297  }
1298  else {
1299  error = errno;
1300  return 0;
1301  }
1302  }
1303 
1304  if ( path[strlen(path)-1] != '/' ) {
1305  nasprintf( &my_path, "%s/", path );
1306  }
1307  else {
1308  my_path = (char *)path;
1309  }
1310 
1311  static struct dirent * ent;
1312  char * next_file;
1313  static struct stat64 my_stat;
1314  ent = readdir( dir );
1315  // Watch each directory within this directory
1316  while ( ent ) {
1317  if ( (0 != strcmp( ent->d_name, "." )) &&
1318  (0 != strcmp( ent->d_name, ".." )) ) {
1319  nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1320  if ( -1 == lstat64( next_file, &my_stat ) ) {
1321  error = errno;
1322  free( next_file );
1323  if ( errno != EACCES ) {
1324  error = errno;
1325  if ( my_path != path ) free( my_path );
1326  closedir( dir );
1327  return 0;
1328  }
1329  }
1330  else if ( S_ISDIR( my_stat.st_mode ) &&
1331  !S_ISLNK( my_stat.st_mode )) {
1332  free( next_file );
1333  nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1334  static unsigned int no_watch;
1335  static char const ** exclude_entry;
1336 
1337  no_watch = 0;
1338  for (exclude_entry = exclude_list;
1339  exclude_entry && *exclude_entry && !no_watch;
1340  ++exclude_entry) {
1341  static int exclude_length;
1342 
1343  exclude_length = strlen(*exclude_entry);
1344  if ((*exclude_entry)[exclude_length-1] == '/') {
1345  --exclude_length;
1346  }
1347  if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1348  !strncmp(*exclude_entry, next_file, exclude_length)) {
1349  // directory found in exclude list
1350  no_watch = 1;
1351  }
1352  }
1353  if (!no_watch) {
1354  static int status;
1356  next_file,
1357  events,
1358  exclude_list );
1359  // For some errors, we will continue.
1360  if ( !status && (EACCES != error) && (ENOENT != error) &&
1361  (ELOOP != error) ) {
1362  free( next_file );
1363  if ( my_path != path ) free( my_path );
1364  closedir( dir );
1365  return 0;
1366  }
1367  } // if !no_watch
1368  free( next_file );
1369  } // if isdir and not islnk
1370  else {
1371  free( next_file );
1372  }
1373  }
1374  ent = readdir( dir );
1375  error = 0;
1376  }
1377 
1378  closedir( dir );
1379 
1380  int ret = inotifytools_watch_file( my_path, events );
1381  if ( my_path != path ) free( my_path );
1382  return ret;
1383 }
1384 
1388 void record_stats( struct inotify_event const * event ) {
1389  if (!event) return;
1390  watch *w = watch_from_wd(event->wd);
1391  if (!w) return;
1392  if ( IN_ACCESS & event->mask ) {
1393  ++w->hit_access;
1394  ++num_access;
1395  }
1396  if ( IN_MODIFY & event->mask ) {
1397  ++w->hit_modify;
1398  ++num_modify;
1399  }
1400  if ( IN_ATTRIB & event->mask ) {
1401  ++w->hit_attrib;
1402  ++num_attrib;
1403  }
1404  if ( IN_CLOSE_WRITE & event->mask ) {
1405  ++w->hit_close_write;
1406  ++num_close_write;
1407  }
1408  if ( IN_CLOSE_NOWRITE & event->mask ) {
1409  ++w->hit_close_nowrite;
1410  ++num_close_nowrite;
1411  }
1412  if ( IN_OPEN & event->mask ) {
1413  ++w->hit_open;
1414  ++num_open;
1415  }
1416  if ( IN_MOVED_FROM & event->mask ) {
1417  ++w->hit_moved_from;
1418  ++num_moved_from;
1419  }
1420  if ( IN_MOVED_TO & event->mask ) {
1421  ++w->hit_moved_to;
1422  ++num_moved_to;
1423  }
1424  if ( IN_CREATE & event->mask ) {
1425  ++w->hit_create;
1426  ++num_create;
1427  }
1428  if ( IN_DELETE & event->mask ) {
1429  ++w->hit_delete;
1430  ++num_delete;
1431  }
1432  if ( IN_DELETE_SELF & event->mask ) {
1433  ++w->hit_delete_self;
1434  ++num_delete_self;
1435  }
1436  if ( IN_UNMOUNT & event->mask ) {
1437  ++w->hit_unmount;
1438  ++num_unmount;
1439  }
1440  if ( IN_MOVE_SELF & event->mask ) {
1441  ++w->hit_move_self;
1442  ++num_move_self;
1443  }
1444 
1445  ++w->hit_total;
1446  ++num_total;
1447 
1448 }
1449 
1450 int *stat_ptr(watch *w, int event)
1451 {
1452  if ( IN_ACCESS == event )
1453  return &w->hit_access;
1454  if ( IN_MODIFY == event )
1455  return &w->hit_modify;
1456  if ( IN_ATTRIB == event )
1457  return &w->hit_attrib;
1458  if ( IN_CLOSE_WRITE == event )
1459  return &w->hit_close_write;
1460  if ( IN_CLOSE_NOWRITE == event )
1461  return &w->hit_close_nowrite;
1462  if ( IN_OPEN == event )
1463  return &w->hit_open;
1464  if ( IN_MOVED_FROM == event )
1465  return &w->hit_moved_from;
1466  if ( IN_MOVED_TO == event )
1467  return &w->hit_moved_to;
1468  if ( IN_CREATE == event )
1469  return &w->hit_create;
1470  if ( IN_DELETE == event )
1471  return &w->hit_delete;
1472  if ( IN_DELETE_SELF == event )
1473  return &w->hit_delete_self;
1474  if ( IN_UNMOUNT == event )
1475  return &w->hit_unmount;
1476  if ( IN_MOVE_SELF == event )
1477  return &w->hit_move_self;
1478  if ( 0 == event )
1479  return &w->hit_total;
1480  return 0;
1481 }
1482 
1498 int inotifytools_get_stat_by_wd( int wd, int event ) {
1499  if (!collect_stats) return -1;
1500 
1501  watch *w = watch_from_wd(wd);
1502  if (!w) return -1;
1503  int *i = stat_ptr(w, event);
1504  if (!i) return -1;
1505  return *i;
1506 }
1507 
1522  if (!collect_stats) return -1;
1523  if ( IN_ACCESS == event )
1524  return num_access;
1525  if ( IN_MODIFY == event )
1526  return num_modify;
1527  if ( IN_ATTRIB == event )
1528  return num_attrib;
1529  if ( IN_CLOSE_WRITE == event )
1530  return num_close_write;
1531  if ( IN_CLOSE_NOWRITE == event )
1532  return num_close_nowrite;
1533  if ( IN_OPEN == event )
1534  return num_open;
1535  if ( IN_MOVED_FROM == event )
1536  return num_moved_from;
1537  if ( IN_MOVED_TO == event )
1538  return num_moved_to;
1539  if ( IN_CREATE == event )
1540  return num_create;
1541  if ( IN_DELETE == event )
1542  return num_delete;
1543  if ( IN_DELETE_SELF == event )
1544  return num_delete_self;
1545  if ( IN_UNMOUNT == event )
1546  return num_unmount;
1547  if ( IN_MOVE_SELF == event )
1548  return num_move_self;
1549 
1550  if ( 0 == event )
1551  return num_total;
1552 
1553  return -1;
1554 }
1555 
1575 int inotifytools_get_stat_by_filename( char const * filename,
1576  int event ) {
1578  filename ), event );
1579 }
1580 
1592  return error;
1593 }
1594 
1598 int isdir( char const * path ) {
1599  static struct stat64 my_stat;
1600 
1601  if ( -1 == lstat64( path, &my_stat ) ) {
1602  if (errno == ENOENT) return 0;
1603  fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1604  return 0;
1605  }
1606 
1607  return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1608 }
1609 
1610 
1618  int ret = 0;
1619  rbwalk(tree_filename, get_num, (void*)&ret);
1620  return ret;
1621 }
1622 
1663 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1664  return inotifytools_fprintf( stdout, event, fmt );
1665 }
1666 
1708 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1709  static char out[MAX_STRLEN+1];
1710  static int ret;
1711  ret = inotifytools_sprintf( out, event, fmt );
1712  if ( -1 != ret ) fprintf( file, "%s", out );
1713  return ret;
1714 }
1715 
1766 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1767  return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1768 }
1769 
1770 
1817 int inotifytools_snprintf( char * out, int size,
1818  struct inotify_event* event, char* fmt ) {
1819  static char * filename, * eventname, * eventstr;
1820  static unsigned int i, ind;
1821  static char ch1;
1822  static char timestr[MAX_STRLEN];
1823  static time_t now;
1824 
1825 
1826  if ( event->len > 0 ) {
1827  eventname = event->name;
1828  }
1829  else {
1830  eventname = NULL;
1831  }
1832 
1833 
1834  filename = inotifytools_filename_from_wd( event->wd );
1835 
1836  if ( !fmt || 0 == strlen(fmt) ) {
1837  error = EINVAL;
1838  return -1;
1839  }
1840  if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1841  error = EMSGSIZE;
1842  return -1;
1843  }
1844 
1845  ind = 0;
1846  for ( i = 0; i < strlen(fmt) &&
1847  (int)ind < size - 1; ++i ) {
1848  if ( fmt[i] != '%' ) {
1849  out[ind++] = fmt[i];
1850  continue;
1851  }
1852 
1853  if ( i == strlen(fmt) - 1 ) {
1854  // last character is %, invalid
1855  error = EINVAL;
1856  return ind;
1857  }
1858 
1859  ch1 = fmt[i+1];
1860 
1861  if ( ch1 == '%' ) {
1862  out[ind++] = '%';
1863  ++i;
1864  continue;
1865  }
1866 
1867  if ( ch1 == 'w' ) {
1868  if ( filename ) {
1869  strncpy( &out[ind], filename, size - ind );
1870  ind += strlen(filename);
1871  }
1872  ++i;
1873  continue;
1874  }
1875 
1876  if ( ch1 == 'f' ) {
1877  if ( eventname ) {
1878  strncpy( &out[ind], eventname, size - ind );
1879  ind += strlen(eventname);
1880  }
1881  ++i;
1882  continue;
1883  }
1884 
1885  if ( ch1 == 'e' ) {
1886  eventstr = inotifytools_event_to_str( event->mask );
1887  strncpy( &out[ind], eventstr, size - ind );
1888  ind += strlen(eventstr);
1889  ++i;
1890  continue;
1891  }
1892 
1893  if ( ch1 == 'T' ) {
1894 
1895  if ( timefmt ) {
1896 
1897  now = time(0);
1898  if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1899  localtime( &now ) ) ) {
1900 
1901  // time format probably invalid
1902  error = EINVAL;
1903  return ind;
1904  }
1905  }
1906  else {
1907  timestr[0] = 0;
1908  }
1909 
1910  strncpy( &out[ind], timestr, size - ind );
1911  ind += strlen(timestr);
1912  ++i;
1913  continue;
1914  }
1915 
1916  // Check if next char in fmt is e
1917  if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1918  eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1919  strncpy( &out[ind], eventstr, size - ind );
1920  ind += strlen(eventstr);
1921  i += 2;
1922  continue;
1923  }
1924 
1925  // OK, this wasn't a special format character, just output it as normal
1926  if ( ind < MAX_STRLEN ) out[ind++] = '%';
1927  if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1928  ++i;
1929  }
1930  out[ind] = 0;
1931 
1932  return ind - 1;
1933 }
1934 
1945  timefmt = fmt;
1946 }
1947 
1957  int ret;
1958  if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1959  return ret;
1960 }
1961 
1972  int ret;
1973  if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1974  return ret;
1975 }
1976 
1987  int ret;
1988  if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
1989  return ret;
1990 }
1991 
2003 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2004  if (!pattern) {
2005  if (regex) {
2006  regfree(regex);
2007  free(regex);
2008  regex = 0;
2009  }
2010  return 1;
2011  }
2012 
2013  if (regex) { regfree(regex); }
2014  else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2015 
2016  int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2017  if (0 == ret) return 1;
2018 
2019  regfree(regex);
2020  free(regex);
2021  regex = 0;
2022  error = EINVAL;
2023  return 0;
2024 }
2025 
2026 int event_compare(const void *p1, const void *p2, const void *config)
2027 {
2028  if (!p1 || !p2) return p1 - p2;
2029  char asc = 1;
2030  int sort_event = (int)config;
2031  if (sort_event == -1) {
2032  sort_event = 0;
2033  asc = 0;
2034  } else if (sort_event < 0) {
2035  sort_event = -sort_event;
2036  asc = 0;
2037  }
2038  int *i1 = stat_ptr((watch*)p1, sort_event);
2039  int *i2 = stat_ptr((watch*)p2, sort_event);
2040  if (0 == *i1 - *i2) {
2041  return ((watch*)p1)->wd - ((watch*)p2)->wd;
2042  }
2043  if (asc)
2044  return *i1 - *i2;
2045  else
2046  return *i2 - *i1;
2047 }
2048 
2049 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2050 {
2051  struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
2052  RBLIST *all = rbopenlist(tree_wd);
2053  void const *p = rbreadlist(all);
2054  while (p) {
2055  void const *r = rbsearch(p, ret);
2056  niceassert((int)(r == p), "Couldn't insert watch into new tree");
2057  p = rbreadlist(all);
2058  }
2059  rbcloselist(all);
2060  return ret;
2061 }
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:763
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
int inotifytools_printf(struct inotify_event *event, char *fmt)
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:910
inotifytools library public interface.
void inotifytools_set_printf_timefmt(char *fmt)
int inotifytools_get_max_user_watches()
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
void inotifytools_initialize_stats()
Definition: inotifytools.c:418
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
struct inotify_event * inotifytools_next_event(int timeout)
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:628
int inotifytools_get_stat_total(int event)
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:829
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:786
struct inotify_event * inotifytools_next_events(int timeout, int num_events)
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:807
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:978
int inotifytools_get_num_watches()
int inotifytools_get_stat_by_wd(int wd, int event)
int inotifytools_error()
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:933
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:956
int inotifytools_get_max_user_instances()
void inotifytools_cleanup()
Definition: inotifytools.c:326
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:859
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:541
int inotifytools_initialize()
Definition: inotifytools.c:281
int inotifytools_get_stat_by_filename(char const *filename, int event)
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:471
int inotifytools_get_max_queued_events()
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:656
int inotifytools_watch_recursively(char const *path, int events)