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 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 struct replace_filename_data {
376  char const *old_name;
377  char const *new_name;
378  size_t old_len;
379 };
380 
384 void replace_filename(const void *nodep, const VISIT which, const int depth,
385  const struct replace_filename_data *data) {
386  if (which != endorder && which != leaf) return;
387  watch *w = (watch*)nodep;
388  char *name;
389  if ( 0 == strncmp( data->old_name, w->filename, data->old_len ) ) {
390  nasprintf( &name, "%s%s", data->new_name, &(w->filename[data->old_len]) );
391  if (!strcmp( w->filename, data->new_name )) {
392  free(name);
393  } else {
394  rbdelete(w, tree_filename);
395  free( w->filename );
396  w->filename = name;
397  rbsearch(w, tree_filename);
398  }
399  }
400 }
401 
405 void get_num(const void *nodep,
406  const VISIT which,
407  const int depth, void *arg) {
408  if (which != endorder && which != leaf) return;
409  ++(*((int*)arg));
410 }
411 
412 
426  niceassert( init, "inotifytools_initialize not called yet" );
427 
428  // if already collecting stats, reset stats
429  if (collect_stats) {
430  rbwalk(tree_wd, empty_stats, 0);
431  }
432 
433  num_access = 0;
434  num_modify = 0;
435  num_attrib = 0;
436  num_close_nowrite = 0;
437  num_close_write = 0;
438  num_open = 0;
439  num_move_self = 0;
440  num_moved_from = 0;
441  num_moved_to = 0;
442  num_create = 0;
443  num_delete = 0;
444  num_delete_self = 0;
445  num_unmount = 0;
446  num_total = 0;
447 
448  collect_stats = 1;
449 }
450 
478 int inotifytools_str_to_event_sep(char const * event, char sep) {
479  if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
480  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
481  return -1;
482  }
483 
484  int ret, ret1, len;
485  char * event1, * event2;
486  static const size_t eventstr_size = 4096;
487  char eventstr[eventstr_size];
488  ret = 0;
489 
490  if ( !event || !event[0] ) return 0;
491 
492  event1 = (char *)event;
493  event2 = strchr( event1, sep );
494  while ( event1 && event1[0] ) {
495  if ( event2 ) {
496  len = event2 - event1;
497  niceassert(len < eventstr_size,
498  "malformed event string (very long)");
499  }
500  else {
501  len = strlen(event1);
502  }
503  if (len > eventstr_size - 1)
504  len = eventstr_size - 1;
505 
506  if (event2 || len == eventstr_size - 1) {
507  strncpy(eventstr, event1, len);
508  } else {
509  strcpy(eventstr, event1);
510  }
511 
512  eventstr[len] = 0;
513 
514  ret1 = onestr_to_event( eventstr );
515  if ( 0 == ret1 || -1 == ret1 ) {
516  ret = ret1;
517  break;
518  }
519  ret |= ret1;
520 
521  event1 = event2;
522  if ( event1 && event1[0] ) {
523  // jump over 'sep' character
524  ++event1;
525  // if last character was 'sep'...
526  if ( !event1[0] ) return 0;
527  event2 = strchr( event1, sep );
528  }
529  }
530 
531  return ret;
532 }
533 
557 int inotifytools_str_to_event(char const * event) {
558  return inotifytools_str_to_event_sep( event, ',' );
559 }
560 
572 int onestr_to_event(char const * event)
573 {
574  static int ret;
575  ret = -1;
576 
577  if ( !event || !event[0] )
578  ret = 0;
579  else if ( 0 == strcasecmp(event, "ACCESS") )
580  ret = IN_ACCESS;
581  else if ( 0 == strcasecmp(event, "MODIFY") )
582  ret = IN_MODIFY;
583  else if ( 0 == strcasecmp(event, "ATTRIB") )
584  ret = IN_ATTRIB;
585  else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
586  ret = IN_CLOSE_WRITE;
587  else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
588  ret = IN_CLOSE_NOWRITE;
589  else if ( 0 == strcasecmp(event, "OPEN") )
590  ret = IN_OPEN;
591  else if ( 0 == strcasecmp(event, "MOVED_FROM") )
592  ret = IN_MOVED_FROM;
593  else if ( 0 == strcasecmp(event, "MOVED_TO") )
594  ret = IN_MOVED_TO;
595  else if ( 0 == strcasecmp(event, "CREATE") )
596  ret = IN_CREATE;
597  else if ( 0 == strcasecmp(event, "DELETE") )
598  ret = IN_DELETE;
599  else if ( 0 == strcasecmp(event, "DELETE_SELF") )
600  ret = IN_DELETE_SELF;
601  else if ( 0 == strcasecmp(event, "UNMOUNT") )
602  ret = IN_UNMOUNT;
603  else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
604  ret = IN_Q_OVERFLOW;
605  else if ( 0 == strcasecmp(event, "IGNORED") )
606  ret = IN_IGNORED;
607  else if ( 0 == strcasecmp(event, "CLOSE") )
608  ret = IN_CLOSE;
609  else if ( 0 == strcasecmp(event, "MOVE_SELF") )
610  ret = IN_MOVE_SELF;
611  else if ( 0 == strcasecmp(event, "MOVE") )
612  ret = IN_MOVE;
613  else if ( 0 == strcasecmp(event, "ISDIR") )
614  ret = IN_ISDIR;
615  else if ( 0 == strcasecmp(event, "ONESHOT") )
616  ret = IN_ONESHOT;
617  else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
618  ret = IN_ALL_EVENTS;
619 
620  return ret;
621 }
622 
644 char * inotifytools_event_to_str(int events) {
645  return inotifytools_event_to_str_sep(events, ',');
646 }
647 
672 char * inotifytools_event_to_str_sep(int events, char sep)
673 {
674  static char ret[1024];
675  ret[0] = '\0';
676  ret[1] = '\0';
677 
678  if ( IN_ACCESS & events ) {
679  strcat( ret, chrtostr(sep) );
680  strcat( ret, "ACCESS" );
681  }
682  if ( IN_MODIFY & events ) {
683  strcat( ret, chrtostr(sep) );
684  strcat( ret, "MODIFY" );
685  }
686  if ( IN_ATTRIB & events ) {
687  strcat( ret, chrtostr(sep) );
688  strcat( ret, "ATTRIB" );
689  }
690  if ( IN_CLOSE_WRITE & events ) {
691  strcat( ret, chrtostr(sep) );
692  strcat( ret, "CLOSE_WRITE" );
693  }
694  if ( IN_CLOSE_NOWRITE & events ) {
695  strcat( ret, chrtostr(sep) );
696  strcat( ret, "CLOSE_NOWRITE" );
697  }
698  if ( IN_OPEN & events ) {
699  strcat( ret, chrtostr(sep) );
700  strcat( ret, "OPEN" );
701  }
702  if ( IN_MOVED_FROM & events ) {
703  strcat( ret, chrtostr(sep) );
704  strcat( ret, "MOVED_FROM" );
705  }
706  if ( IN_MOVED_TO & events ) {
707  strcat( ret, chrtostr(sep) );
708  strcat( ret, "MOVED_TO" );
709  }
710  if ( IN_CREATE & events ) {
711  strcat( ret, chrtostr(sep) );
712  strcat( ret, "CREATE" );
713  }
714  if ( IN_DELETE & events ) {
715  strcat( ret, chrtostr(sep) );
716  strcat( ret, "DELETE" );
717  }
718  if ( IN_DELETE_SELF & events ) {
719  strcat( ret, chrtostr(sep) );
720  strcat( ret, "DELETE_SELF" );
721  }
722  if ( IN_UNMOUNT & events ) {
723  strcat( ret, chrtostr(sep) );
724  strcat( ret, "UNMOUNT" );
725  }
726  if ( IN_Q_OVERFLOW & events ) {
727  strcat( ret, chrtostr(sep) );
728  strcat( ret, "Q_OVERFLOW" );
729  }
730  if ( IN_IGNORED & events ) {
731  strcat( ret, chrtostr(sep) );
732  strcat( ret, "IGNORED" );
733  }
734  if ( IN_CLOSE & events ) {
735  strcat( ret, chrtostr(sep) );
736  strcat( ret, "CLOSE" );
737  }
738  if ( IN_MOVE_SELF & events ) {
739  strcat( ret, chrtostr(sep) );
740  strcat( ret, "MOVE_SELF" );
741  }
742  if ( IN_ISDIR & events ) {
743  strcat( ret, chrtostr(sep) );
744  strcat( ret, "ISDIR" );
745  }
746  if ( IN_ONESHOT & events ) {
747  strcat( ret, chrtostr(sep) );
748  strcat( ret, "ONESHOT" );
749  }
750 
751  // Maybe we didn't match any... ?
752  if (ret[0] == '\0') {
753  niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
754  }
755 
756  return &ret[1];
757 }
758 
780  niceassert( init, "inotifytools_initialize not called yet" );
781  watch *w = watch_from_wd(wd);
782  if (!w)
783  return NULL;
784 
785  return w->filename;
786 }
787 
802 int inotifytools_wd_from_filename( char const * filename ) {
803  niceassert( init, "inotifytools_initialize not called yet" );
804  watch *w = watch_from_filename(filename);
805  if (!w) return -1;
806  return w->wd;
807 }
808 
823 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
824  niceassert( init, "inotifytools_initialize not called yet" );
825  watch *w = watch_from_wd(wd);
826  if (!w) return;
827  if (w->filename) free(w->filename);
828  w->filename = strdup(filename);
829 }
830 
845 void inotifytools_set_filename_by_filename( char const * oldname,
846  char const * newname ) {
847  watch *w = watch_from_filename(oldname);
848  if (!w) return;
849  if (w->filename) free(w->filename);
850  w->filename = strdup(newname);
851 }
852 
875 void inotifytools_replace_filename( char const * oldname,
876  char const * newname ) {
877  if ( !oldname || !newname ) return;
878  struct replace_filename_data data;
879  data.old_name = oldname;
880  data.new_name = newname;
881  data.old_len = strlen(oldname);
882  rbwalk(tree_filename, (void *)replace_filename, (void *)&data);
883 }
884 
888 int remove_inotify_watch(watch *w) {
889  error = 0;
890  int status = inotify_rm_watch( inotify_fd, w->wd );
891  if ( status < 0 ) {
892  fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
893  strerror(status) );
894  error = status;
895  return 0;
896  }
897  return 1;
898 }
899 
903 watch *create_watch(int wd, char *filename) {
904  if ( wd <= 0 || !filename) return 0;
905 
906  watch *w = (watch*)calloc(1, sizeof(watch));
907  w->wd = wd;
908  w->filename = strdup(filename);
909  rbsearch(w, tree_wd);
910  rbsearch(w, tree_filename);
911  return NULL;
912 }
913 
927  niceassert( init, "inotifytools_initialize not called yet" );
928  watch *w = watch_from_wd(wd);
929  if (!w) return 1;
930 
931  if (!remove_inotify_watch(w)) return 0;
932  rbdelete(w, tree_wd);
933  rbdelete(w, tree_filename);
934  destroy_watch(w);
935  return 1;
936 }
937 
949 int inotifytools_remove_watch_by_filename( char const * filename ) {
950  niceassert( init, "inotifytools_initialize not called yet" );
951  watch *w = watch_from_filename(filename);
952  if (!w) return 1;
953 
954  if (!remove_inotify_watch(w)) return 0;
955  rbdelete(w, tree_wd);
956  rbdelete(w, tree_filename);
957  destroy_watch(w);
958  return 1;
959 }
960 
972 int inotifytools_watch_file( char const * filename, int events ) {
973  static char const * filenames[2];
974  filenames[0] = filename;
975  filenames[1] = NULL;
976  return inotifytools_watch_files( filenames, events );
977 }
978 
994 int inotifytools_watch_files( char const * filenames[], int events ) {
995  niceassert( init, "inotifytools_initialize not called yet" );
996  error = 0;
997 
998  static int i;
999  for ( i = 0; filenames[i]; ++i ) {
1000  static int wd;
1001  wd = inotify_add_watch( inotify_fd, filenames[i], events );
1002  if ( wd < 0 ) {
1003  if ( wd == -1 ) {
1004  error = errno;
1005  return 0;
1006  } // if ( wd == -1 )
1007  else {
1008  fprintf( stderr, "Failed to watch %s: returned wd was %d "
1009  "(expected -1 or >0 )", filenames[i], wd );
1010  // no appropriate value for error
1011  return 0;
1012  } // else
1013  } // if ( wd < 0 )
1014 
1015  char *filename;
1016  // Always end filename with / if it is a directory
1017  if ( !isdir(filenames[i])
1018  || filenames[i][strlen(filenames[i])-1] == '/') {
1019  filename = strdup(filenames[i]);
1020  }
1021  else {
1022  nasprintf( &filename, "%s/", filenames[i] );
1023  }
1024  create_watch(wd, filename);
1025  free(filename);
1026  } // for
1027 
1028  return 1;
1029 }
1030 
1057 struct inotify_event * inotifytools_next_event( long int timeout ) {
1058  return inotifytools_next_events( timeout, 1 );
1059 }
1060 
1061 
1110 struct inotify_event * inotifytools_next_events( long int timeout, int num_events ) {
1111  niceassert( init, "inotifytools_initialize not called yet" );
1112  niceassert( num_events <= MAX_EVENTS, "too many events requested" );
1113 
1114  if ( num_events < 1 ) return NULL;
1115 
1116  static struct inotify_event event[MAX_EVENTS];
1117  static struct inotify_event * ret;
1118  static int first_byte = 0;
1119  static ssize_t bytes;
1120  static jmp_buf jmp;
1121  static struct nstring match_name;
1122  static char match_name_string[MAX_STRLEN+1];
1123 
1124 #define RETURN(A) {\
1125  if (regex) {\
1126  inotifytools_snprintf(&match_name, MAX_STRLEN, A, "%w%f");\
1127  memcpy(&match_name_string, &match_name.buf, match_name.len);\
1128  match_name_string[match_name.len] = '\0';\
1129  if (0 == regexec(regex, match_name_string, 0, 0, 0)) {\
1130  if (!invert_regexp)\
1131  longjmp(jmp,0);\
1132  } else {\
1133  if (invert_regexp)\
1134  longjmp(jmp,0);\
1135  }\
1136  }\
1137  if ( collect_stats ) {\
1138  record_stats( A );\
1139  }\
1140  return A;\
1141 }
1142 
1143  setjmp(jmp);
1144 
1145  error = 0;
1146 
1147  // first_byte is index into event buffer
1148  if ( first_byte != 0
1149  && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
1150 
1151  ret = (struct inotify_event *)((char *)&event[0] + first_byte);
1152  first_byte += sizeof(struct inotify_event) + ret->len;
1153 
1154  // if the pointer to the next event exactly hits end of bytes read,
1155  // that's good. next time we're called, we'll read.
1156  if ( first_byte == bytes ) {
1157  first_byte = 0;
1158  }
1159  else if ( first_byte > bytes ) {
1160  // oh... no. this can't be happening. An incomplete event.
1161  // Copy what we currently have into first element, call self to
1162  // read remainder.
1163  // oh, and they BETTER NOT overlap.
1164  // Boy I hope this code works.
1165  // But I think this can never happen due to how inotify is written.
1166  niceassert( (long)((char *)&event[0] +
1167  sizeof(struct inotify_event) +
1168  event[0].len) <= (long)ret,
1169  "extremely unlucky user, death imminent" );
1170  // how much of the event do we have?
1171  bytes = (char *)&event[0] + bytes - (char *)ret;
1172  memcpy( &event[0], ret, bytes );
1173  return inotifytools_next_events( timeout, num_events );
1174  }
1175  RETURN(ret);
1176 
1177  }
1178 
1179  else if ( first_byte == 0 ) {
1180  bytes = 0;
1181  }
1182 
1183 
1184  static ssize_t this_bytes;
1185  static unsigned int bytes_to_read;
1186  static int rc;
1187  static fd_set read_fds;
1188 
1189  static struct timeval read_timeout;
1190  read_timeout.tv_sec = timeout;
1191  read_timeout.tv_usec = 0;
1192  static struct timeval * read_timeout_ptr;
1193  read_timeout_ptr = ( timeout < 0 ? NULL : &read_timeout );
1194 
1195  FD_ZERO(&read_fds);
1196  FD_SET(inotify_fd, &read_fds);
1197  rc = select(inotify_fd + 1, &read_fds,
1198  NULL, NULL, read_timeout_ptr);
1199  if ( rc < 0 ) {
1200  // error
1201  error = errno;
1202  return NULL;
1203  }
1204  else if ( rc == 0 ) {
1205  // timeout
1206  return NULL;
1207  }
1208 
1209  // wait until we have enough bytes to read
1210  do {
1211  rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
1212  } while ( !rc &&
1213  bytes_to_read < sizeof(struct inotify_event)*num_events );
1214 
1215  if ( rc == -1 ) {
1216  error = errno;
1217  return NULL;
1218  }
1219 
1220  this_bytes = read(inotify_fd, &event[0] + bytes,
1221  sizeof(struct inotify_event)*MAX_EVENTS - bytes);
1222  if ( this_bytes < 0 ) {
1223  error = errno;
1224  return NULL;
1225  }
1226  if ( this_bytes == 0 ) {
1227  fprintf(stderr, "Inotify reported end-of-file. Possibly too many "
1228  "events occurred at once.\n");
1229  return NULL;
1230  }
1231  bytes += this_bytes;
1232 
1233  ret = &event[0];
1234  first_byte = sizeof(struct inotify_event) + ret->len;
1235  niceassert( first_byte <= bytes, "ridiculously long filename, things will "
1236  "almost certainly screw up." );
1237  if ( first_byte == bytes ) {
1238  first_byte = 0;
1239  }
1240 
1241  RETURN(ret);
1242 
1243 #undef RETURN
1244 }
1245 
1271 int inotifytools_watch_recursively( char const * path, int events ) {
1272  return inotifytools_watch_recursively_with_exclude( path, events, 0 );
1273 }
1274 
1307 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
1308  char const ** exclude_list ) {
1309  niceassert( init, "inotifytools_initialize not called yet" );
1310 
1311  DIR * dir;
1312  char * my_path;
1313  error = 0;
1314  dir = opendir( path );
1315  if ( !dir ) {
1316  // If not a directory, don't need to do anything special
1317  if ( errno == ENOTDIR ) {
1318  return inotifytools_watch_file( path, events );
1319  }
1320  else {
1321  error = errno;
1322  return 0;
1323  }
1324  }
1325 
1326  if ( path[strlen(path)-1] != '/' ) {
1327  nasprintf( &my_path, "%s/", path );
1328  }
1329  else {
1330  my_path = (char *)path;
1331  }
1332 
1333  static struct dirent * ent;
1334  char * next_file;
1335  static struct stat64 my_stat;
1336  ent = readdir( dir );
1337  // Watch each directory within this directory
1338  while ( ent ) {
1339  if ( (0 != strcmp( ent->d_name, "." )) &&
1340  (0 != strcmp( ent->d_name, ".." )) ) {
1341  nasprintf(&next_file,"%s%s", my_path, ent->d_name);
1342  if ( -1 == lstat64( next_file, &my_stat ) ) {
1343  error = errno;
1344  free( next_file );
1345  if ( errno != EACCES ) {
1346  error = errno;
1347  if ( my_path != path ) free( my_path );
1348  closedir( dir );
1349  return 0;
1350  }
1351  }
1352  else if ( S_ISDIR( my_stat.st_mode ) &&
1353  !S_ISLNK( my_stat.st_mode )) {
1354  free( next_file );
1355  nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
1356  static unsigned int no_watch;
1357  static char const ** exclude_entry;
1358 
1359  no_watch = 0;
1360  for (exclude_entry = exclude_list;
1361  exclude_entry && *exclude_entry && !no_watch;
1362  ++exclude_entry) {
1363  static int exclude_length;
1364 
1365  exclude_length = strlen(*exclude_entry);
1366  if ((*exclude_entry)[exclude_length-1] == '/') {
1367  --exclude_length;
1368  }
1369  if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
1370  !strncmp(*exclude_entry, next_file, exclude_length)) {
1371  // directory found in exclude list
1372  no_watch = 1;
1373  }
1374  }
1375  if (!no_watch) {
1376  static int status;
1378  next_file,
1379  events,
1380  exclude_list );
1381  // For some errors, we will continue.
1382  if ( !status && (EACCES != error) && (ENOENT != error) &&
1383  (ELOOP != error) ) {
1384  free( next_file );
1385  if ( my_path != path ) free( my_path );
1386  closedir( dir );
1387  return 0;
1388  }
1389  } // if !no_watch
1390  free( next_file );
1391  } // if isdir and not islnk
1392  else {
1393  free( next_file );
1394  }
1395  }
1396  ent = readdir( dir );
1397  error = 0;
1398  }
1399 
1400  closedir( dir );
1401 
1402  int ret = inotifytools_watch_file( my_path, events );
1403  if ( my_path != path ) free( my_path );
1404  return ret;
1405 }
1406 
1410 void record_stats( struct inotify_event const * event ) {
1411  if (!event) return;
1412  watch *w = watch_from_wd(event->wd);
1413  if (!w) return;
1414  if ( IN_ACCESS & event->mask ) {
1415  ++w->hit_access;
1416  ++num_access;
1417  }
1418  if ( IN_MODIFY & event->mask ) {
1419  ++w->hit_modify;
1420  ++num_modify;
1421  }
1422  if ( IN_ATTRIB & event->mask ) {
1423  ++w->hit_attrib;
1424  ++num_attrib;
1425  }
1426  if ( IN_CLOSE_WRITE & event->mask ) {
1427  ++w->hit_close_write;
1428  ++num_close_write;
1429  }
1430  if ( IN_CLOSE_NOWRITE & event->mask ) {
1431  ++w->hit_close_nowrite;
1432  ++num_close_nowrite;
1433  }
1434  if ( IN_OPEN & event->mask ) {
1435  ++w->hit_open;
1436  ++num_open;
1437  }
1438  if ( IN_MOVED_FROM & event->mask ) {
1439  ++w->hit_moved_from;
1440  ++num_moved_from;
1441  }
1442  if ( IN_MOVED_TO & event->mask ) {
1443  ++w->hit_moved_to;
1444  ++num_moved_to;
1445  }
1446  if ( IN_CREATE & event->mask ) {
1447  ++w->hit_create;
1448  ++num_create;
1449  }
1450  if ( IN_DELETE & event->mask ) {
1451  ++w->hit_delete;
1452  ++num_delete;
1453  }
1454  if ( IN_DELETE_SELF & event->mask ) {
1455  ++w->hit_delete_self;
1456  ++num_delete_self;
1457  }
1458  if ( IN_UNMOUNT & event->mask ) {
1459  ++w->hit_unmount;
1460  ++num_unmount;
1461  }
1462  if ( IN_MOVE_SELF & event->mask ) {
1463  ++w->hit_move_self;
1464  ++num_move_self;
1465  }
1466 
1467  ++w->hit_total;
1468  ++num_total;
1469 
1470 }
1471 
1472 unsigned int *stat_ptr(watch *w, int event)
1473 {
1474  if ( IN_ACCESS == event )
1475  return &w->hit_access;
1476  if ( IN_MODIFY == event )
1477  return &w->hit_modify;
1478  if ( IN_ATTRIB == event )
1479  return &w->hit_attrib;
1480  if ( IN_CLOSE_WRITE == event )
1481  return &w->hit_close_write;
1482  if ( IN_CLOSE_NOWRITE == event )
1483  return &w->hit_close_nowrite;
1484  if ( IN_OPEN == event )
1485  return &w->hit_open;
1486  if ( IN_MOVED_FROM == event )
1487  return &w->hit_moved_from;
1488  if ( IN_MOVED_TO == event )
1489  return &w->hit_moved_to;
1490  if ( IN_CREATE == event )
1491  return &w->hit_create;
1492  if ( IN_DELETE == event )
1493  return &w->hit_delete;
1494  if ( IN_DELETE_SELF == event )
1495  return &w->hit_delete_self;
1496  if ( IN_UNMOUNT == event )
1497  return &w->hit_unmount;
1498  if ( IN_MOVE_SELF == event )
1499  return &w->hit_move_self;
1500  if ( 0 == event )
1501  return &w->hit_total;
1502  return 0;
1503 }
1504 
1520 int inotifytools_get_stat_by_wd( int wd, int event ) {
1521  if (!collect_stats) return -1;
1522 
1523  watch *w = watch_from_wd(wd);
1524  if (!w) return -1;
1525  unsigned int *i = stat_ptr(w, event);
1526  if (!i) return -1;
1527  return *i;
1528 }
1529 
1544  if (!collect_stats) return -1;
1545  if ( IN_ACCESS == event )
1546  return num_access;
1547  if ( IN_MODIFY == event )
1548  return num_modify;
1549  if ( IN_ATTRIB == event )
1550  return num_attrib;
1551  if ( IN_CLOSE_WRITE == event )
1552  return num_close_write;
1553  if ( IN_CLOSE_NOWRITE == event )
1554  return num_close_nowrite;
1555  if ( IN_OPEN == event )
1556  return num_open;
1557  if ( IN_MOVED_FROM == event )
1558  return num_moved_from;
1559  if ( IN_MOVED_TO == event )
1560  return num_moved_to;
1561  if ( IN_CREATE == event )
1562  return num_create;
1563  if ( IN_DELETE == event )
1564  return num_delete;
1565  if ( IN_DELETE_SELF == event )
1566  return num_delete_self;
1567  if ( IN_UNMOUNT == event )
1568  return num_unmount;
1569  if ( IN_MOVE_SELF == event )
1570  return num_move_self;
1571 
1572  if ( 0 == event )
1573  return num_total;
1574 
1575  return -1;
1576 }
1577 
1597 int inotifytools_get_stat_by_filename( char const * filename,
1598  int event ) {
1600  filename ), event );
1601 }
1602 
1614  return error;
1615 }
1616 
1620 static int isdir( char const * path ) {
1621  static struct stat64 my_stat;
1622 
1623  if ( -1 == lstat64( path, &my_stat ) ) {
1624  if (errno == ENOENT) return 0;
1625  fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
1626  return 0;
1627  }
1628 
1629  return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
1630 }
1631 
1632 
1640  int ret = 0;
1641  rbwalk(tree_filename, get_num, (void*)&ret);
1642  return ret;
1643 }
1644 
1689 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
1690  return inotifytools_fprintf( stdout, event, fmt );
1691 }
1692 
1738 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
1739  static struct nstring out;
1740  static int ret;
1741  ret = inotifytools_sprintf( &out, event, fmt );
1742  if ( -1 != ret ) fwrite( out.buf, sizeof(char), out.len, file );
1743  return ret;
1744 }
1745 
1798 int inotifytools_sprintf( struct nstring * out, struct inotify_event* event, char* fmt ) {
1799  return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
1800 }
1801 
1802 
1853 int inotifytools_snprintf( struct nstring * out, int size,
1854  struct inotify_event* event, char* fmt ) {
1855  static char * filename, * eventname, * eventstr;
1856  static unsigned int i, ind;
1857  static char ch1;
1858  static char timestr[MAX_STRLEN];
1859  static time_t now;
1860 
1861  if ( event->len > 0 ) {
1862  eventname = event->name;
1863  }
1864  else {
1865  eventname = NULL;
1866  }
1867 
1868 
1869  filename = inotifytools_filename_from_wd( event->wd );
1870 
1871  if ( !fmt || 0 == strlen(fmt) ) {
1872  error = EINVAL;
1873  return -1;
1874  }
1875  if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
1876  error = EMSGSIZE;
1877  return -1;
1878  }
1879 
1880  ind = 0;
1881  for ( i = 0; i < strlen(fmt) &&
1882  (int)ind < size - 1; ++i ) {
1883  if ( fmt[i] != '%' ) {
1884  out->buf[ind++] = fmt[i];
1885  continue;
1886  }
1887 
1888  if ( i == strlen(fmt) - 1 ) {
1889  // last character is %, invalid
1890  error = EINVAL;
1891  return ind;
1892  }
1893 
1894  ch1 = fmt[i+1];
1895 
1896  if ( ch1 == '%' ) {
1897  out->buf[ind++] = '%';
1898  ++i;
1899  continue;
1900  }
1901 
1902  if ( ch1 == '0' ) {
1903  out->buf[ind++] = '\0';
1904  ++i;
1905  continue;
1906  }
1907 
1908  if ( ch1 == 'n' ) {
1909  out->buf[ind++] = '\n';
1910  ++i;
1911  continue;
1912  }
1913 
1914  if ( ch1 == 'w' ) {
1915  if ( filename ) {
1916  strncpy( &out->buf[ind], filename, size - ind );
1917  ind += strlen(filename);
1918  }
1919  ++i;
1920  continue;
1921  }
1922 
1923  if ( ch1 == 'f' ) {
1924  if ( eventname ) {
1925  strncpy( &out->buf[ind], eventname, size - ind );
1926  ind += strlen(eventname);
1927  }
1928  ++i;
1929  continue;
1930  }
1931 
1932  if ( ch1 == 'c' ) {
1933  ind += snprintf( &out->buf[ind], size-ind, "%x", event->cookie);
1934  ++i;
1935  continue;
1936  }
1937 
1938  if ( ch1 == 'e' ) {
1939  eventstr = inotifytools_event_to_str( event->mask );
1940  strncpy( &out->buf[ind], eventstr, size - ind );
1941  ind += strlen(eventstr);
1942  ++i;
1943  continue;
1944  }
1945 
1946  if ( ch1 == 'T' ) {
1947 
1948  if ( timefmt ) {
1949  now = time(0);
1950  struct tm now_tm;
1951  if (0 >= strftime(timestr, MAX_STRLEN - 1,
1952  timefmt,
1953  localtime_r(&now, &now_tm))) {
1954  // time format probably invalid
1955  error = EINVAL;
1956  return ind;
1957  }
1958  }
1959  else {
1960  timestr[0] = 0;
1961  }
1962 
1963  strncpy( &out->buf[ind], timestr, size - ind );
1964  ind += strlen(timestr);
1965  ++i;
1966  continue;
1967  }
1968 
1969  // Check if next char in fmt is e
1970  if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
1971  eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
1972  strncpy( &out->buf[ind], eventstr, size - ind );
1973  ind += strlen(eventstr);
1974  i += 2;
1975  continue;
1976  }
1977 
1978  // OK, this wasn't a special format character, just output it as normal
1979  if ( ind < MAX_STRLEN ) out->buf[ind++] = '%';
1980  if ( ind < MAX_STRLEN ) out->buf[ind++] = ch1;
1981  ++i;
1982  }
1983  out->len = ind;
1984 
1985  return ind - 1;
1986 }
1987 
1998  timefmt = fmt;
1999 }
2000 
2010  int ret;
2011  if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
2012  return ret;
2013 }
2014 
2025  int ret;
2026  if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
2027  return ret;
2028 }
2029 
2040  int ret;
2041  if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
2042  return ret;
2043 }
2044 
2058 static int do_ignore_events_by_regex( char const *pattern, int flags, int invert ) {
2059  if (!pattern) {
2060  if (regex) {
2061  regfree(regex);
2062  free(regex);
2063  regex = 0;
2064  }
2065  return 1;
2066  }
2067 
2068  if (regex) { regfree(regex); }
2069  else { regex = (regex_t *)malloc(sizeof(regex_t)); }
2070 
2071  invert_regexp = invert;
2072  int ret = regcomp(regex, pattern, flags | REG_NOSUB);
2073  if (0 == ret) return 1;
2074 
2075  regfree(regex);
2076  free(regex);
2077  regex = 0;
2078  error = EINVAL;
2079  return 0;
2080 }
2081 
2093 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
2094  return do_ignore_events_by_regex(pattern, flags, 0);
2095 }
2096 
2108 int inotifytools_ignore_events_by_inverted_regex( char const *pattern, int flags ) {
2109  return do_ignore_events_by_regex(pattern, flags, 1);
2110 }
2111 
2112 int event_compare(const void *p1, const void *p2, const void *config)
2113 {
2114  if (!p1 || !p2) return p1 - p2;
2115  char asc = 1;
2116  long sort_event = (long)config;
2117  if (sort_event == -1) {
2118  sort_event = 0;
2119  asc = 0;
2120  } else if (sort_event < 0) {
2121  sort_event = -sort_event;
2122  asc = 0;
2123  }
2124  unsigned int *i1 = stat_ptr((watch*)p1, sort_event);
2125  unsigned int *i2 = stat_ptr((watch*)p2, sort_event);
2126  if (0 == *i1 - *i2) {
2127  return ((watch*)p1)->wd - ((watch*)p2)->wd;
2128  }
2129  if (asc)
2130  return *i1 - *i2;
2131  else
2132  return *i2 - *i1;
2133 }
2134 
2135 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
2136 {
2137  struct rbtree *ret = rbinit(event_compare, (void*)(uintptr_t)sort_event);
2138  RBLIST *all = rbopenlist(tree_wd);
2139  void const *p = rbreadlist(all);
2140  while (p) {
2141  void const *r = rbsearch(p, ret);
2142  niceassert((int)(r == p), "Couldn't insert watch into new tree");
2143  p = rbreadlist(all);
2144  }
2145  rbcloselist(all);
2146  return ret;
2147 }
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:926
inotifytools_next_events
struct inotify_event * inotifytools_next_events(long int timeout, int num_events)
Definition: inotifytools.c:1110
inotifytools_get_stat_by_filename
int inotifytools_get_stat_by_filename(char const *filename, int event)
Definition: inotifytools.c:1597
inotifytools_set_printf_timefmt
void inotifytools_set_printf_timefmt(char *fmt)
Definition: inotifytools.c:1997
inotifytools_event_to_str_sep
char * inotifytools_event_to_str_sep(int events, char sep)
Definition: inotifytools.c:672
inotifytools_initialize_stats
void inotifytools_initialize_stats()
Definition: inotifytools.c:425
inotifytools_watch_recursively_with_exclude
int inotifytools_watch_recursively_with_exclude(char const *path, int events, char const **exclude_list)
Definition: inotifytools.c:1307
inotifytools_printf
int inotifytools_printf(struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1689
nstring::buf
char buf[MAX_STRLEN]
Definition: inotifytools.h:26
inotifytools_get_max_user_watches
int inotifytools_get_max_user_watches()
Definition: inotifytools.c:2039
inotifytools_set_filename_by_filename
void inotifytools_set_filename_by_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:845
inotifytools_fprintf
int inotifytools_fprintf(FILE *file, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1738
inotifytools_sprintf
int inotifytools_sprintf(struct nstring *out, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1798
inotifytools_get_stat_total
int inotifytools_get_stat_total(int event)
Definition: inotifytools.c:1543
inotifytools_set_filename_by_wd
void inotifytools_set_filename_by_wd(int wd, char const *filename)
Definition: inotifytools.c:823
inotifytools_snprintf
int inotifytools_snprintf(struct nstring *out, int size, struct inotify_event *event, char *fmt)
Definition: inotifytools.c:1853
inotifytools_get_num_watches
int inotifytools_get_num_watches()
Definition: inotifytools.c:1639
inotifytools_get_stat_by_wd
int inotifytools_get_stat_by_wd(int wd, int event)
Definition: inotifytools.c:1520
inotifytools_event_to_str
char * inotifytools_event_to_str(int events)
Definition: inotifytools.c:644
inotifytools_watch_file
int inotifytools_watch_file(char const *filename, int events)
Definition: inotifytools.c:972
inotifytools_error
int inotifytools_error()
Definition: inotifytools.c:1613
inotifytools_wd_from_filename
int inotifytools_wd_from_filename(char const *filename)
Definition: inotifytools.c:802
inotifytools.h
inotifytools library public interface.
inotifytools_str_to_event
int inotifytools_str_to_event(char const *event)
Definition: inotifytools.c:557
inotifytools_watch_files
int inotifytools_watch_files(char const *filenames[], int events)
Definition: inotifytools.c:994
inotifytools_ignore_events_by_regex
int inotifytools_ignore_events_by_regex(char const *pattern, int flags)
Definition: inotifytools.c:2093
nstring
This structure holds string that can contain any charater including NULL.
Definition: inotifytools.h:25
inotifytools_remove_watch_by_filename
int inotifytools_remove_watch_by_filename(char const *filename)
Definition: inotifytools.c:949
inotifytools_replace_filename
void inotifytools_replace_filename(char const *oldname, char const *newname)
Definition: inotifytools.c:875
inotifytools_next_event
struct inotify_event * inotifytools_next_event(long int timeout)
Definition: inotifytools.c:1057
inotifytools_str_to_event_sep
int inotifytools_str_to_event_sep(char const *event, char sep)
Definition: inotifytools.c:478
inotifytools_filename_from_wd
char * inotifytools_filename_from_wd(int wd)
Definition: inotifytools.c:779
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:2108
inotifytools_watch_recursively
int inotifytools_watch_recursively(char const *path, int events)
Definition: inotifytools.c:1271
inotifytools_get_max_queued_events
int inotifytools_get_max_queued_events()
Definition: inotifytools.c:2009
nstring::len
unsigned int len
Definition: inotifytools.h:27
inotifytools_get_max_user_instances
int inotifytools_get_max_user_instances()
Definition: inotifytools.c:2024