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 <stdint.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <sys/select.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <time.h>
32 #include <regex.h>
33 #include <setjmp.h>
34 
35 #include "inotifytools/inotify.h"
36 
123 #define MAX_EVENTS 4096
124 #define MAX_STRLEN 4096
125 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
126 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
127 #define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
128 #define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
129 
130 static int inotify_fd;
131 static unsigned num_access;
132 static unsigned num_modify;
133 static unsigned num_attrib;
134 static unsigned num_close_nowrite;
135 static unsigned num_close_write;
136 static unsigned num_open;
137 static unsigned num_move_self;
138 static unsigned num_moved_to;
139 static unsigned num_moved_from;
140 static unsigned num_create;
141 static unsigned num_delete;
142 static unsigned num_delete_self;
143 static unsigned num_unmount;
144 static unsigned num_total;
145 static int collect_stats = 0;
146 
147 struct rbtree *tree_wd = 0;
148 struct rbtree *tree_filename = 0;
149 static int error = 0;
150 static int init = 0;
151 static char* timefmt = 0;
152 static regex_t* regex = 0;
153 /* 0: --exclude[i], 1: --include[i] */
154 static int invert_regexp = 0;
155 
156 static int isdir( char const * path );
157 void record_stats( struct inotify_event const * event );
158 int onestr_to_event(char const * event);
159 
177 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
178  #cond, mesg)
179 
180 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
181 
199 static void _niceassert( long cond, int line, char const * file,
200  char const * condstr, char const * mesg ) {
201  if ( cond ) return;
202 
203  if ( mesg ) {
204  fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
205  condstr, mesg );
206  }
207  else {
208  fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
209  }
210 }
211 
221 char * chrtostr(char ch) {
222  static char str[2] = { '\0', '\0' };
223  str[0] = ch;
224  return str;
225 }
226 
230 int read_num_from_file( char * filename, int * num ) {
231  FILE * file = fopen( filename, "r" );
232  if ( !file ) {
233  error = errno;
234  return 0;
235  }
236 
237  if ( EOF == fscanf( file, "%d", num ) ) {
238  error = errno;
239  return 0;
240  }
241 
242  niceassert( 0 == fclose( file ), 0 );
243 
244  return 1;
245 }
246 
247 int wd_compare(const void *d1, const void *d2, const void *config) {
248  if (!d1 || !d2) return d1 - d2;
249  return ((watch*)d1)->wd - ((watch*)d2)->wd;
250 }
251 
252 int filename_compare(const void *d1, const void *d2, const void *config) {
253  if (!d1 || !d2) return d1 - d2;
254  return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
255 }
256 
260 watch *watch_from_wd( int wd ) {
261  watch w;
262  w.wd = wd;
263  return (watch*)rbfind(&w, tree_wd);
264 }
265 
269 watch *watch_from_filename( char const *filename ) {
270  watch w;
271  w.filename = (char*)filename;
272  return (watch*)rbfind(&w, tree_filename);
273 }
274 
285  if (init) return 1;
286 
287  error = 0;
288  // Try to initialise inotify
289  inotify_fd = inotify_init();
290  if (inotify_fd < 0) {
291  error = errno;
292  return 0;
293  }
294 
295  collect_stats = 0;
296  init = 1;
297  tree_wd = rbinit(wd_compare, 0);
298  tree_filename = rbinit(filename_compare, 0);
299  timefmt = 0;
300 
301  return 1;
302 }
303 
307 void destroy_watch(watch *w) {
308  if (w->filename) free(w->filename);
309  free(w);
310 }
311 
315 void cleanup_tree(const void *nodep,
316  const VISIT which,
317  const int depth, void* arg) {
318  if (which != endorder && which != leaf) return;
319  watch *w = (watch*)nodep;
320  destroy_watch(w);
321 }
322 
330  if (!init) return;
331 
332  init = 0;
333  close(inotify_fd);
334  collect_stats = 0;
335  error = 0;
336  timefmt = 0;
337 
338  if (regex) {
339  regfree(regex);
340  free(regex);
341  regex = 0;
342  }
343 
344  rbwalk(tree_wd, cleanup_tree, 0);
345  rbdestroy(tree_wd); tree_wd = 0;
346  rbdestroy(tree_filename); tree_filename = 0;
347 }
348 
352 void empty_stats(const void *nodep,
353  const VISIT which,
354  const int depth, void *arg) {
355  if (which != endorder && which != leaf) return;
356  watch *w = (watch*)nodep;
357  w->hit_access = 0;
358  w->hit_modify = 0;
359  w->hit_attrib = 0;
360  w->hit_close_nowrite = 0;
361  w->hit_close_write = 0;
362  w->hit_open = 0;
363  w->hit_move_self = 0;
364  w->hit_moved_from = 0;
365  w->hit_moved_to = 0;
366  w->hit_create = 0;
367  w->hit_delete = 0;
368  w->hit_delete_self = 0;
369  w->hit_unmount = 0;
370  w->hit_total = 0;
371 }
372 
376 struct replace_filename_data {
377  char const *old_name;
378  char const *new_name;
379  size_t old_len;
380 };
381 
385 void replace_filename(const void *nodep, const VISIT which, const int depth,
386  const struct replace_filename_data *data) {
387  if (which != endorder && which != leaf) return;
388  watch *w = (watch*)nodep;
389  char *name;
390  if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
391  nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
392  if (!strcmp( w->filename, data->new_name )) {
393  free(name);
394  } else {
395  rbdelete(w, tree_filename);
396  free( w->filename );
397  w->filename = name;
398  rbsearch(w, tree_filename);
399  }
400  }
401 }
402 
406 void get_num(const void *nodep,
407  const VISIT which,
408  const int depth, void *arg) {
409  if (which != endorder && which != leaf) return;
410  ++(*((int*)arg));
411 }
412 
413 
427  niceassert( init, "inotifytools_initialize not called yet" );
428 
429  // if already collecting stats, reset stats
430  if (collect_stats) {
431  rbwalk(tree_wd, empty_stats, 0);
432  }
433 
434  num_access = 0;
435  num_modify = 0;
436  num_attrib = 0;
437  num_close_nowrite = 0;
438  num_close_write = 0;
439  num_open = 0;
440  num_move_self = 0;
441  num_moved_from = 0;
442  num_moved_to = 0;
443  num_create = 0;
444  num_delete = 0;
445  num_delete_self = 0;
446  num_unmount = 0;
447  num_total = 0;
448 
449  collect_stats = 1;
450 }
451 
479 int inotifytools_str_to_event_sep(char const * event, char sep) {
480  if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
481  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
482  return -1;
483  }
484 
485  int ret, ret1, len;
486  char * event1, * event2;
487  static const size_t eventstr_size = 4096;
488  char eventstr[eventstr_size];
489  ret = 0;
490 
491  if ( !event || !event[0] ) return 0;
492 
493  event1 = (char *)event;
494  event2 = strchr( event1, sep );
495  while ( event1 && event1[0] ) {
496  if ( event2 ) {
497  len = event2 - event1;
498  niceassert(len < eventstr_size,
499  "malformed event string (very long)");
500  }
501  else {
502  len = strlen(event1);
503  }
504  if (len > eventstr_size - 1)
505  len = eventstr_size - 1;
506 
507  if (event2 || len == eventstr_size - 1) {
508  strncpy(eventstr, event1, len);
509  } else {
510  strcpy(eventstr, event1);
511  }
512 
513  eventstr[len] = 0;
514 
515  ret1 = onestr_to_event( eventstr );
516  if ( 0 == ret1 || -1 == ret1 ) {
517  ret = ret1;
518  break;
519  }
520  ret |= ret1;
521 
522  event1 = event2;
523  if ( event1 && event1[0] ) {
524  // jump over 'sep' character
525  ++event1;
526  // if last character was 'sep'...
527  if ( !event1[0] ) return 0;
528  event2 = strchr( event1, sep );
529  }
530  }
531 
532  return ret;
533 }
534 
558 int inotifytools_str_to_event(char const * event) {
559  return inotifytools_str_to_event_sep( event, ',' );
560 }
561 
573 int onestr_to_event(char const * event)
574 {
575  static int ret;
576  ret = -1;
577 
578  if ( !event || !event[0] )
579  ret = 0;
580  else if ( 0 == strcasecmp(event, "ACCESS") )
581  ret = IN_ACCESS;
582  else if ( 0 == strcasecmp(event, "MODIFY") )
583  ret = IN_MODIFY;
584  else if ( 0 == strcasecmp(event, "ATTRIB") )
585  ret = IN_ATTRIB;
586  else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
587  ret = IN_CLOSE_WRITE;
588  else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
589  ret = IN_CLOSE_NOWRITE;
590  else if ( 0 == strcasecmp(event, "OPEN") )
591  ret = IN_OPEN;
592  else if ( 0 == strcasecmp(event, "MOVED_FROM") )
593  ret = IN_MOVED_FROM;
594  else if ( 0 == strcasecmp(event, "MOVED_TO") )
595  ret = IN_MOVED_TO;
596  else if ( 0 == strcasecmp(event, "CREATE") )
597  ret = IN_CREATE;
598  else if ( 0 == strcasecmp(event, "DELETE") )
599  ret = IN_DELETE;
600  else if ( 0 == strcasecmp(event, "DELETE_SELF") )
601  ret = IN_DELETE_SELF;
602  else if ( 0 == strcasecmp(event, "UNMOUNT") )
603  ret = IN_UNMOUNT;
604  else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
605  ret = IN_Q_OVERFLOW;
606  else if ( 0 == strcasecmp(event, "IGNORED") )
607  ret = IN_IGNORED;
608  else if ( 0 == strcasecmp(event, "CLOSE") )
609  ret = IN_CLOSE;
610  else if ( 0 == strcasecmp(event, "MOVE_SELF") )
611  ret = IN_MOVE_SELF;
612  else if ( 0 == strcasecmp(event, "MOVE") )
613  ret = IN_MOVE;
614  else if ( 0 == strcasecmp(event, "ISDIR") )
615  ret = IN_ISDIR;
616  else if ( 0 == strcasecmp(event, "ONESHOT") )
617  ret = IN_ONESHOT;
618  else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
619  ret = IN_ALL_EVENTS;
620 
621  return ret;
622 }
623 
645 char * inotifytools_event_to_str(int events) {
646  return inotifytools_event_to_str_sep(events, ',');
647 }
648 
673 char * inotifytools_event_to_str_sep(int events, char sep)
674 {
675  static char ret[1024];
676  ret[0] = '\0';
677  ret[1] = '\0';
678 
679  if ( IN_ACCESS & events ) {
680  strcat( ret, chrtostr(sep) );
681  strcat( ret, "ACCESS" );
682  }
683  if ( IN_MODIFY & events ) {
684  strcat( ret, chrtostr(sep) );
685  strcat( ret, "MODIFY" );
686  }
687  if ( IN_ATTRIB & events ) {
688  strcat( ret, chrtostr(sep) );
689  strcat( ret, "ATTRIB" );
690  }
691  if ( IN_CLOSE_WRITE & events ) {
692  strcat( ret, chrtostr(sep) );
693  strcat( ret, "CLOSE_WRITE" );
694  }
695  if ( IN_CLOSE_NOWRITE & events ) {
696  strcat( ret, chrtostr(sep) );
697  strcat( ret, "CLOSE_NOWRITE" );
698  }
699  if ( IN_OPEN & events ) {
700  strcat( ret, chrtostr(sep) );
701  strcat( ret, "OPEN" );
702  }
703  if ( IN_MOVED_FROM & events ) {
704  strcat( ret, chrtostr(sep) );
705  strcat( ret, "MOVED_FROM" );
706  }
707  if ( IN_MOVED_TO & events ) {
708  strcat( ret, chrtostr(sep) );
709  strcat( ret, "MOVED_TO" );
710  }
711  if ( IN_CREATE & events ) {
712  strcat( ret, chrtostr(sep) );
713  strcat( ret, "CREATE" );
714  }
715  if ( IN_DELETE & events ) {
716  strcat( ret, chrtostr(sep) );
717  strcat( ret, "DELETE" );
718  }
719  if ( IN_DELETE_SELF & events ) {
720  strcat( ret, chrtostr(sep) );
721  strcat( ret, "DELETE_SELF" );
722  }
723  if ( IN_UNMOUNT & events ) {
724  strcat( ret, chrtostr(sep) );
725  strcat( ret, "UNMOUNT" );
726  }
727  if ( IN_Q_OVERFLOW & events ) {
728  strcat( ret, chrtostr(sep) );
729  strcat( ret, "Q_OVERFLOW" );
730  }
731  if ( IN_IGNORED & events ) {
732  strcat( ret, chrtostr(sep) );
733  strcat( ret, "IGNORED" );
734  }
735  if ( IN_CLOSE & events ) {
736  strcat( ret, chrtostr(sep) );
737  strcat( ret, "CLOSE" );
738  }
739  if ( IN_MOVE_SELF & events ) {
740  strcat( ret, chrtostr(sep) );
741  strcat( ret, "MOVE_SELF" );
742  }
743  if ( IN_ISDIR & events ) {
744  strcat( ret, chrtostr(sep) );
745  strcat( ret, "ISDIR" );
746  }
747  if ( IN_ONESHOT & events ) {
748  strcat( ret, chrtostr(sep) );
749  strcat( ret, "ONESHOT" );
750  }
751 
752  // Maybe we didn't match any... ?
753  if (ret[0] == '\0') {
754  niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
755  }
756 
757  return &ret[1];
758 }
759 
781  niceassert( init, "inotifytools_initialize not called yet" );
782  watch *w = watch_from_wd(wd);
783  if (!w)
784  return NULL;
785 
786  return w->filename;
787 }
788 
803 int inotifytools_wd_from_filename( char const * filename ) {
804  niceassert( init, "inotifytools_initialize not called yet" );
805  watch *w = watch_from_filename(filename);
806  if (!w) return -1;
807  return w->wd;
808 }
809 
824 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
825  niceassert( init, "inotifytools_initialize not called yet" );
826  watch *w = watch_from_wd(wd);
827  if (!w) return;
828  if (w->filename) free(w->filename);
829  w->filename = strdup(filename);
830 }
831 
846 void inotifytools_set_filename_by_filename( char const * oldname,
847  char const * newname ) {
848  watch *w = watch_from_filename(oldname);
849  if (!w) return;
850  if (w->filename) free(w->filename);
851  w->filename = strdup(newname);
852 }
853 
876 void inotifytools_replace_filename( char const * oldname,
877  char const * newname ) {
878  if ( !oldname || !newname ) return;
879  struct replace_filename_data data;
880  data.old_name = oldname;
881  data.new_name = newname;
882  data.old_len = strlen(oldname);
883  rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
884 }
885 
889 int remove_inotify_watch(watch *w) {
890  error = 0;
891  int status = inotify_rm_watch( inotify_fd, w->wd );
892  if ( status < 0 ) {
893  fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
894  strerror(status) );
895  error = status;
896  return 0;
897  }
898  return 1;
899 }
900 
904 watch *create_watch(int wd, char *filename) {
905  if ( wd <= 0 || !filename) return 0;
906 
907  watch *w = (watch*)calloc(1, sizeof(watch));
908  w->wd = wd;
909  w->filename = strdup(filename);
910  rbsearch(w, tree_wd);
911  rbsearch(w, tree_filename);
912  return NULL;
913 }
914 
928  niceassert( init, "inotifytools_initialize not called yet" );
929  watch *w = watch_from_wd(wd);
930  if (!w) return 1;
931 
932  if (!remove_inotify_watch(w)) return 0;
933  rbdelete(w, tree_wd);
934  rbdelete(w, tree_filename);
935  destroy_watch(w);
936  return 1;
937 }
938 
950 int inotifytools_remove_watch_by_filename( char const * filename ) {
951  niceassert( init, "inotifytools_initialize not called yet" );
952  watch *w = watch_from_filename(filename);
953  if (!w) return 1;
954 
955  if (!remove_inotify_watch(w)) return 0;
956  rbdelete(w, tree_wd);
957  rbdelete(w, tree_filename);
958  destroy_watch(w);
959  return 1;
960 }
961 
973 int inotifytools_watch_file( char const * filename, int events ) {
974  static char const * filenames[2];
975  filenames[0] = filename;
976  filenames[1] = NULL;
977  return inotifytools_watch_files( filenames, events );
978 }
979 
995 int inotifytools_watch_files( char const * filenames[], int events ) {
996  niceassert( init, "inotifytools_initialize not called yet" );
997  error = 0;
998 
999  static int i;
1000  for ( i = 0; filenames[i]; ++i ) {
1001  static int wd;
1002  wd = inotify_add_watch( inotify_fd, filenames[i], events );
1003  if ( wd < 0 ) {
1004  if ( wd == -1 ) {
1005  error = errno;
1006  return 0;
1007  } // if ( wd == -1 )
1008  else {
1009  fprintf( stderr, "Failed to watch %s: returned wd was %d "
1010  "(expected -1 or >0 )", filenames[i], wd );
1011  // no appropriate value for error
1012  return 0;
1013  } // else
1014  } // if ( wd < 0 )
1015 
1016  char *filename;
1017  // Always end filename with / if it is a directory
1018  if ( !isdir(filenames[i])
1019  || filenames[i][strlen(filenames[i])-1] == '/') {
1020  filename = strdup(filenames[i]);
1021  }
1022  else {
1023  nasprintf( &filename, "%s/", filenames[i] );
1024  }
1025  create_watch(wd, filename);
1026  free(filename);
1027  } // for
1028 
1029  return 1;
1030 }
1031 
1058 struct inotify_event * inotifytools_next_event( long int timeout ) {
1059  return inotifytools_next_events( timeout, 1 );
1060 }
1061 
1062 
1111 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1112  niceassert( init, "inotifytools_initialize not called yet" );
1113  niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1114 
1115  if ( num_events < 1 ) return NULL;
1116 
1117  static struct inotify_event event[MAX_EVENTS];
1118  static struct inotify_event * ret;
1119  static int first_byte = 0;
1120  static ssize_t bytes;
1121  static jmp_buf jmp;
1122  static char match_name[MAX_STRLEN];
1123 
1124 #define RETURN(A) {\
1125  if (regex) {\
1126  inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
1127  if (0 == regexec(regex, match_name, 0, 0, 0)) {\
1128  if (!invert_regexp)\
1129  longjmp(jmp,0);\
1130  } else {\
1131  if (invert_regexp)\
1132  longjmp(jmp,0);\
1133  }\
1134  }\
1135  if ( collect_stats ) {\
1136  record_stats( A );\
1137  }\
1138  return A;\
1139 }
1140 
1141  setjmp(jmp);
1142 
1143  error = 0;
1144 
1145  // first_byte is index into event buffer
1146  if ( first_byte != 0
1147  && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1148 
1149  ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1150  first_byte += sizeof(struct inotify_event) + ret->len;
1151 
1152  // if the pointer to the next event exactly hits end of bytes read,
1153  // that's good. next time we're called, we'll read.
1154  if ( first_byte == bytes ) {
1155  first_byte = 0;
1156  }
1157  else if ( first_byte > bytes ) {
1158  // oh... no. this can't be happening. An incomplete event.
1159  // Copy what we currently have into first element, call self to
1160  // read remainder.
1161  // oh, and they BETTER NOT overlap.
1162  // Boy I hope this code works.
1163  // But I think this can never happen due to how inotify is written.
1164  niceassert( (long)((char *)&event[0] +
1165  sizeof(struct inotify_event) +
1166  event[0].len) <= (long)ret,
1167  "extremely unlucky user, death imminent" );
1168  // how much of the event do we have?
1169  bytes = (char *)&event[0] + bytes - (char *)ret;
1170  memcpy( &event[0], ret, bytes );
1171  return inotifytools_next_events( timeout, num_events );
1172  }
1173  RETURN(ret);
1174 
1175  }
1176 
1177  else if ( first_byte == 0 ) {
1178  bytes = 0;
1179  }
1180 
1181 
1182  static ssize_t this_bytes;
1183  static unsigned int bytes_to_read;
1184  static int rc;
1185  static fd_set read_fds;
1186 
1187  static struct timeval read_timeout;
1188  read_timeout.tv_sec = timeout;
1189  read_timeout.tv_usec = 0;
1190  static struct timeval * read_timeout_ptr;
1191  read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1192 
1193  FD_ZERO(&read_fds);
1194  FD_SET(inotify_fd, &read_fds);
1195  rc = select(inotify_fd + 1, &read_fds,
1196  NULL, NULL, read_timeout_ptr);
1197  if ( rc < 0 ) {
1198  // error
1199  error = errno;
1200  return NULL;
1201  }
1202  else if ( rc == 0 ) {
1203  // timeout
1204  return NULL;
1205  }
1206 
1207  // wait until we have enough bytes to read
1208  do {
1209  rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1210  } while ( !rc &&
1211  bytes_to_read < sizeof(struct inotify_event)*num_events );
1212 
1213  if ( rc == -1 ) {
1214  error = errno;
1215  return NULL;
1216  }
1217 
1218  this_bytes = read(inotify_fd, &event[0] + bytes,
1219  sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1220  if ( this_bytes < 0 ) {
1221  error = errno;
1222  return NULL;
1223  }
1224  if ( this_bytes == 0 ) {
1225  fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1226  "events occurred at once.\n");
1227  return NULL;
1228  }
1229  bytes += this_bytes;
1230 
1231  ret = &event[0];
1232  first_byte = sizeof(struct inotify_event) + ret->len;
1233  niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1234  "almost certainly screw up." );
1235  if ( first_byte == bytes ) {
1236  first_byte = 0;
1237  }
1238 
1239  RETURN(ret);
1240 
1241 #undef RETURN
1242 }
1243 
1269 int inotifytools_watch_recursively( char const * path, int events ) {
1270  return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1271 }
1272 
1305 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1306  char const ** exclude_list ) {
1307  niceassert( init, "inotifytools_initialize not called yet" );
1308 
1309  DIR * dir;
1310  char * my_path;
1311  error = 0;
1312  dir = opendir( path );
1313  if ( !dir ) {
1314  // If not a directory, don't need to do anything special
1315  if ( errno == ENOTDIR ) {
1316  return inotifytools_watch_file( path, events );
1317  }
1318  else {
1319  error = errno;
1320  return 0;
1321  }
1322  }
1323 
1324  if ( path[strlen(path)-1] != '/' ) {
1325  nasprintf( &my_path, "%s/", path );
1326  }
1327  else {
1328  my_path = (char *)path;
1329  }
1330 
1331  static struct dirent * ent;
1332  char * next_file;
1333  static struct stat64 my_stat;
1334  ent = readdir( dir );
1335  // Watch each directory within this directory
1336  while ( ent ) {
1337  if ( (0 != strcmp( ent->d_name, "." )) &&
1338  (0 != strcmp( ent->d_name, ".." )) ) {
1339  nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1340  if ( -1 == lstat64( next_file, &my_stat ) ) {
1341  error = errno;
1342  free( next_file );
1343  if ( errno != EACCES ) {
1344  error = errno;
1345  if ( my_path != path ) free( my_path );
1346  closedir( dir );
1347  return 0;
1348  }
1349  }
1350  else if ( S_ISDIR( my_stat.st_mode ) &&
1351  !S_ISLNK( my_stat.st_mode )) {
1352  free( next_file );
1353  nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1354  static unsigned int no_watch;
1355  static char const ** exclude_entry;
1356 
1357  no_watch = 0;
1358  for (exclude_entry = exclude_list;
1359  exclude_entry && *exclude_entry && !no_watch;
1360  ++exclude_entry) {
1361  static int exclude_length;
1362 
1363  exclude_length = strlen(*exclude_entry);
1364  if ((*exclude_entry)[exclude_length-1] == '/') {
1365  --exclude_length;
1366  }
1367  if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1368  !strncmp(*exclude_entry, next_file, exclude_length)) {
1369  // directory found in exclude list
1370  no_watch = 1;
1371  }
1372  }
1373  if (!no_watch) {
1374  static int status;
1376  next_file,
1377  events,
1378  exclude_list );
1379  // For some errors, we will continue.
1380  if ( !status && (EACCES != error) && (ENOENT != error) &&
1381  (ELOOP != error) ) {
1382  free( next_file );
1383  if ( my_path != path ) free( my_path );
1384  closedir( dir );
1385  return 0;
1386  }
1387  } // if !no_watch
1388  free( next_file );
1389  } // if isdir and not islnk
1390  else {
1391  free( next_file );
1392  }
1393  }
1394  ent = readdir( dir );
1395  error = 0;
1396  }
1397 
1398  closedir( dir );
1399 
1400  int ret = inotifytools_watch_file( my_path, events );
1401  if ( my_path != path ) free( my_path );
1402  return ret;
1403 }
1404 
1408 void record_stats( struct inotify_event const * event ) {
1409  if (!event) return;
1410  watch *w = watch_from_wd(event->wd);
1411  if (!w) return;
1412  if ( IN_ACCESS & event->mask ) {
1413  ++w->hit_access;
1414  ++num_access;
1415  }
1416  if ( IN_MODIFY & event->mask ) {
1417  ++w->hit_modify;
1418  ++num_modify;
1419  }
1420  if ( IN_ATTRIB & event->mask ) {
1421  ++w->hit_attrib;
1422  ++num_attrib;
1423  }
1424  if ( IN_CLOSE_WRITE & event->mask ) {
1425  ++w->hit_close_write;
1426  ++num_close_write;
1427  }
1428  if ( IN_CLOSE_NOWRITE & event->mask ) {
1429  ++w->hit_close_nowrite;
1430  ++num_close_nowrite;
1431  }
1432  if ( IN_OPEN & event->mask ) {
1433  ++w->hit_open;
1434  ++num_open;
1435  }
1436  if ( IN_MOVED_FROM & event->mask ) {
1437  ++w->hit_moved_from;
1438  ++num_moved_from;
1439  }
1440  if ( IN_MOVED_TO & event->mask ) {
1441  ++w->hit_moved_to;
1442  ++num_moved_to;
1443  }
1444  if ( IN_CREATE & event->mask ) {
1445  ++w->hit_create;
1446  ++num_create;
1447  }
1448  if ( IN_DELETE & event->mask ) {
1449  ++w->hit_delete;
1450  ++num_delete;
1451  }
1452  if ( IN_DELETE_SELF & event->mask ) {
1453  ++w->hit_delete_self;
1454  ++num_delete_self;
1455  }
1456  if ( IN_UNMOUNT & event->mask ) {
1457  ++w->hit_unmount;
1458  ++num_unmount;
1459  }
1460  if ( IN_MOVE_SELF & event->mask ) {
1461  ++w->hit_move_self;
1462  ++num_move_self;
1463  }
1464 
1465  ++w->hit_total;
1466  ++num_total;
1467 
1468 }
1469 
1470 unsigned int *stat_ptr(watch *w, int event)
1471 {
1472  if ( IN_ACCESS == event )
1473  return &w->hit_access;
1474  if ( IN_MODIFY == event )
1475  return &w->hit_modify;
1476  if ( IN_ATTRIB == event )
1477  return &w->hit_attrib;
1478  if ( IN_CLOSE_WRITE == event )
1479  return &w->hit_close_write;
1480  if ( IN_CLOSE_NOWRITE == event )
1481  return &w->hit_close_nowrite;
1482  if ( IN_OPEN == event )
1483  return &w->hit_open;
1484  if ( IN_MOVED_FROM == event )
1485  return &w->hit_moved_from;
1486  if ( IN_MOVED_TO == event )
1487  return &w->hit_moved_to;
1488  if ( IN_CREATE == event )
1489  return &w->hit_create;
1490  if ( IN_DELETE == event )
1491  return &w->hit_delete;
1492  if ( IN_DELETE_SELF == event )
1493  return &w->hit_delete_self;
1494  if ( IN_UNMOUNT == event )
1495  return &w->hit_unmount;
1496  if ( IN_MOVE_SELF == event )
1497  return &w->hit_move_self;
1498  if ( 0 == event )
1499  return &w->hit_total;
1500  return 0;
1501 }
1502 
1518 int inotifytools_get_stat_by_wd( int wd, int event ) {
1519  if (!collect_stats) return -1;
1520 
1521  watch *w = watch_from_wd(wd);
1522  if (!w) return -1;
1523  unsigned int *i = stat_ptr(w, event);
1524  if (!i) return -1;
1525  return *i;
1526 }
1527 
1542  if (!collect_stats) return -1;
1543  if ( IN_ACCESS == event )
1544  return num_access;
1545  if ( IN_MODIFY == event )
1546  return num_modify;
1547  if ( IN_ATTRIB == event )
1548  return num_attrib;
1549  if ( IN_CLOSE_WRITE == event )
1550  return num_close_write;
1551  if ( IN_CLOSE_NOWRITE == event )
1552  return num_close_nowrite;
1553  if ( IN_OPEN == event )
1554  return num_open;
1555  if ( IN_MOVED_FROM == event )
1556  return num_moved_from;
1557  if ( IN_MOVED_TO == event )
1558  return num_moved_to;
1559  if ( IN_CREATE == event )
1560  return num_create;
1561  if ( IN_DELETE == event )
1562  return num_delete;
1563  if ( IN_DELETE_SELF == event )
1564  return num_delete_self;
1565  if ( IN_UNMOUNT == event )
1566  return num_unmount;
1567  if ( IN_MOVE_SELF == event )
1568  return num_move_self;
1569 
1570  if ( 0 == event )
1571  return num_total;
1572 
1573  return -1;
1574 }
1575 
1595 int inotifytools_get_stat_by_filename( char const * filename,
1596  int event ) {
1598  filename ), event );
1599 }
1600 
1612  return error;
1613 }
1614 
1618 static int isdir( char const * path ) {
1619  static struct stat64 my_stat;
1620 
1621  if ( -1 == lstat64( path, &my_stat ) ) {
1622  if (errno == ENOENT) return 0;
1623  fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1624  return 0;
1625  }
1626 
1627  return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1628 }
1629 
1630 
1638  int ret = 0;
1639  rbwalk(tree_filename, get_num, (void*)&ret);
1640  return ret;
1641 }
1642 
1683 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1684  return inotifytools_fprintf( stdout, event, fmt );
1685 }
1686 
1728 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1729  static char out[MAX_STRLEN+1];
1730  static int ret;
1731  ret = inotifytools_sprintf( out, event, fmt );
1732  if ( -1 != ret ) fprintf( file, "%s", out );
1733  return ret;
1734 }
1735 
1786 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
1787  return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1788 }
1789 
1790 
1837 int inotifytools_snprintf( char * out, int size,
1838  struct inotify_event* event, char* fmt ) {
1839  static char * filename, * eventname, * eventstr;
1840  static unsigned int i, ind;
1841  static char ch1;
1842  static char timestr[MAX_STRLEN];
1843  static time_t now;
1844 
1845 
1846  if ( event->len > 0 ) {
1847  eventname = event->name;
1848  }
1849  else {
1850  eventname = NULL;
1851  }
1852 
1853 
1854  filename = inotifytools_filename_from_wd( event->wd );
1855 
1856  if ( !fmt || 0 == strlen(fmt) ) {
1857  error = EINVAL;
1858  return -1;
1859  }
1860  if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1861  error = EMSGSIZE;
1862  return -1;
1863  }
1864 
1865  ind = 0;
1866  for ( i = 0; i < strlen(fmt) &&
1867  (int)ind < size - 1; ++i ) {
1868  if ( fmt[i] != '%' ) {
1869  out[ind++] = fmt[i];
1870  continue;
1871  }
1872 
1873  if ( i == strlen(fmt) - 1 ) {
1874  // last character is %, invalid
1875  error = EINVAL;
1876  return ind;
1877  }
1878 
1879  ch1 = fmt[i+1];
1880 
1881  if ( ch1 == '%' ) {
1882  out[ind++] = '%';
1883  ++i;
1884  continue;
1885  }
1886 
1887  if ( ch1 == 'w' ) {
1888  if ( filename ) {
1889  strncpy( &out[ind], filename, size - ind );
1890  ind += strlen(filename);
1891  }
1892  ++i;
1893  continue;
1894  }
1895 
1896  if ( ch1 == 'f' ) {
1897  if ( eventname ) {
1898  strncpy( &out[ind], eventname, size - ind );
1899  ind += strlen(eventname);
1900  }
1901  ++i;
1902  continue;
1903  }
1904 
1905  if ( ch1 == 'e' ) {
1906  eventstr = inotifytools_event_to_str( event->mask );
1907  strncpy( &out[ind], eventstr, size - ind );
1908  ind += strlen(eventstr);
1909  ++i;
1910  continue;
1911  }
1912 
1913  if ( ch1 == 'T' ) {
1914 
1915  if ( timefmt ) {
1916 
1917  now = time(0);
1918  if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
1919  localtime( &now ) ) ) {
1920 
1921  // time format probably invalid
1922  error = EINVAL;
1923  return ind;
1924  }
1925  }
1926  else {
1927  timestr[0] = 0;
1928  }
1929 
1930  strncpy( &out[ind], timestr, size - ind );
1931  ind += strlen(timestr);
1932  ++i;
1933  continue;
1934  }
1935 
1936  // Check if next char in fmt is e
1937  if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1938  eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1939  strncpy( &out[ind], eventstr, size - ind );
1940  ind += strlen(eventstr);
1941  i += 2;
1942  continue;
1943  }
1944 
1945  // OK, this wasn't a special format character, just output it as normal
1946  if ( ind < MAX_STRLEN ) out[ind++] = '%';
1947  if ( ind < MAX_STRLEN ) out[ind++] = ch1;
1948  ++i;
1949  }
1950  out[ind] = 0;
1951 
1952  return ind - 1;
1953 }
1954 
1965  timefmt = fmt;
1966 }
1967 
1977  int ret;
1978  if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
1979  return ret;
1980 }
1981 
1992  int ret;
1993  if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
1994  return ret;
1995 }
1996 
2007  int ret;
2008  if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2009  return ret;
2010 }
2011 
2025 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2026  if (!pattern) {
2027  if (regex) {
2028  regfree(regex);
2029  free(regex);
2030  regex = 0;
2031  }
2032  return 1;
2033  }
2034 
2035  if (regex) { regfree(regex); }
2036  else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2037 
2038  invert_regexp = invert;
2039  int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2040  if (0 == ret) return 1;
2041 
2042  regfree(regex);
2043  free(regex);
2044  regex = 0;
2045  error = EINVAL;
2046  return 0;
2047 }
2048 
2060 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2061  return do_ignore_events_by_regex(pattern, flags, 0);
2062 }
2063 
2075 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2076  return do_ignore_events_by_regex(pattern, flags, 1);
2077 }
2078 
2079 int event_compare(const void *p1, const void *p2, const void *config)
2080 {
2081  if (!p1 || !p2) return p1 - p2;
2082  char asc = 1;
2083  int sort_event = *(int*)config;
2084  if (sort_event == -1) {
2085  sort_event = 0;
2086  asc = 0;
2087  } else if (sort_event < 0) {
2088  sort_event = -sort_event;
2089  asc = 0;
2090  }
2091  unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2092  unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2093  if (0 == *i1 - *i2) {
2094  return ((watch*)p1)->wd - ((watch*)p2)->wd;
2095  }
2096  if (asc)
2097  return *i1 - *i2;
2098  else
2099  return *i2 - *i1;
2100 }
2101 
2102 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2103 {
2104  struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2105  RBLIST *all = rbopenlist(tree_wd);
2106  void const *p = rbreadlist(all);
2107  while (p) {
2108  void const *r = rbsearch(p, ret);
2109  niceassert((int)(r == p), "Couldn't insert watch into new tree");
2110  p = rbreadlist(all);
2111  }
2112  rbcloselist(all);
2113  return ret;
2114 }
inotifytools_initialize
int inotifytools_initialize()
Definition: inotifytools.c:284
inotifytools_remove_watch_by_wd
int inotifytools_remove_watch_by_wd(int wd)
Definition: inotifytools.c:927
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1111
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1595
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1964
inotifytools_sprintf
int inotifytools_sprintf(char *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1786
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:673
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:426
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1305
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1683
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:2006
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:846
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1728
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1541
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:824
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1637
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1518
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:645
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:973
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1611
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:803
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:558
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:995
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2060
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:950
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:876
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1058
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:479
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:780
inotifytools_snprintf
int inotifytools_snprintf(char *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1837
inotifytools_cleanup
void inotifytools_cleanup()
Definition: inotifytools.c:329
inotifytools_ignore_events_by_inverted_regex
int inotifytools_ignore_events_by_inverted_regex(char const *pattern, int flags)
Definition: inotifytools.c:2075
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1269
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:1976
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:1991