#include "externals.hh" #include "filewatcher.hh" #include "utilities.hh" /*= T_FilesWatcher ===========================================================*/ T_FilesWatcher::T_FilesWatcher( ) : fd( inotify_init1( O_NONBLOCK ) ) { } T_FilesWatcher::T_FilesWatcher( T_FilesWatcher&& other ) noexcept : fd( 0 ) , watched_( std::move( other.watched_ ) ) { std::swap( fd , other.fd ); other.watched_.clear( ); for ( T_WatchedFiles* wf : watched_ ) { if ( wf ) { wf->watcher = this; } } } T_FilesWatcher::~T_FilesWatcher( ) { if ( fd ) { close( fd ); } for ( T_WatchedFiles* wf : watched_ ) { if ( wf ) { wf->watcher = nullptr; } } } void T_FilesWatcher::check( ) { // Reset triggered status for ( T_WatchedFiles* wf : watched_ ) { wf->triggered = false; } // Handle events from inotify inotify_event ie; while ( read( fd , &ie , sizeof( ie ) ) == sizeof( ie ) ) { printf( "inotify wd %d / evt %x\n" , ie.wd , ie.mask ); fflush( stdout ); if ( ( ie.mask & ( IN_CLOSE_WRITE | IN_DELETE_SELF ) ) == 0 ) { printf( " -> skipping this event\n" ); continue; } const auto pos( inotify_.find( ie.wd ) ); if ( pos == inotify_.end( ) ) { printf( " -> no matching file\n" ); fflush( stdout ); continue; } printf( " -> file '%s'\n" , pos->second.c_str( ) ); auto& entry( fileWatchers_.find( pos->second )->second ); entry.trigger( ); if ( ( ie.mask & IN_DELETE_SELF ) != 0 ) { printf( " -> deleted, added as missing\n" ); fflush( stdout ); inotify_rm_watch( fd , entry.wd ); missing_.push_back( pos->second ); entry.wd = -2; inotify_.erase( entry.wd ); } } // Handle missing/deleted files for ( auto it = missing_.begin( ) ; it < missing_.end( ) ; ++it ) { auto const& path( *it ); //printf( "Checking missing file '%s'\n" , path.c_str( ) ); fflush( stdout ); const auto p( fileWatchers_.find( path ) ); //printf( " -> found? %c\n" , p != fileWatchers_.end( ) ? 'Y' : 'N' ); assert( p != fileWatchers_.end( ) ); //printf( " -> wd = %d\n" , p->second.wd ); assert( p->second.wd < 0 ); auto& we( p->second ); const auto nwd( inotify_add_watch( fd , path.c_str( ) , IN_CLOSE_WRITE | IN_DELETE_SELF ) ); //printf( " -> inotify add watch %d\n" , nwd ); fflush( stdout ); if ( nwd < 0 ) { //printf( " -> still missing\n" ); fflush( stdout ); we.wd = -1; continue; } // Trigger if this was really a missing file if ( we.wd == -1 ) { //printf( " -> triggering\n" ); fflush( stdout ); we.trigger( ); } // Update cache we.wd = nwd; missing_.erase( it ); inotify_.emplace( nwd , path ); } } /*----------------------------------------------------------------------------*/ void T_FilesWatcher::T_File_::trigger( ) { for ( T_WatchedFiles* const wf : watchers ) { if ( !wf->triggered ) { wf->triggered = true; wf->callback( ); } } } void T_FilesWatcher::watchFile( __rd__ std::string const& path , __rd__ T_WatchedFiles* const watcher ) { //printf( "creating watch on '%s' for watcher %p\n" , path.c_str( ) , watcher ); fflush( stdout ); auto const exFilePos( fileWatchers_.find( path ) ); if ( exFilePos != fileWatchers_.end( ) ) { //printf( " -> existing\n" ); fflush( stdout ); auto& wl( exFilePos->second.watchers ); if ( !Contains( wl , watcher ) ) { wl.push_back( watcher ); } } else { T_File_ f; f.wd = inotify_add_watch( fd , path.c_str( ) , IN_CLOSE_WRITE | IN_DELETE_SELF ); //printf( " -> inotify wd %d\n" , f.wd ); fflush( stdout ); if ( f.wd == -1 ) { missing_.push_back( path ); //printf( " -> missing\n" ); fflush( stdout ); } else { inotify_.emplace( f.wd , path ); //printf( " -> found\n" ); fflush( stdout ); } f.watchers.push_back( watcher ); fileWatchers_.emplace( path , std::move( f ) ); } } void T_FilesWatcher::unwatchAll( __rd__ T_WatchedFiles* const watcher ) { for ( auto& entry : fileWatchers_ ) { auto& wl( entry.second.watchers ); const auto pos( find( wl , watcher ) ); if ( pos == wl.end( ) ) { continue; } wl.erase( pos ); if ( wl.empty( ) ) { const auto wd( entry.second.wd ); if ( wd < 0 ) { auto const pos( find( missing_ , entry.first ) ); assert( pos != missing_.end( ) ); missing_.erase( pos ); } else { inotify_rm_watch( fd , wd ); inotify_.erase( wd ); } fileWatchers_.erase( entry.first ); } } } /*= T_WatchedFiles ===========================================================*/ T_WatchedFiles::T_WatchedFiles( T_WatchedFiles&& other ) noexcept : watcher( other.watcher ) , callback( other.callback ) , triggered( other.triggered ) { if ( watcher ) { other.watcher = nullptr; *( find( watcher->watched_ , &other ) ) = this; } } T_WatchedFiles::T_WatchedFiles( __rw__ T_FilesWatcher& watcher , __rd__ const F_OnFileChanges callback ) : watcher( &watcher ) , callback( callback ) , triggered( false ) { watcher.watched_.push_back( this ); } T_WatchedFiles::~T_WatchedFiles( ) { if ( watcher ) { watcher->unwatchAll( this ); watcher->watched_.erase( find( watcher->watched_ , this ) ); } } void T_WatchedFiles::clear( ) { if ( watcher ) { watcher->unwatchAll( this ); } } bool T_WatchedFiles::watch( __rd__ std::string const& file ) { if ( !watcher ) { return false; } watcher->watchFile( file , this ); return true; }