inotifytools.c

00001 // kate: replace-tabs off; space-indent off;
00002 
00015 #include "../../config.h"
00016 #include "inotifytools/inotifytools.h"
00017 #include "inotifytools_p.h"
00018 
00019 #include <string.h>
00020 #include <strings.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <errno.h>
00024 #include <sys/select.h>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/ioctl.h>
00028 #include <unistd.h>
00029 #include <dirent.h>
00030 #include <time.h>
00031 #include <regex.h>
00032 #include <setjmp.h>
00033 
00034 #include "inotifytools/inotify.h"
00035 
00126 #define MAX_EVENTS 4096
00127 #define MAX_STRLEN 4096
00128 #define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
00129 #define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
00130 #define QUEUE_SIZE_PATH   INOTIFY_PROCDIR "max_queued_watches"
00131 #define INSTANCES_PATH    INOTIFY_PROCDIR "max_user_instances"
00132 
00133 static int inotify_fd;
00134 static unsigned  num_access;
00135 static unsigned  num_modify;
00136 static unsigned  num_attrib;
00137 static unsigned  num_close_nowrite;
00138 static unsigned  num_close_write;
00139 static unsigned  num_open;
00140 static unsigned  num_move_self;
00141 static unsigned  num_moved_to;
00142 static unsigned  num_moved_from;
00143 static unsigned  num_create;
00144 static unsigned  num_delete;
00145 static unsigned  num_delete_self;
00146 static unsigned  num_unmount;
00147 static unsigned  num_total;
00148 static int collect_stats = 0;
00149 
00150 struct rbtree *tree_wd = 0;
00151 struct rbtree *tree_filename = 0;
00152 static int error = 0;
00153 static int init = 0;
00154 static char* timefmt = 0;
00155 static regex_t* regex = 0;
00156 
00157 int isdir( char const * path );
00158 void record_stats( struct inotify_event const * event );
00159 int onestr_to_event(char const * event);
00160 
00178 #define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
00179                                           #cond, mesg)
00180 
00181 #define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
00182 
00200 void _niceassert( long cond, int line, char const * file, char const * condstr,
00201                   char const * mesg ) {
00202         if ( cond ) return;
00203 
00204         if ( mesg ) {
00205                 fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
00206                         condstr, mesg );
00207         }
00208         else {
00209                 fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
00210         }
00211 }
00212 
00222 char * chrtostr(char ch) {
00223         static char str[2] = { '\0', '\0' };
00224         str[0] = ch;
00225         return str;
00226 }
00227 
00231 int read_num_from_file( char * filename, int * num ) {
00232         FILE * file = fopen( filename, "r" );
00233         if ( !file ) {
00234                 error = errno;
00235                 return 0;
00236         }
00237 
00238         if ( EOF == fscanf( file, "%d", num ) ) {
00239                 error = errno;
00240                 return 0;
00241         }
00242 
00243         niceassert( 0 == fclose( file ), 0 );
00244 
00245         return 1;
00246 }
00247 
00248 int wd_compare(const void *d1, const void *d2, const void *config) {
00249         if (!d1 || !d2) return d1 - d2;
00250         return ((watch*)d1)->wd - ((watch*)d2)->wd;
00251 }
00252 
00253 int filename_compare(const void *d1, const void *d2, const void *config) {
00254         if (!d1 || !d2) return d1 - d2;
00255         return strcmp(((watch*)d1)->filename, ((watch*)d2)->filename);
00256 }
00257 
00261 watch *watch_from_wd( int wd ) {
00262         watch w;
00263         w.wd = wd;
00264         return (watch*)rbfind(&w, tree_wd);
00265 }
00266 
00270 watch *watch_from_filename( char const *filename ) {
00271         watch w;
00272         w.filename = (char*)filename;
00273         return (watch*)rbfind(&w, tree_filename);
00274 }
00275 
00285 int inotifytools_initialize() {
00286         if (init) return 1;
00287 
00288         error = 0;
00289         // Try to initialise inotify
00290         inotify_fd = inotify_init();
00291         if (inotify_fd < 0)     {
00292                 error = inotify_fd;
00293                 return 0;
00294         }
00295 
00296         collect_stats = 0;
00297         init = 1;
00298         tree_wd = rbinit(wd_compare, 0);
00299         tree_filename = rbinit(filename_compare, 0);
00300 
00301         return 1;
00302 }
00303 
00307 void empty_stats(const void *nodep,
00308                  const VISIT which,
00309                  const int depth, void *arg) {
00310     if (which != endorder && which != leaf) return;
00311         watch *w = (watch*)nodep;
00312         w->hit_access = 0;
00313         w->hit_modify = 0;
00314         w->hit_attrib = 0;
00315         w->hit_close_nowrite = 0;
00316         w->hit_close_write = 0;
00317         w->hit_open = 0;
00318         w->hit_move_self = 0;
00319         w->hit_moved_from = 0;
00320         w->hit_moved_to = 0;
00321         w->hit_create = 0;
00322         w->hit_delete = 0;
00323         w->hit_delete_self = 0;
00324         w->hit_unmount = 0;
00325         w->hit_total = 0;
00326 }
00327 
00331 void replace_filename(const void *nodep,
00332                       const VISIT which,
00333                       const int depth, void *arg) {
00334     if (which != endorder && which != leaf) return;
00335         watch *w = (watch*)nodep;
00336         char *old_name = ((char**)arg)[0];
00337         char *new_name = ((char**)arg)[1];
00338         int old_len = *((int*)&((char**)arg)[2]);
00339         char *name;
00340         if ( 0 == strncmp( old_name, w->filename, old_len ) ) {
00341                 nasprintf( &name, "%s%s", new_name, &(w->filename[old_len]) );
00342                 if (!strcmp( w->filename, new_name )) {
00343                         free(name);
00344                 } else {
00345                 rbdelete(w, tree_filename);
00346                         free( w->filename );
00347                         w->filename = name;
00348                         rbsearch(w, tree_filename);
00349                 }
00350         }
00351 }
00352 
00356 void get_num(const void *nodep,
00357              const VISIT which,
00358              const int depth, void *arg) {
00359     if (which != endorder && which != leaf) return;
00360         ++(*((int*)arg));
00361 }
00362 
00363 
00376 void inotifytools_initialize_stats() {
00377         niceassert( init, "inotifytools_initialize not called yet" );
00378 
00379         // if already collecting stats, reset stats
00380         if (collect_stats) {
00381                 rbwalk(tree_wd, empty_stats, 0);
00382         }
00383 
00384         num_access = 0;
00385         num_modify = 0;
00386         num_attrib = 0;
00387         num_close_nowrite = 0;
00388         num_close_write = 0;
00389         num_open = 0;
00390         num_move_self = 0;
00391         num_moved_from = 0;
00392         num_moved_to = 0;
00393         num_create = 0;
00394         num_delete = 0;
00395         num_delete_self = 0;
00396         num_unmount = 0;
00397         num_total = 0;
00398 
00399         collect_stats = 1;
00400 }
00401 
00429 int inotifytools_str_to_event_sep(char const * event, char sep) {
00430         if ( strchr( "_" "abcdefghijklmnopqrstuvwxyz"
00431                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sep ) ) {
00432                 return -1;
00433         }
00434 
00435         int ret, ret1, len;
00436         char * event1, * event2;
00437         char eventstr[4096];
00438         ret = 0;
00439 
00440         if ( !event || !event[0] ) return 0;
00441 
00442         event1 = (char *)event;
00443         event2 = strchr( event1, sep );
00444         while ( event1 && event1[0] ) {
00445                 if ( event2 ) {
00446                         len = event2 - event1;
00447                         niceassert( len < 4096, "malformed event string (very long)" );
00448                 }
00449                 else {
00450                         len = strlen(event1);
00451                 }
00452                 if ( len > 4095 ) len = 4095;
00453                 strncpy( eventstr, event1, len );
00454                 eventstr[len] = 0;
00455 
00456                 ret1 = onestr_to_event( eventstr );
00457                 if ( 0 == ret1 || -1 == ret1 ) {
00458                         ret = ret1;
00459                         break;
00460                 }
00461                 ret |= ret1;
00462 
00463                 event1 = event2;
00464                 if ( event1 && event1[0] ) {
00465                         // jump over 'sep' character
00466                         ++event1;
00467                         // if last character was 'sep'...
00468                         if ( !event1[0] ) return 0;
00469                         event2 = strchr( event1, sep );
00470                 }
00471         }
00472 
00473         return ret;
00474 }
00475 
00499 int inotifytools_str_to_event(char const * event) {
00500         return inotifytools_str_to_event_sep( event, ',' );
00501 }
00502 
00514 int onestr_to_event(char const * event)
00515 {
00516         static int ret;
00517         ret = -1;
00518 
00519         if ( !event || !event[0] )
00520                 ret = 0;
00521         else if ( 0 == strcasecmp(event, "ACCESS") )
00522                 ret = IN_ACCESS;
00523         else if ( 0 == strcasecmp(event, "MODIFY") )
00524                 ret = IN_MODIFY;
00525         else if ( 0 == strcasecmp(event, "ATTRIB") )
00526                 ret = IN_ATTRIB;
00527         else if ( 0 == strcasecmp(event, "CLOSE_WRITE") )
00528                 ret = IN_CLOSE_WRITE;
00529         else if ( 0 == strcasecmp(event, "CLOSE_NOWRITE") )
00530                 ret = IN_CLOSE_NOWRITE;
00531         else if ( 0 == strcasecmp(event, "OPEN") )
00532                 ret = IN_OPEN;
00533         else if ( 0 == strcasecmp(event, "MOVED_FROM") )
00534                 ret = IN_MOVED_FROM;
00535         else if ( 0 == strcasecmp(event, "MOVED_TO") )
00536                 ret = IN_MOVED_TO;
00537         else if ( 0 == strcasecmp(event, "CREATE") )
00538                 ret = IN_CREATE;
00539         else if ( 0 == strcasecmp(event, "DELETE") )
00540                 ret = IN_DELETE;
00541         else if ( 0 == strcasecmp(event, "DELETE_SELF") )
00542                 ret = IN_DELETE_SELF;
00543         else if ( 0 == strcasecmp(event, "UNMOUNT") )
00544                 ret = IN_UNMOUNT;
00545         else if ( 0 == strcasecmp(event, "Q_OVERFLOW") )
00546                 ret = IN_Q_OVERFLOW;
00547         else if ( 0 == strcasecmp(event, "IGNORED") )
00548                 ret = IN_IGNORED;
00549         else if ( 0 == strcasecmp(event, "CLOSE") )
00550                 ret = IN_CLOSE;
00551         else if ( 0 == strcasecmp(event, "MOVE_SELF") )
00552                 ret = IN_MOVE_SELF;
00553         else if ( 0 == strcasecmp(event, "MOVE") )
00554                 ret = IN_MOVE;
00555         else if ( 0 == strcasecmp(event, "ISDIR") )
00556                 ret = IN_ISDIR;
00557         else if ( 0 == strcasecmp(event, "ONESHOT") )
00558                 ret = IN_ONESHOT;
00559         else if ( 0 == strcasecmp(event, "ALL_EVENTS") )
00560                 ret = IN_ALL_EVENTS;
00561 
00562         return ret;
00563 }
00564 
00586 char * inotifytools_event_to_str(int events) {
00587         return inotifytools_event_to_str_sep(events, ',');
00588 }
00589 
00614 char * inotifytools_event_to_str_sep(int events, char sep)
00615 {
00616         static char ret[1024];
00617         ret[0] = '\0';
00618         ret[1] = '\0';
00619 
00620         if ( IN_ACCESS & events ) {
00621                 strcat( ret, chrtostr(sep) );
00622                 strcat( ret, "ACCESS" );
00623         }
00624         if ( IN_MODIFY & events ) {
00625                 strcat( ret, chrtostr(sep) );
00626                 strcat( ret, "MODIFY" );
00627         }
00628         if ( IN_ATTRIB & events ) {
00629                 strcat( ret, chrtostr(sep) );
00630                 strcat( ret, "ATTRIB" );
00631         }
00632         if ( IN_CLOSE_WRITE & events ) {
00633                 strcat( ret, chrtostr(sep) );
00634                 strcat( ret, "CLOSE_WRITE" );
00635         }
00636         if ( IN_CLOSE_NOWRITE & events ) {
00637                 strcat( ret, chrtostr(sep) );
00638                 strcat( ret, "CLOSE_NOWRITE" );
00639         }
00640         if ( IN_OPEN & events ) {
00641                 strcat( ret, chrtostr(sep) );
00642                 strcat( ret, "OPEN" );
00643         }
00644         if ( IN_MOVED_FROM & events ) {
00645                 strcat( ret, chrtostr(sep) );
00646                 strcat( ret, "MOVED_FROM" );
00647         }
00648         if ( IN_MOVED_TO & events ) {
00649                 strcat( ret, chrtostr(sep) );
00650                 strcat( ret, "MOVED_TO" );
00651         }
00652         if ( IN_CREATE & events ) {
00653                 strcat( ret, chrtostr(sep) );
00654                 strcat( ret, "CREATE" );
00655         }
00656         if ( IN_DELETE & events ) {
00657                 strcat( ret, chrtostr(sep) );
00658                 strcat( ret, "DELETE" );
00659         }
00660         if ( IN_DELETE_SELF & events ) {
00661                 strcat( ret, chrtostr(sep) );
00662                 strcat( ret, "DELETE_SELF" );
00663         }
00664         if ( IN_UNMOUNT & events ) {
00665                 strcat( ret, chrtostr(sep) );
00666                 strcat( ret, "UNMOUNT" );
00667         }
00668         if ( IN_Q_OVERFLOW & events ) {
00669                 strcat( ret, chrtostr(sep) );
00670                 strcat( ret, "Q_OVERFLOW" );
00671         }
00672         if ( IN_IGNORED & events ) {
00673                 strcat( ret, chrtostr(sep) );
00674                 strcat( ret, "IGNORED" );
00675         }
00676         if ( IN_CLOSE & events ) {
00677                 strcat( ret, chrtostr(sep) );
00678                 strcat( ret, "CLOSE" );
00679         }
00680         if ( IN_MOVE_SELF & events ) {
00681                 strcat( ret, chrtostr(sep) );
00682                 strcat( ret, "MOVE_SELF" );
00683         }
00684         if ( IN_ISDIR & events ) {
00685                 strcat( ret, chrtostr(sep) );
00686                 strcat( ret, "ISDIR" );
00687         }
00688         if ( IN_ONESHOT & events ) {
00689                 strcat( ret, chrtostr(sep) );
00690                 strcat( ret, "ONESHOT" );
00691         }
00692 
00693         // Maybe we didn't match any... ?
00694         if (ret[0] == '\0') {
00695                 niceassert( -1 != sprintf( ret, "%c0x%08x", sep, events ), 0 );
00696         }
00697 
00698         return &ret[1];
00699 }
00700 
00721 char * inotifytools_filename_from_wd( int wd ) {
00722         niceassert( init, "inotifytools_initialize not called yet" );
00723         watch *w = watch_from_wd(wd);
00724         if (!w) return 0;
00725         return w->filename;
00726 }
00727 
00742 int inotifytools_wd_from_filename( char const * filename ) {
00743         niceassert( init, "inotifytools_initialize not called yet" );
00744         watch *w = watch_from_filename(filename);
00745         if (!w) return -1;
00746         return w->wd;
00747 }
00748 
00763 void inotifytools_set_filename_by_wd( int wd, char const * filename ) {
00764         niceassert( init, "inotifytools_initialize not called yet" );
00765         watch *w = watch_from_wd(wd);
00766         if (!w) return;
00767         if (w->filename) free(w->filename);
00768         w->filename = strdup(filename);
00769 }
00770 
00785 void inotifytools_set_filename_by_filename( char const * oldname,
00786                                             char const * newname ) {
00787         watch *w = watch_from_filename(oldname);
00788         if (!w) return;
00789         if (w->filename) free(w->filename);
00790         w->filename = strdup(newname);
00791 }
00792 
00815 void inotifytools_replace_filename( char const * oldname,
00816                                     char const * newname ) {
00817         if ( !oldname || !newname ) return;
00818         char *names[2+sizeof(int)/sizeof(char*)];
00819         names[0] = (char*)oldname;
00820         names[1] = (char*)newname;
00821         *((int*)&names[2]) = strlen(oldname);
00822         rbwalk(tree_filename, replace_filename, (void*)names);
00823 }
00824 
00828 int remove_inotify_watch(watch *w) {
00829         error = 0;
00830         int status = inotify_rm_watch( inotify_fd, w->wd );
00831         if ( status < 0 ) {
00832                 fprintf(stderr, "Failed to remove watch on %s: %s\n", w->filename,
00833                         strerror(status) );
00834                 error = status;
00835                 return 0;
00836         }
00837         return 1;
00838 }
00839 
00843 void destroy_watch(watch *w) {
00844         if (w->filename) free(w->filename);
00845         free(w);
00846 }
00847 
00851 watch *create_watch(int wd, char *filename) {
00852         if ( wd <= 0 || !filename) return 0;
00853 
00854         watch *w = (watch*)calloc(1, sizeof(watch));
00855         w->wd = wd;
00856         w->filename = strdup(filename);
00857         rbsearch(w, tree_wd);
00858         rbsearch(w, tree_filename);
00859 }
00860 
00873 int inotifytools_remove_watch_by_wd( int wd ) {
00874         niceassert( init, "inotifytools_initialize not called yet" );
00875         watch *w = watch_from_wd(wd);
00876         if (!w) return 1;
00877 
00878         if (!remove_inotify_watch(w)) return 0;
00879         rbdelete(w, tree_wd);
00880         rbdelete(w, tree_filename);
00881         destroy_watch(w);
00882         return 1;
00883 }
00884 
00896 int inotifytools_remove_watch_by_filename( char const * filename ) {
00897         niceassert( init, "inotifytools_initialize not called yet" );
00898         watch *w = watch_from_filename(filename);
00899         if (!w) return 1;
00900 
00901         if (!remove_inotify_watch(w)) return 0;
00902         rbdelete(w, tree_wd);
00903         rbdelete(w, tree_filename);
00904         destroy_watch(w);
00905         return 1;
00906 }
00907 
00919 int inotifytools_watch_file( char const * filename, int events ) {
00920         static char const * filenames[2];
00921         filenames[0] = filename;
00922         filenames[1] = NULL;
00923         return inotifytools_watch_files( filenames, events );
00924 }
00925 
00941 int inotifytools_watch_files( char const * filenames[], int events ) {
00942         niceassert( init, "inotifytools_initialize not called yet" );
00943         error = 0;
00944 
00945         static int i;
00946         for ( i = 0; filenames[i]; ++i ) {
00947                 static int wd;
00948                 wd = inotify_add_watch( inotify_fd, filenames[i], events );
00949                 if ( wd < 0 ) {
00950                         if ( wd == -1 ) {
00951                                 error = errno;
00952                                 return 0;
00953                         } // if ( wd == -1 )
00954                         else {
00955                                 fprintf( stderr, "Failed to watch %s: returned wd was %d "
00956                                          "(expected -1 or >0 )", filenames[i], wd );
00957                                 // no appropriate value for error
00958                                 return 0;
00959                         } // else
00960                 } // if ( wd < 0 )
00961 
00962                 char *filename;
00963                 // Always end filename with / if it is a directory
00964                 if ( !isdir(filenames[i])
00965                      || filenames[i][strlen(filenames[i])-1] == '/') {
00966                         filename = strdup(filenames[i]);
00967                 }
00968                 else {
00969                         nasprintf( &filename, "%s/", filenames[i] );
00970                 }
00971                 create_watch(wd, filename);
00972                 free(filename);
00973         } // for
00974 
00975         return 1;
00976 }
00977 
01004 struct inotify_event * inotifytools_next_event( int timeout ) {
01005         return inotifytools_next_events( timeout, 1 );
01006 }
01007 
01008 
01058 struct inotify_event * inotifytools_next_events( int timeout, int num_events ) {
01059         niceassert( init, "inotifytools_initialize not called yet" );
01060         niceassert( num_events <= MAX_EVENTS, "too many events requested" );
01061 
01062         if ( num_events < 1 ) return NULL;
01063 
01064         static struct inotify_event event[MAX_EVENTS];
01065         static struct inotify_event * ret;
01066         static int first_byte = 0;
01067         static ssize_t bytes;
01068         static jmp_buf jmp;
01069         static char match_name[MAX_STRLEN];
01070 
01071 #define RETURN(A) {\
01072         if (regex) {\
01073                 inotifytools_snprintf(match_name, MAX_STRLEN, A, "%w%f");\
01074                 if (0 == regexec(regex, match_name, 0, 0, 0)) {\
01075                         longjmp(jmp,0);\
01076                 }\
01077         }\
01078         if ( collect_stats ) {\
01079                 record_stats( A );\
01080         }\
01081         return A;\
01082 }
01083 
01084         setjmp(jmp);
01085 
01086         error = 0;
01087 
01088         // first_byte is index into event buffer
01089         if ( first_byte != 0
01090           && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
01091 
01092                 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
01093                 first_byte += sizeof(struct inotify_event) + ret->len;
01094 
01095                 // if the pointer to the next event exactly hits end of bytes read,
01096                 // that's good.  next time we're called, we'll read.
01097                 if ( first_byte == bytes ) {
01098                         first_byte = 0;
01099                 }
01100                 else if ( first_byte > bytes ) {
01101                         // oh... no.  this can't be happening.  An incomplete event.
01102                         // Copy what we currently have into first element, call self to
01103                         // read remainder.
01104                         // oh, and they BETTER NOT overlap.
01105                         // Boy I hope this code works.
01106                         // But I think this can never happen due to how inotify is written.
01107                         niceassert( (long)((char *)&event[0] +
01108                                     sizeof(struct inotify_event) +
01109                                     event[0].len) <= (long)ret,
01110                                     "extremely unlucky user, death imminent" );
01111                         // how much of the event do we have?
01112                         bytes = (char *)&event[0] + bytes - (char *)ret;
01113                         memcpy( &event[0], ret, bytes );
01114                         return inotifytools_next_events( timeout, num_events );
01115                 }
01116                 RETURN(ret);
01117 
01118         }
01119 
01120         else if ( first_byte == 0 ) {
01121                 bytes = 0;
01122         }
01123 
01124 
01125         static ssize_t this_bytes;
01126         static unsigned int bytes_to_read;
01127         static int rc;
01128         static fd_set read_fds;
01129 
01130         static struct timeval read_timeout;
01131         read_timeout.tv_sec = timeout;
01132         read_timeout.tv_usec = 0;
01133         static struct timeval * read_timeout_ptr;
01134         read_timeout_ptr = ( timeout <= 0 ? NULL : &read_timeout );
01135 
01136         FD_ZERO(&read_fds);
01137         FD_SET(inotify_fd, &read_fds);
01138         rc = select(inotify_fd + 1, &read_fds,
01139                     NULL, NULL, read_timeout_ptr);
01140         if ( rc < 0 ) {
01141                 // error
01142                 error = errno;
01143                 return NULL;
01144         }
01145         else if ( rc == 0 ) {
01146                 // timeout
01147                 return NULL;
01148         }
01149 
01150         // wait until we have enough bytes to read
01151         do {
01152                 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
01153         } while ( !rc &&
01154                   bytes_to_read < sizeof(struct inotify_event)*num_events );
01155 
01156         if ( rc == -1 ) {
01157                 error = errno;
01158                 return NULL;
01159         }
01160 
01161         this_bytes = read(inotify_fd, &event[0] + bytes,
01162                           sizeof(struct inotify_event)*MAX_EVENTS - bytes);
01163         if ( this_bytes < 0 ) {
01164                 error = errno;
01165                 return NULL;
01166         }
01167         if ( this_bytes == 0 ) {
01168                 fprintf(stderr, "Inotify reported end-of-file.  Possibly too many "
01169                                 "events occurred at once.\n");
01170                 return NULL;
01171         }
01172         bytes += this_bytes;
01173 
01174         ret = &event[0];
01175         first_byte = sizeof(struct inotify_event) + ret->len;
01176         niceassert( first_byte <= bytes, "ridiculously long filename, things will "
01177                                          "almost certainly screw up." );
01178         if ( first_byte == bytes ) {
01179                 first_byte = 0;
01180         }
01181 
01182         RETURN(ret);
01183 
01184 #undef RETURN
01185 }
01186 
01212 int inotifytools_watch_recursively( char const * path, int events ) {
01213         return inotifytools_watch_recursively_with_exclude( path, events, 0 );
01214 }
01215 
01248 int inotifytools_watch_recursively_with_exclude( char const * path, int events,
01249                                                  char const ** exclude_list ) {
01250         niceassert( init, "inotifytools_initialize not called yet" );
01251 
01252         DIR * dir;
01253         char * my_path;
01254         error = 0;
01255         dir = opendir( path );
01256         if ( !dir ) {
01257                 // If not a directory, don't need to do anything special
01258                 if ( errno == ENOTDIR ) {
01259                         return inotifytools_watch_file( path, events );
01260                 }
01261                 else {
01262                         error = errno;
01263                         return 0;
01264                 }
01265         }
01266 
01267         if ( path[strlen(path)-1] != '/' ) {
01268                 nasprintf( &my_path, "%s/", path );
01269         }
01270         else {
01271                 my_path = (char *)path;
01272         }
01273 
01274         static struct dirent * ent;
01275         char * next_file;
01276         static struct stat64 my_stat;
01277         ent = readdir( dir );
01278         // Watch each directory within this directory
01279         while ( ent ) {
01280                 if ( (0 != strcmp( ent->d_name, "." )) &&
01281                      (0 != strcmp( ent->d_name, ".." )) ) {
01282                         nasprintf(&next_file,"%s%s", my_path, ent->d_name);
01283                         if ( -1 == lstat64( next_file, &my_stat ) ) {
01284                                 error = errno;
01285                                 free( next_file );
01286                                 if ( errno != EACCES ) {
01287                                         error = errno;
01288                                         if ( my_path != path ) free( my_path );
01289                                         closedir( dir );
01290                                         return 0;
01291                                 }
01292                         }
01293                         else if ( S_ISDIR( my_stat.st_mode ) &&
01294                                   !S_ISLNK( my_stat.st_mode )) {
01295                                 free( next_file );
01296                                 nasprintf(&next_file,"%s%s/", my_path, ent->d_name);
01297                                 static unsigned int no_watch;
01298                                 static char const ** exclude_entry;
01299 
01300                                 no_watch = 0;
01301                                 for (exclude_entry = exclude_list;
01302                                          exclude_entry && *exclude_entry && !no_watch;
01303                                          ++exclude_entry) {
01304                                         static int exclude_length;
01305 
01306                                         exclude_length = strlen(*exclude_entry);
01307                                         if ((*exclude_entry)[exclude_length-1] == '/') {
01308                                                 --exclude_length;
01309                                         }
01310                                         if ( strlen(next_file) == (unsigned)(exclude_length + 1) &&
01311                                             !strncmp(*exclude_entry, next_file, exclude_length)) {
01312                                                 // directory found in exclude list
01313                                                 no_watch = 1;
01314                                         }
01315                                 }
01316                                 if (!no_watch) {
01317                                         static int status;
01318                                         status = inotifytools_watch_recursively_with_exclude(
01319                                                       next_file,
01320                                                       events,
01321                                                       exclude_list );
01322                                         // For some errors, we will continue.
01323                                         if ( !status && (EACCES != error) && (ENOENT != error) &&
01324                                              (ELOOP != error) ) {
01325                                                 free( next_file );
01326                                                 if ( my_path != path ) free( my_path );
01327                                                 closedir( dir );
01328                                                 return 0;
01329                                         }
01330                                 } // if !no_watch
01331                                 free( next_file );
01332                         } // if isdir and not islnk
01333                         else {
01334                                 free( next_file );
01335                         }
01336                 }
01337                 ent = readdir( dir );
01338                 error = 0;
01339         }
01340 
01341         if ( my_path != path ) free( my_path );
01342         closedir( dir );
01343 
01344         return inotifytools_watch_file( path, events );
01345 }
01346 
01350 void record_stats( struct inotify_event const * event ) {
01351         if (!event) return;
01352         watch *w = watch_from_wd(event->wd);
01353         if (!w) return;
01354         if ( IN_ACCESS & event->mask ) {
01355                 ++w->hit_access;
01356                 ++num_access;
01357         }
01358         if ( IN_MODIFY & event->mask ) {
01359                 ++w->hit_modify;
01360                 ++num_modify;
01361         }
01362         if ( IN_ATTRIB & event->mask ) {
01363                 ++w->hit_attrib;
01364                 ++num_attrib;
01365         }
01366         if ( IN_CLOSE_WRITE & event->mask ) {
01367                 ++w->hit_close_write;
01368                 ++num_close_write;
01369         }
01370         if ( IN_CLOSE_NOWRITE & event->mask ) {
01371                 ++w->hit_close_nowrite;
01372                 ++num_close_nowrite;
01373         }
01374         if ( IN_OPEN & event->mask ) {
01375                 ++w->hit_open;
01376                 ++num_open;
01377         }
01378         if ( IN_MOVED_FROM & event->mask ) {
01379                 ++w->hit_moved_from;
01380                 ++num_moved_from;
01381         }
01382         if ( IN_MOVED_TO & event->mask ) {
01383                 ++w->hit_moved_to;
01384                 ++num_moved_to;
01385         }
01386         if ( IN_CREATE & event->mask ) {
01387                 ++w->hit_create;
01388                 ++num_create;
01389         }
01390         if ( IN_DELETE & event->mask ) {
01391                 ++w->hit_delete;
01392                 ++num_delete;
01393         }
01394         if ( IN_DELETE_SELF & event->mask ) {
01395                 ++w->hit_delete_self;
01396                 ++num_delete_self;
01397         }
01398         if ( IN_UNMOUNT & event->mask ) {
01399                 ++w->hit_unmount;
01400                 ++num_unmount;
01401         }
01402         if ( IN_MOVE_SELF & event->mask ) {
01403                 ++w->hit_move_self;
01404                 ++num_move_self;
01405         }
01406 
01407         ++w->hit_total;
01408         ++num_total;
01409 
01410 }
01411 
01412 int *stat_ptr(watch *w, int event)
01413 {
01414         if ( IN_ACCESS == event )
01415                 return &w->hit_access;
01416         if ( IN_MODIFY == event )
01417                 return &w->hit_modify;
01418         if ( IN_ATTRIB == event )
01419                 return &w->hit_attrib;
01420         if ( IN_CLOSE_WRITE == event )
01421                 return &w->hit_close_write;
01422         if ( IN_CLOSE_NOWRITE == event )
01423                 return &w->hit_close_nowrite;
01424         if ( IN_OPEN == event )
01425                 return &w->hit_open;
01426         if ( IN_MOVED_FROM == event )
01427                 return &w->hit_moved_from;
01428         if ( IN_MOVED_TO == event )
01429                 return &w->hit_moved_to;
01430         if ( IN_CREATE == event )
01431                 return &w->hit_create;
01432         if ( IN_DELETE == event )
01433                 return &w->hit_delete;
01434         if ( IN_DELETE_SELF == event )
01435                 return &w->hit_delete_self;
01436         if ( IN_UNMOUNT == event )
01437                 return &w->hit_unmount;
01438         if ( IN_MOVE_SELF == event )
01439                 return &w->hit_move_self;
01440         if ( 0 == event )
01441                 return &w->hit_total;
01442         return 0;
01443 }
01444 
01460 int inotifytools_get_stat_by_wd( int wd, int event ) {
01461         if (!collect_stats) return -1;
01462 
01463         watch *w = watch_from_wd(wd);
01464         if (!w) return -1;
01465         int *i = stat_ptr(w, event);
01466         if (!i) return -1;
01467         return *i;
01468 }
01469 
01483 int inotifytools_get_stat_total( int event ) {
01484         if (!collect_stats) return -1;
01485         if ( IN_ACCESS == event )
01486                 return num_access;
01487         if ( IN_MODIFY == event )
01488                 return num_modify;
01489         if ( IN_ATTRIB == event )
01490                 return num_attrib;
01491         if ( IN_CLOSE_WRITE == event )
01492                 return num_close_write;
01493         if ( IN_CLOSE_NOWRITE == event )
01494                 return num_close_nowrite;
01495         if ( IN_OPEN == event )
01496                 return num_open;
01497         if ( IN_MOVED_FROM == event )
01498                 return num_moved_from;
01499         if ( IN_MOVED_TO == event )
01500                 return num_moved_to;
01501         if ( IN_CREATE == event )
01502                 return num_create;
01503         if ( IN_DELETE == event )
01504                 return num_delete;
01505         if ( IN_DELETE_SELF == event )
01506                 return num_delete_self;
01507         if ( IN_UNMOUNT == event )
01508                 return num_unmount;
01509         if ( IN_MOVE_SELF == event )
01510                 return num_move_self;
01511 
01512         if ( 0 == event )
01513                 return num_total;
01514 
01515         return -1;
01516 }
01517 
01537 int inotifytools_get_stat_by_filename( char const * filename,
01538                                                 int event ) {
01539         return inotifytools_get_stat_by_wd( inotifytools_wd_from_filename(
01540                filename ), event );
01541 }
01542 
01553 int inotifytools_error() {
01554         return error;
01555 }
01556 
01560 int isdir( char const * path ) {
01561         static struct stat64 my_stat;
01562 
01563         if ( -1 == lstat64( path, &my_stat ) ) {
01564                 if (errno == ENOENT) return 0;
01565                 fprintf(stderr, "Stat failed on %s: %s\n", path, strerror(errno));
01566                 return 0;
01567         }
01568 
01569         return S_ISDIR( my_stat.st_mode ) && !S_ISLNK( my_stat.st_mode );
01570 }
01571 
01572 
01579 int inotifytools_get_num_watches() {
01580         int ret = 0;
01581         rbwalk(tree_filename, get_num, (void*)&ret);
01582         return ret;
01583 }
01584 
01625 int inotifytools_printf( struct inotify_event* event, char* fmt ) {
01626         return inotifytools_fprintf( stdout, event, fmt );
01627 }
01628 
01670 int inotifytools_fprintf( FILE* file, struct inotify_event* event, char* fmt ) {
01671         static char out[MAX_STRLEN+1];
01672         static int ret;
01673         ret = inotifytools_sprintf( out, event, fmt );
01674         if ( -1 != ret ) fprintf( file, "%s", out );
01675         return ret;
01676 }
01677 
01728 int inotifytools_sprintf( char * out, struct inotify_event* event, char* fmt ) {
01729         return inotifytools_snprintf( out, MAX_STRLEN, event, fmt );
01730 }
01731 
01732 
01779 int inotifytools_snprintf( char * out, int size,
01780                            struct inotify_event* event, char* fmt ) {
01781         static char * filename, * eventname, * eventstr;
01782         static unsigned int i, ind;
01783         static char ch1;
01784         static char timestr[MAX_STRLEN];
01785         static time_t now;
01786 
01787 
01788         if ( event->len > 0 ) {
01789                 eventname = event->name;
01790         }
01791         else {
01792                 eventname = NULL;
01793         }
01794 
01795 
01796         filename = inotifytools_filename_from_wd( event->wd );
01797 
01798         if ( !fmt || 0 == strlen(fmt) ) {
01799                 error = EINVAL;
01800                 return -1;
01801         }
01802         if ( strlen(fmt) > MAX_STRLEN || size > MAX_STRLEN) {
01803                 error = EMSGSIZE;
01804                 return -1;
01805         }
01806 
01807         ind = 0;
01808         for ( i = 0; i < strlen(fmt) &&
01809                      (int)ind < size - 1; ++i ) {
01810                 if ( fmt[i] != '%' ) {
01811                         out[ind++] = fmt[i];
01812                         continue;
01813                 }
01814 
01815                 if ( i == strlen(fmt) - 1 ) {
01816                         // last character is %, invalid
01817                         error = EINVAL;
01818                         return ind;
01819                 }
01820 
01821                 ch1 = fmt[i+1];
01822 
01823                 if ( ch1 == '%' ) {
01824                         out[ind++] = '%';
01825                         ++i;
01826                         continue;
01827                 }
01828 
01829                 if ( ch1 == 'w' ) {
01830                         if ( filename ) {
01831                                 strncpy( &out[ind], filename, size - ind );
01832                                 ind += strlen(filename);
01833                         }
01834                         ++i;
01835                         continue;
01836                 }
01837 
01838                 if ( ch1 == 'f' ) {
01839                         if ( eventname ) {
01840                                 strncpy( &out[ind], eventname, size - ind );
01841                                 ind += strlen(eventname);
01842                         }
01843                         ++i;
01844                         continue;
01845                 }
01846 
01847                 if ( ch1 == 'e' ) {
01848                         eventstr = inotifytools_event_to_str( event->mask );
01849                         strncpy( &out[ind], eventstr, size - ind );
01850                         ind += strlen(eventstr);
01851                         ++i;
01852                         continue;
01853                 }
01854 
01855                 if ( ch1 == 'T' ) {
01856 
01857                         if ( timefmt ) {
01858 
01859                                 now = time(0);
01860                                 if ( 0 >= strftime( timestr, MAX_STRLEN-1, timefmt,
01861                                                     localtime( &now ) ) ) {
01862 
01863                                         // time format probably invalid
01864                                         error = EINVAL;
01865                                         return ind;
01866                                 }
01867                         }
01868                         else {
01869                                 timestr[0] = 0;
01870                         }
01871 
01872                         strncpy( &out[ind], timestr, size - ind );
01873                         ind += strlen(timestr);
01874                         ++i;
01875                         continue;
01876                 }
01877 
01878                 // Check if next char in fmt is e
01879                 if ( i < strlen(fmt) - 2 && fmt[i+2] == 'e' ) {
01880                         eventstr = inotifytools_event_to_str_sep( event->mask, ch1 );
01881                         strncpy( &out[ind], eventstr, size - ind );
01882                         ind += strlen(eventstr);
01883                         i += 2;
01884                         continue;
01885                 }
01886 
01887                 // OK, this wasn't a special format character, just output it as normal
01888                 if ( ind < MAX_STRLEN ) out[ind++] = '%';
01889                 if ( ind < MAX_STRLEN ) out[ind++] = ch1;
01890                 ++i;
01891         }
01892         out[ind] = 0;
01893 
01894         return ind - 1;
01895 }
01896 
01906 void inotifytools_set_printf_timefmt( char * fmt ) {
01907         timefmt = fmt;
01908 }
01909 
01918 int inotifytools_get_max_queued_events() {
01919         int ret;
01920         if ( !read_num_from_file( QUEUE_SIZE_PATH, &ret ) ) return -1;
01921         return ret;
01922 }
01923 
01933 int inotifytools_get_max_user_instances() {
01934         int ret;
01935         if ( !read_num_from_file( INSTANCES_PATH, &ret ) ) return -1;
01936         return ret;
01937 }
01938 
01948 int inotifytools_get_max_user_watches() {
01949         int ret;
01950         if ( !read_num_from_file( WATCHES_SIZE_PATH, &ret ) ) return -1;
01951         return ret;
01952 }
01953 
01965 int inotifytools_ignore_events_by_regex( char const *pattern, int flags ) {
01966         if (!pattern) {
01967                 if (regex) {
01968                         regfree(regex);
01969                         free(regex);
01970                         regex = 0;
01971                 }
01972                 return 1;
01973         }
01974 
01975         if (regex) { regfree(regex); }
01976         else       { regex = (regex_t *)malloc(sizeof(regex_t)); }
01977 
01978         int ret = regcomp(regex, pattern, flags | REG_NOSUB);
01979         if (0 == ret) return 1;
01980 
01981         regfree(regex);
01982         free(regex);
01983         regex = 0;
01984         error = EINVAL;
01985         return 0;
01986 }
01987 
01988 int event_compare(const void *p1, const void *p2, const void *config)
01989 {
01990         if (!p1 || !p2) return p1 - p2;
01991         char asc = 1;
01992         int sort_event = (int)config;
01993         if (sort_event == -1) {
01994                 sort_event = 0;
01995                 asc = 0;
01996         } else if (sort_event < 0) {
01997                 sort_event = -sort_event;
01998                 asc = 0;
01999         }
02000         int *i1 = stat_ptr((watch*)p1, sort_event);
02001         int *i2 = stat_ptr((watch*)p2, sort_event);
02002         if (0 == *i1 - *i2) {
02003                 return ((watch*)p1)->wd - ((watch*)p2)->wd;
02004         }
02005         if (asc)
02006                 return *i1 - *i2;
02007         else
02008                 return *i2 - *i1;
02009 }
02010 
02011 struct rbtree *inotifytools_wd_sorted_by_event(int sort_event)
02012 {
02013         struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
02014         RBLIST *all = rbopenlist(tree_wd);
02015         void const *p = rbreadlist(all);
02016         while (p) {
02017                 void const *r = rbsearch(p, ret);
02018                 niceassert((int)(r == p), "Couldn't insert watch into new tree");
02019                 p = rbreadlist(all);
02020         }
02021         rbcloselist(all);
02022         return ret;
02023 }

Generated on Tue Oct 16 09:56:32 2007 for libinotifytools by  doxygen 1.5.2