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 /* 0: --exclude[i], 1: --include[i] */
153 static int invert_regexp = 0;
154 
155 static int isdir( char const * path );
156 void record_stats( struct inotify_event const * event );
157 int onestr_to_event(char const * event);
158 
176 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
177  #cond, mesg)
178 
179 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
180 
198 static void _niceassert( long cond, int line, char const * file,
199  char const * condstr, char const * mesg ) {
200  if ( cond ) return;
201 
202  if ( mesg ) {
203  fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
204  condstr, mesg );
205  }
206  else {
207  fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
208  }
209 }
210 
220 char * chrtostr(char ch) {
221  static char str[2] = { '\0', '\0' };
222  str[0] = ch;
223  return str;
224 }
225 
229 int read_num_from_file( char * filename, int * num ) {
230  FILE * file = fopen( filename, "r" );
231  if ( !file ) {
232  error = errno;
233  return 0;
234  }
235 
236  if ( EOF == fscanf( file, "%d", num ) ) {
237  error = errno;
238  return 0;
239  }
240 
241  niceassert( 0 == fclose( file ), 0 );
242 
243  return 1;
244 }
245 
246 int wd_compare(const void *d1, const void *d2, const void *config) {
247  if (!d1 || !d2) return d1 - d2;
248  return ((watch*)d1)->wd - ((watch*)d2)->wd;
249 }
250 
251 int filename_compare(const void *d1, const void *d2, const void *config) {
252  if (!d1 || !d2) return d1 - d2;
253  return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
254 }
255 
259 watch *watch_from_wd( int wd ) {
260  watch w;
261  w.wd = wd;
262  return (watch*)rbfind(&w, tree_wd);
263 }
264 
268 watch *watch_from_filename( char const *filename ) {
269  watch w;
270  w.filename = (char*)filename;
271  return (watch*)rbfind(&w, tree_filename);
272 }
273 
284  if (init) return 1;
285 
286  error = 0;
287  // Try to initialise inotify
288  inotify_fd = inotify_init();
289  if (inotify_fd < 0) {
290  error = errno;
291  return 0;
292  }
293 
294  collect_stats = 0;
295  init = 1;
296  tree_wd = rbinit(wd_compare, 0);
297  tree_filename = rbinit(filename_compare, 0);
298  timefmt = 0;
299 
300  return 1;
301 }
302 
306 void destroy_watch(watch *w) {
307  if (w->filename) free(w->filename);
308  free(w);
309 }
310 
314 void cleanup_tree(const void *nodep,
315  const VISIT which,
316  const int depth, void* arg) {
317  if (which != endorder && which != leaf) return;
318  watch *w = (watch*)nodep;
319  destroy_watch(w);
320 }
321 
329  if (!init) return;
330 
331  init = 0;
332  close(inotify_fd);
333  collect_stats = 0;
334  error = 0;
335  timefmt = 0;
336 
337  if (regex) {
338  regfree(regex);
339  free(regex);
340  regex = 0;
341  }
342 
343  rbwalk(tree_wd, cleanup_tree, 0);
344  rbdestroy(tree_wd); tree_wd = 0;
345  rbdestroy(tree_filename); tree_filename = 0;
346 }
347 
351 void empty_stats(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  w->hit_access = 0;
357  w->hit_modify = 0;
358  w->hit_attrib = 0;
359  w->hit_close_nowrite = 0;
360  w->hit_close_write = 0;
361  w->hit_open = 0;
362  w->hit_move_self = 0;
363  w->hit_moved_from = 0;
364  w->hit_moved_to = 0;
365  w->hit_create = 0;
366  w->hit_delete = 0;
367  w->hit_delete_self = 0;
368  w->hit_unmount = 0;
369  w->hit_total = 0;
370 }
371 
375 void replace_filename(const void *nodep,
376  const VISIT which,
377  const int depth, void *arg) {
378  if (which != endorder && which != leaf) return;
379  watch *w = (watch*)nodep;
380  char *old_name = ((char**)arg)[0];
381  char *new_name = ((char**)arg)[1];
382  int old_len = *((int*)&((char**)arg)[2]);
383  char *name;
384  if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
385  nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
386  if (!strcmp( w->filename, new_name )) {
387  free(name);
388  } else {
389  rbdelete(w, tree_filename);
390  free( w->filename );
391  w->filename = name;
392  rbsearch(w, tree_filename);
393  }
394  }
395 }
396 
400 void get_num(const void *nodep,
401  const VISIT which,
402  const int depth, void *arg) {
403  if (which != endorder && which != leaf) return;
404  ++(*((int*)arg));
405 }
406 
407 
421  niceassert( init, "inotifytools_initialize not called yet" );
422 
423  // if already collecting stats, reset stats
424  if (collect_stats) {
425  rbwalk(tree_wd, empty_stats, 0);
426  }
427 
428  num_access = 0;
429  num_modify = 0;
430  num_attrib = 0;
431  num_close_nowrite = 0;
432  num_close_write = 0;
433  num_open = 0;
434  num_move_self = 0;
435  num_moved_from = 0;
436  num_moved_to = 0;
437  num_create = 0;
438  num_delete = 0;
439  num_delete_self = 0;
440  num_unmount = 0;
441  num_total = 0;
442 
443  collect_stats = 1;
444 }
445 
473 int inotifytools_str_to_event_sep(char const * event, char sep) {
474  if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
475  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
476  return -1;
477  }
478 
479  int ret, ret1, len;
480  char * event1, * event2;
481  char eventstr[4096];
482  ret = 0;
483 
484  if ( !event || !event[0] ) return 0;
485 
486  event1 = (char *)event;
487  event2 = strchr( event1, sep );
488  while ( event1 && event1[0] ) {
489  if ( event2 ) {
490  len = event2 - event1;
491  niceassert( len < 4096, "malformed event string (very long)" );
492  }
493  else {
494  len = strlen(event1);
495  }
496  if ( len > 4095 ) len = 4095;
497  strncpy( eventstr, event1, len );
498  eventstr[len] = 0;
499 
500  ret1 = onestr_to_event( eventstr );
501  if ( 0 == ret1 || -1 == ret1 ) {
502  ret = ret1;
503  break;
504  }
505  ret |= ret1;
506 
507  event1 = event2;
508  if ( event1 && event1[0] ) {
509  // jump over 'sep' character
510  ++event1;
511  // if last character was 'sep'...
512  if ( !event1[0] ) return 0;
513  event2 = strchr( event1, sep );
514  }
515  }
516 
517  return ret;
518 }
519 
543 int inotifytools_str_to_event(char const * event) {
544  return inotifytools_str_to_event_sep( event, ',' );
545 }
546 
558 int onestr_to_event(char const * event)
559 {
560  static int ret;
561  ret = -1;
562 
563  if ( !event || !event[0] )
564  ret = 0;
565  else if ( 0 == strcasecmp(event, "ACCESS") )
566  ret = IN_ACCESS;
567  else if ( 0 == strcasecmp(event, "MODIFY") )
568  ret = IN_MODIFY;
569  else if ( 0 == strcasecmp(event, "ATTRIB") )
570  ret = IN_ATTRIB;
571  else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
572  ret = IN_CLOSE_WRITE;
573  else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
574  ret = IN_CLOSE_NOWRITE;
575  else if ( 0 == strcasecmp(event, "OPEN") )
576  ret = IN_OPEN;
577  else if ( 0 == strcasecmp(event, "MOVED_FROM") )
578  ret = IN_MOVED_FROM;
579  else if ( 0 == strcasecmp(event, "MOVED_TO") )
580  ret = IN_MOVED_TO;
581  else if ( 0 == strcasecmp(event, "CREATE") )
582  ret = IN_CREATE;
583  else if ( 0 == strcasecmp(event, "DELETE") )
584  ret = IN_DELETE;
585  else if ( 0 == strcasecmp(event, "DELETE_SELF") )
586  ret = IN_DELETE_SELF;
587  else if ( 0 == strcasecmp(event, "UNMOUNT") )
588  ret = IN_UNMOUNT;
589  else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
590  ret = IN_Q_OVERFLOW;
591  else if ( 0 == strcasecmp(event, "IGNORED") )
592  ret = IN_IGNORED;
593  else if ( 0 == strcasecmp(event, "CLOSE") )
594  ret = IN_CLOSE;
595  else if ( 0 == strcasecmp(event, "MOVE_SELF") )
596  ret = IN_MOVE_SELF;
597  else if ( 0 == strcasecmp(event, "MOVE") )
598  ret = IN_MOVE;
599  else if ( 0 == strcasecmp(event, "ISDIR") )
600  ret = IN_ISDIR;
601  else if ( 0 == strcasecmp(event, "ONESHOT") )
602  ret = IN_ONESHOT;
603  else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
604  ret = IN_ALL_EVENTS;
605 
606  return ret;
607 }
608 
630 char * inotifytools_event_to_str(int events) {
631  return inotifytools_event_to_str_sep(events, ',');
632 }
633 
658 char * inotifytools_event_to_str_sep(int events, char sep)
659 {
660  static char ret[1024];
661  ret[0] = '\0';
662  ret[1] = '\0';
663 
664  if ( IN_ACCESS & events ) {
665  strcat( ret, chrtostr(sep) );
666  strcat( ret, "ACCESS" );
667  }
668  if ( IN_MODIFY & events ) {
669  strcat( ret, chrtostr(sep) );
670  strcat( ret, "MODIFY" );
671  }
672  if ( IN_ATTRIB & events ) {
673  strcat( ret, chrtostr(sep) );
674  strcat( ret, "ATTRIB" );
675  }
676  if ( IN_CLOSE_WRITE & events ) {
677  strcat( ret, chrtostr(sep) );
678  strcat( ret, "CLOSE_WRITE" );
679  }
680  if ( IN_CLOSE_NOWRITE & events ) {
681  strcat( ret, chrtostr(sep) );
682  strcat( ret, "CLOSE_NOWRITE" );
683  }
684  if ( IN_OPEN & events ) {
685  strcat( ret, chrtostr(sep) );
686  strcat( ret, "OPEN" );
687  }
688  if ( IN_MOVED_FROM & events ) {
689  strcat( ret, chrtostr(sep) );
690  strcat( ret, "MOVED_FROM" );
691  }
692  if ( IN_MOVED_TO & events ) {
693  strcat( ret, chrtostr(sep) );
694  strcat( ret, "MOVED_TO" );
695  }
696  if ( IN_CREATE & events ) {
697  strcat( ret, chrtostr(sep) );
698  strcat( ret, "CREATE" );
699  }
700  if ( IN_DELETE & events ) {
701  strcat( ret, chrtostr(sep) );
702  strcat( ret, "DELETE" );
703  }
704  if ( IN_DELETE_SELF & events ) {
705  strcat( ret, chrtostr(sep) );
706  strcat( ret, "DELETE_SELF" );
707  }
708  if ( IN_UNMOUNT & events ) {
709  strcat( ret, chrtostr(sep) );
710  strcat( ret, "UNMOUNT" );
711  }
712  if ( IN_Q_OVERFLOW & events ) {
713  strcat( ret, chrtostr(sep) );
714  strcat( ret, "Q_OVERFLOW" );
715  }
716  if ( IN_IGNORED & events ) {
717  strcat( ret, chrtostr(sep) );
718  strcat( ret, "IGNORED" );
719  }
720  if ( IN_CLOSE & events ) {
721  strcat( ret, chrtostr(sep) );
722  strcat( ret, "CLOSE" );
723  }
724  if ( IN_MOVE_SELF & events ) {
725  strcat( ret, chrtostr(sep) );
726  strcat( ret, "MOVE_SELF" );
727  }
728  if ( IN_ISDIR & events ) {
729  strcat( ret, chrtostr(sep) );
730  strcat( ret, "ISDIR" );
731  }
732  if ( IN_ONESHOT & events ) {
733  strcat( ret, chrtostr(sep) );
734  strcat( ret, "ONESHOT" );
735  }
736 
737  // Maybe we didn't match any... ?
738  if (ret[0] == '\0') {
739  niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
740  }
741 
742  return &ret[1];
743 }
744 
766  niceassert( init, "inotifytools_initialize not called yet" );
767  watch *w = watch_from_wd(wd);
768  if (!w)
769  return NULL;
770 
771  return w->filename;
772 }
773 
788 int inotifytools_wd_from_filename( char const * filename ) {
789  niceassert( init, "inotifytools_initialize not called yet" );
790  watch *w = watch_from_filename(filename);
791  if (!w) return -1;
792  return w->wd;
793 }
794 
809 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
810  niceassert( init, "inotifytools_initialize not called yet" );
811  watch *w = watch_from_wd(wd);
812  if (!w) return;
813  if (w->filename) free(w->filename);
814  w->filename = strdup(filename);
815 }
816 
831 void inotifytools_set_filename_by_filename( char const * oldname,
832  char const * newname ) {
833  watch *w = watch_from_filename(oldname);
834  if (!w) return;
835  if (w->filename) free(w->filename);
836  w->filename = strdup(newname);
837 }
838 
861 void inotifytools_replace_filename( char const * oldname,
862  char const * newname ) {
863  if ( !oldname || !newname ) return;
864  char *names[2+sizeof(int)/sizeof(char*)];
865  names[0] = (char*)oldname;
866  names[1] = (char*)newname;
867  *((int*)&names[2]) = strlen(oldname);
868  rbwalk(tree_filename, replace_filename, (void*)names);
869 }
870 
874 int remove_inotify_watch(watch *w) {
875  error = 0;
876  int status = inotify_rm_watch( inotify_fd, w->wd );
877  if ( status < 0 ) {
878  fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
879  strerror(status) );
880  error = status;
881  return 0;
882  }
883  return 1;
884 }
885 
889 watch *create_watch(int wd, char *filename) {
890  if ( wd <= 0 || !filename) return 0;
891 
892  watch *w = (watch*)calloc(1, sizeof(watch));
893  w->wd = wd;
894  w->filename = strdup(filename);
895  rbsearch(w, tree_wd);
896  rbsearch(w, tree_filename);
897 }
898 
912  niceassert( init, "inotifytools_initialize not called yet" );
913  watch *w = watch_from_wd(wd);
914  if (!w) return 1;
915 
916  if (!remove_inotify_watch(w)) return 0;
917  rbdelete(w, tree_wd);
918  rbdelete(w, tree_filename);
919  destroy_watch(w);
920  return 1;
921 }
922 
934 int inotifytools_remove_watch_by_filename( char const * filename ) {
935  niceassert( init, "inotifytools_initialize not called yet" );
936  watch *w = watch_from_filename(filename);
937  if (!w) return 1;
938 
939  if (!remove_inotify_watch(w)) return 0;
940  rbdelete(w, tree_wd);
941  rbdelete(w, tree_filename);
942  destroy_watch(w);
943  return 1;
944 }
945 
957 int inotifytools_watch_file( char const * filename, int events ) {
958  static char const * filenames[2];
959  filenames[0] = filename;
960  filenames[1] = NULL;
961  return inotifytools_watch_files( filenames, events );
962 }
963 
979 int inotifytools_watch_files( char const * filenames[], int events ) {
980  niceassert( init, "inotifytools_initialize not called yet" );
981  error = 0;
982 
983  static int i;
984  for ( i = 0; filenames[i]; ++i ) {
985  static int wd;
986  wd = inotify_add_watch( inotify_fd, filenames[i], events );
987  if ( wd < 0 ) {
988  if ( wd == -1 ) {
989  error = errno;
990  return 0;
991  } // if ( wd == -1 )
992  else {
993  fprintf( stderr, "Failed to watch %s: returned wd was %d "
994  "(expected -1 or >0 )", filenames[i], wd );
995  // no appropriate value for error
996  return 0;
997  } // else
998  } // if ( wd < 0 )
999 
1000  char *filename;
1001  // Always end filename with / if it is a directory
1002  if ( !isdir(filenames[i])
1003  || filenames[i][strlen(filenames[i])-1] == '/') {
1004  filename = strdup(filenames[i]);
1005  }
1006  else {
1007  nasprintf( &filename, "%s/", filenames[i] );
1008  }
1009  create_watch(wd, filename);
1010  free(filename);
1011  } // for
1012 
1013  return 1;
1014 }
1015 
1042 struct inotify_event * inotifytools_next_event( int timeout ) {
1043  return inotifytools_next_events( timeout, 1 );
1044 }
1045 
1046 
1096 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
1097  niceassert( init, "inotifytools_initialize not called yet" );
1098  niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1099 
1100  if ( num_events < 1 ) return NULL;
1101 
1102  static struct inotify_event event[MAX_EVENTS];
1103  static struct inotify_event * ret;
1104  static int first_byte = 0;
1105  static ssize_t bytes;
1106  static jmp_buf jmp;
1107  static char match_name[MAX_STRLEN];
1108 
1109 #define RETURN(A) {\
1110  if (regex) {\
1111  inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1112  if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1113  if (!invert_regexp)\
1114  longjmp(jmp,0);\
1115  } else {\
1116  if (invert_regexp)\
1117  longjmp(jmp,0);\
1118  }\
1119  }\
1120  if ( collect_stats ) {\
1121  record_stats( A );\
1122  }\
1123  return A;\
1124 }
1125 
1126  setjmp(jmp);
1127 
1128  error = 0;
1129 
1130  // first_byte is index into event buffer
1131  if ( first_byte != 0
1132  && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1133 
1134  ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1135  first_byte += sizeof(struct inotify_event) + ret->len;
1136 
1137  // if the pointer to the next event exactly hits end of bytes read,
1138  // that's good. next time we're called, we'll read.
1139  if ( first_byte == bytes ) {
1140  first_byte = 0;
1141  }
1142  else if ( first_byte > bytes ) {
1143  // oh... no. this can't be happening. An incomplete event.
1144  // Copy what we currently have into first element, call self to
1145  // read remainder.
1146  // oh, and they BETTER NOT overlap.
1147  // Boy I hope this code works.
1148  // But I think this can never happen due to how inotify is written.
1149  niceassert( (long)((char *)&event[0] +
1150  sizeof(struct inotify_event) +
1151  event[0].len) <= (long)ret,
1152  "extremely unlucky user, death imminent" );
1153  // how much of the event do we have?
1154  bytes = (char *)&event[0] + bytes - (char *)ret;
1155  memcpy( &event[0], ret, bytes );
1156  return inotifytools_next_events( timeout, num_events );
1157  }
1158  RETURN(ret);
1159 
1160  }
1161 
1162  else if ( first_byte == 0 ) {
1163  bytes = 0;
1164  }
1165 
1166 
1167  static ssize_t this_bytes;
1168  static unsigned int bytes_to_read;
1169  static int rc;
1170  static fd_set read_fds;
1171 
1172  static struct timeval read_timeout;
1173  read_timeout.tv_sec = timeout;
1174  read_timeout.tv_usec = 0;
1175  static struct timeval * read_timeout_ptr;
1176  read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
1177 
1178  FD_ZERO(&read_fds);
1179  FD_SET(inotify_fd, &read_fds);
1180  rc = select(inotify_fd + 1, &read_fds,
1181  NULL, NULL, read_timeout_ptr);
1182  if ( rc < 0 ) {
1183  // error
1184  error = errno;
1185  return NULL;
1186  }
1187  else if ( rc == 0 ) {
1188  // timeout
1189  return NULL;
1190  }
1191 
1192  // wait until we have enough bytes to read
1193  do {
1194  rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1195  } while ( !rc &&
1196  bytes_to_read < sizeof(struct inotify_event)*num_events );
1197 
1198  if ( rc == -1 ) {
1199  error = errno;
1200  return NULL;
1201  }
1202 
1203  this_bytes = read(inotify_fd, &event[0] + bytes,
1204  sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1205  if ( this_bytes < 0 ) {
1206  error = errno;
1207  return NULL;
1208  }
1209  if ( this_bytes == 0 ) {
1210  fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1211  "events occurred at once.\n");
1212  return NULL;
1213  }
1214  bytes += this_bytes;
1215 
1216  ret = &event[0];
1217  first_byte = sizeof(struct inotify_event) + ret->len;
1218  niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1219  "almost certainly screw up." );
1220  if ( first_byte == bytes ) {
1221  first_byte = 0;
1222  }
1223 
1224  RETURN(ret);
1225 
1226 #undef RETURN
1227 }
1228 
1254 int inotifytools_watch_recursively( char const * path, int events ) {
1255  return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1256 }
1257 
1290 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1291  char const ** exclude_list ) {
1292  niceassert( init, "inotifytools_initialize not called yet" );
1293 
1294  DIR * dir;
1295  char * my_path;
1296  error = 0;
1297  dir = opendir( path );
1298  if ( !dir ) {
1299  // If not a directory, don't need to do anything special
1300  if ( errno == ENOTDIR ) {
1301  return inotifytools_watch_file( path, events );
1302  }
1303  else {
1304  error = errno;
1305  return 0;
1306  }
1307  }
1308 
1309  if ( path[strlen(path)-1] != '/' ) {
1310  nasprintf( &my_path, "%s/", path );
1311  }
1312  else {
1313  my_path = (char *)path;
1314  }
1315 
1316  static struct dirent * ent;
1317  char * next_file;
1318  static struct stat64 my_stat;
1319  ent = readdir( dir );
1320  // Watch each directory within this directory
1321  while ( ent ) {
1322  if ( (0 != strcmp( ent->d_name, "." )) &&
1323  (0 != strcmp( ent->d_name, ".." )) ) {
1324  nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1325  if ( -1 == lstat64( next_file, &my_stat ) ) {
1326  error = errno;
1327  free( next_file );
1328  if ( errno != EACCES ) {
1329  error = errno;
1330  if ( my_path != path ) free( my_path );
1331  closedir( dir );
1332  return 0;
1333  }
1334  }
1335  else if ( S_ISDIR( my_stat.st_mode ) &&
1336  !S_ISLNK( my_stat.st_mode )) {
1337  free( next_file );
1338  nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1339  static unsigned int no_watch;
1340  static char const ** exclude_entry;
1341 
1342  no_watch = 0;
1343  for (exclude_entry = exclude_list;
1344  exclude_entry && *exclude_entry && !no_watch;
1345  ++exclude_entry) {
1346  static int exclude_length;
1347 
1348  exclude_length = strlen(*exclude_entry);
1349  if ((*exclude_entry)[exclude_length-1] == '/') {
1350  --exclude_length;
1351  }
1352  if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1353  !strncmp(*exclude_entry, next_file, exclude_length)) {
1354  // directory found in exclude list
1355  no_watch = 1;
1356  }
1357  }
1358  if (!no_watch) {
1359  static int status;
1361  next_file,
1362  events,
1363  exclude_list );
1364  // For some errors, we will continue.
1365  if ( !status && (EACCES != error) && (ENOENT != error) &&
1366  (ELOOP != error) ) {
1367  free( next_file );
1368  if ( my_path != path ) free( my_path );
1369  closedir( dir );
1370  return 0;
1371  }
1372  } // if !no_watch
1373  free( next_file );
1374  } // if isdir and not islnk
1375  else {
1376  free( next_file );
1377  }
1378  }
1379  ent = readdir( dir );
1380  error = 0;
1381  }
1382 
1383  closedir( dir );
1384 
1385  int ret = inotifytools_watch_file( my_path, events );
1386  if ( my_path != path ) free( my_path );
1387  return ret;
1388 }
1389 
1393 void record_stats( struct inotify_event const * event ) {
1394  if (!event) return;
1395  watch *w = watch_from_wd(event->wd);
1396  if (!w) return;
1397  if ( IN_ACCESS & event->mask ) {
1398  ++w->hit_access;
1399  ++num_access;
1400  }
1401  if ( IN_MODIFY & event->mask ) {
1402  ++w->hit_modify;
1403  ++num_modify;
1404  }
1405  if ( IN_ATTRIB & event->mask ) {
1406  ++w->hit_attrib;
1407  ++num_attrib;
1408  }
1409  if ( IN_CLOSE_WRITE & event->mask ) {
1410  ++w->hit_close_write;
1411  ++num_close_write;
1412  }
1413  if ( IN_CLOSE_NOWRITE & event->mask ) {
1414  ++w->hit_close_nowrite;
1415  ++num_close_nowrite;
1416  }
1417  if ( IN_OPEN & event->mask ) {
1418  ++w->hit_open;
1419  ++num_open;
1420  }
1421  if ( IN_MOVED_FROM & event->mask ) {
1422  ++w->hit_moved_from;
1423  ++num_moved_from;
1424  }
1425  if ( IN_MOVED_TO & event->mask ) {
1426  ++w->hit_moved_to;
1427  ++num_moved_to;
1428  }
1429  if ( IN_CREATE & event->mask ) {
1430  ++w->hit_create;
1431  ++num_create;
1432  }
1433  if ( IN_DELETE & event->mask ) {
1434  ++w->hit_delete;
1435  ++num_delete;
1436  }
1437  if ( IN_DELETE_SELF & event->mask ) {
1438  ++w->hit_delete_self;
1439  ++num_delete_self;
1440  }
1441  if ( IN_UNMOUNT & event->mask ) {
1442  ++w->hit_unmount;
1443  ++num_unmount;
1444  }
1445  if ( IN_MOVE_SELF & event->mask ) {
1446  ++w->hit_move_self;
1447  ++num_move_self;
1448  }
1449 
1450  ++w->hit_total;
1451  ++num_total;
1452 
1453 }
1454 
1455 int *stat_ptr(watch *w, int event)
1456 {
1457  if ( IN_ACCESS == event )
1458  return &w->hit_access;
1459  if ( IN_MODIFY == event )
1460  return &w->hit_modify;
1461  if ( IN_ATTRIB == event )
1462  return &w->hit_attrib;
1463  if ( IN_CLOSE_WRITE == event )
1464  return &w->hit_close_write;
1465  if ( IN_CLOSE_NOWRITE == event )
1466  return &w->hit_close_nowrite;
1467  if ( IN_OPEN == event )
1468  return &w->hit_open;
1469  if ( IN_MOVED_FROM == event )
1470  return &w->hit_moved_from;
1471  if ( IN_MOVED_TO == event )
1472  return &w->hit_moved_to;
1473  if ( IN_CREATE == event )
1474  return &w->hit_create;
1475  if ( IN_DELETE == event )
1476  return &w->hit_delete;
1477  if ( IN_DELETE_SELF == event )
1478  return &w->hit_delete_self;
1479  if ( IN_UNMOUNT == event )
1480  return &w->hit_unmount;
1481  if ( IN_MOVE_SELF == event )
1482  return &w->hit_move_self;
1483  if ( 0 == event )
1484  return &w->hit_total;
1485  return 0;
1486 }
1487 
1503 int inotifytools_get_stat_by_wd( int wd, int event ) {
1504  if (!collect_stats) return -1;
1505 
1506  watch *w = watch_from_wd(wd);
1507  if (!w) return -1;
1508  int *i = stat_ptr(w, event);
1509  if (!i) return -1;
1510  return *i;
1511 }
1512 
1527  if (!collect_stats) return -1;
1528  if ( IN_ACCESS == event )
1529  return num_access;
1530  if ( IN_MODIFY == event )
1531  return num_modify;
1532  if ( IN_ATTRIB == event )
1533  return num_attrib;
1534  if ( IN_CLOSE_WRITE == event )
1535  return num_close_write;
1536  if ( IN_CLOSE_NOWRITE == event )
1537  return num_close_nowrite;
1538  if ( IN_OPEN == event )
1539  return num_open;
1540  if ( IN_MOVED_FROM == event )
1541  return num_moved_from;
1542  if ( IN_MOVED_TO == event )
1543  return num_moved_to;
1544  if ( IN_CREATE == event )
1545  return num_create;
1546  if ( IN_DELETE == event )
1547  return num_delete;
1548  if ( IN_DELETE_SELF == event )
1549  return num_delete_self;
1550  if ( IN_UNMOUNT == event )
1551  return num_unmount;
1552  if ( IN_MOVE_SELF == event )
1553  return num_move_self;
1554 
1555  if ( 0 == event )
1556  return num_total;
1557 
1558  return -1;
1559 }
1560 
1580 int inotifytools_get_stat_by_filename( char const * filename,
1581  int event ) {
1583  filename ), event );
1584 }
1585 
1597  return error;
1598 }
1599 
1603 static int isdir( char const * path ) {
1604  static struct stat64 my_stat;
1605 
1606  if ( -1 == lstat64( path, &my_stat ) ) {
1607  if (errno == ENOENT) return 0;
1608  fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1609  return 0;
1610  }
1611 
1612  return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1613 }
1614 
1615 
1623  int ret = 0;
1624  rbwalk(tree_filename, get_num, (void*)&ret);
1625  return ret;
1626 }
1627 
1668 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1669  return inotifytools_fprintf( stdout, event, fmt );
1670 }
1671 
1713 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1714  static char out[MAX_STRLEN+1];
1715  static int ret;
1716  ret = inotifytools_sprintf( out, event, fmt );
1717  if ( -1 != ret ) fprintf( file, "%s", out );
1718  return ret;
1719 }
1720 
1771 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1772  return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1773 }
1774 
1775 
1822 int inotifytools_snprintf( char * out, int size,
1823  struct inotify_event* event, char* fmt ) {
1824  static char * filename, * eventname, * eventstr;
1825  static unsigned int i, ind;
1826  static char ch1;
1827  static char timestr[MAX_STRLEN];
1828  static time_t now;
1829 
1830 
1831  if ( event->len > 0 ) {
1832  eventname = event->name;
1833  }
1834  else {
1835  eventname = NULL;
1836  }
1837 
1838 
1839  filename = inotifytools_filename_from_wd( event->wd );
1840 
1841  if ( !fmt || 0 == strlen(fmt) ) {
1842  error = EINVAL;
1843  return -1;
1844  }
1845  if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1846  error = EMSGSIZE;
1847  return -1;
1848  }
1849 
1850  ind = 0;
1851  for ( i = 0; i < strlen(fmt) &&
1852  (int)ind < size - 1; ++i ) {
1853  if ( fmt[i] != '%' ) {
1854  out[ind++] = fmt[i];
1855  continue;
1856  }
1857 
1858  if ( i == strlen(fmt) - 1 ) {
1859  // last character is %, invalid
1860  error = EINVAL;
1861  return ind;
1862  }
1863 
1864  ch1 = fmt[i+1];
1865 
1866  if ( ch1 == '%' ) {
1867  out[ind++] = '%';
1868  ++i;
1869  continue;
1870  }
1871 
1872  if ( ch1 == 'w' ) {
1873  if ( filename ) {
1874  strncpy( &out[ind], filename, size - ind );
1875  ind += strlen(filename);
1876  }
1877  ++i;
1878  continue;
1879  }
1880 
1881  if ( ch1 == 'f' ) {
1882  if ( eventname ) {
1883  strncpy( &out[ind], eventname, size - ind );
1884  ind += strlen(eventname);
1885  }
1886  ++i;
1887  continue;
1888  }
1889 
1890  if ( ch1 == 'e' ) {
1891  eventstr = inotifytools_event_to_str( event->mask );
1892  strncpy( &out[ind], eventstr, size - ind );
1893  ind += strlen(eventstr);
1894  ++i;
1895  continue;
1896  }
1897 
1898  if ( ch1 == 'T' ) {
1899 
1900  if ( timefmt ) {
1901 
1902  now = time(0);
1903  if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1904  localtime( &now ) ) ) {
1905 
1906  // time format probably invalid
1907  error = EINVAL;
1908  return ind;
1909  }
1910  }
1911  else {
1912  timestr[0] = 0;
1913  }
1914 
1915  strncpy( &out[ind], timestr, size - ind );
1916  ind += strlen(timestr);
1917  ++i;
1918  continue;
1919  }
1920 
1921  // Check if next char in fmt is e
1922  if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1923  eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1924  strncpy( &out[ind], eventstr, size - ind );
1925  ind += strlen(eventstr);
1926  i += 2;
1927  continue;
1928  }
1929 
1930  // OK, this wasn't a special format character, just output it as normal
1931  if ( ind < MAX_STRLEN ) out[ind++] = '%';
1932  if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1933  ++i;
1934  }
1935  out[ind] = 0;
1936 
1937  return ind - 1;
1938 }
1939 
1950  timefmt = fmt;
1951 }
1952 
1962  int ret;
1963  if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1964  return ret;
1965 }
1966 
1977  int ret;
1978  if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1979  return ret;
1980 }
1981 
1992  int ret;
1993  if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
1994  return ret;
1995 }
1996 
2010 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2011  if (!pattern) {
2012  if (regex) {
2013  regfree(regex);
2014  free(regex);
2015  regex = 0;
2016  }
2017  return 1;
2018  }
2019 
2020  if (regex) { regfree(regex); }
2021  else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2022 
2023  invert_regexp = invert;
2024  int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2025  if (0 == ret) return 1;
2026 
2027  regfree(regex);
2028  free(regex);
2029  regex = 0;
2030  error = EINVAL;
2031  return 0;
2032 }
2033 
2045 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2046  return do_ignore_events_by_regex(pattern, flags, 0);
2047 }
2048 
2060 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2061  return do_ignore_events_by_regex(pattern, flags, 1);
2062 }
2063 
2064 int event_compare(const void *p1, const void *p2, const void *config)
2065 {
2066  if (!p1 || !p2) return p1 - p2;
2067  char asc = 1;
2068  int sort_event = (int)config;
2069  if (sort_event == -1) {
2070  sort_event = 0;
2071  asc = 0;
2072  } else if (sort_event < 0) {
2073  sort_event = -sort_event;
2074  asc = 0;
2075  }
2076  int *i1 = stat_ptr((watch*)p1, sort_event);
2077  int *i2 = stat_ptr((watch*)p2, sort_event);
2078  if (0 == *i1 - *i2) {
2079  return ((watch*)p1)->wd - ((watch*)p2)->wd;
2080  }
2081  if (asc)
2082  return *i1 - *i2;
2083  else
2084  return *i2 - *i1;
2085 }
2086 
2087 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2088 {
2089  struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
2090  RBLIST *all = rbopenlist(tree_wd);
2091  void const *p = rbreadlist(all);
2092  while (p) {
2093  void const *r = rbsearch(p, ret);
2094  niceassert((int)(r == p), "Couldn't insert watch into new tree");
2095  p = rbreadlist(all);
2096  }
2097  rbcloselist(all);
2098  return ret;
2099 }
inotifytools_initialize
int inotifytools_initialize()
Definition: inotifytools.c:283
inotifytools_remove_watch_by_wd
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:911
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1580
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1949
inotifytools_sprintf
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1771
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:658
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:420
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1290
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1668
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:1991
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:831
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1713
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1526
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:809
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1622
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1503
inotifytools_next_event
struct inotify_event * inotifytools_next_event(int timeout)
Definition: inotifytools.c:1042
inotifytools_next_events
struct inotify_event * inotifytools_next_events(int timeout, int num_events)
Definition: inotifytools.c:1096
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:630
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:957
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1596
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:788
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:543
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:979
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2045
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:934
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:861
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:473
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:765
inotifytools_snprintf
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1822
inotifytools_cleanup
void inotifytools_cleanup()
Definition: inotifytools.c:328
inotifytools_ignore_events_by_inverted_regex
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
Definition: inotifytools.c:2060
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1254
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:1961
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:1976