1 #include "watchtower.h" 2 3 #include <algorithm> 4 5 #include "log.h" 6 7 namespace { 8 bool isSymLink( const std::string& file_name ); 9 std::string directory_path( const std::string& path ); 10 }; 11 12 WatchTower::WatchTower( std::shared_ptr<WatchTowerDriver> driver ) 13 : thread_(), driver_( driver ), 14 heartBeat_(std::shared_ptr<void>((void*) 0xDEADC0DE, [] (void*) {})) 15 { 16 running_ = true; 17 thread_ = std::thread( &WatchTower::run, this ); 18 } 19 20 WatchTower::~WatchTower() 21 { 22 running_ = false; 23 thread_.join(); 24 } 25 26 WatchTower::Registration WatchTower::addFile( 27 const std::string& file_name, 28 std::function<void()> notification ) 29 { 30 LOG(logDEBUG) << "WatchTower::addFile " << file_name; 31 32 std::lock_guard<std::mutex> lock( observers_mutex_ ); 33 34 ObservedFile* existing_observed_file = 35 observed_file_list_.searchByName( file_name ); 36 37 std::shared_ptr<std::function<void()>> ptr( new std::function<void()>(std::move( notification ) ) ); 38 39 if ( ! existing_observed_file ) 40 { 41 WatchTowerDriver::SymlinkId* symlink_id = nullptr; 42 43 auto file_id = driver_->addFile( file_name ); 44 45 if ( isSymLink( file_name ) ) 46 { 47 // We want to follow the name (as opposed to the inode) 48 // so we watch the symlink as well. 49 symlink_id = driver_->addSymlink( file_name ); 50 } 51 52 auto new_file = observed_file_list_.addNewObservedFile( 53 ObservedFile( file_name, ptr, wd, symlink_wd ) ); 54 55 auto dir = observed_file_list_.watchedDirectoryForFile( file_name ); 56 if ( ! dir ) 57 { 58 LOG(logDEBUG) << "INotifyWatchTower::addFile dir for " << file_name 59 << " not watched, adding..."; 60 dir = observed_file_list_.addWatchedDirectoryForFile( file_name ); 61 62 dir->dir_wd_ = driver_->addDir( dir->path ); 63 } 64 65 new_file->dir_ = dir; 66 } 67 else 68 { 69 existing_observed_file->addCallback( ptr ); 70 } 71 72 std::weak_ptr<void> weakHeartBeat(heartBeat_); 73 74 // Returns a shared pointer that removes its own entry 75 // from the list of watched stuff when it goes out of scope! 76 // Uses a custom deleter to do the work. 77 return std::shared_ptr<void>( 0x0, [this, ptr, weakHeartBeat] (void*) { 78 if ( auto heart_beat = weakHeartBeat.lock() ) 79 removeNotification( this, ptr ); 80 } ); 81 } 82 83 // 84 // Private functions 85 // 86 87 // Called by the dtor for a registration object 88 void WatchTower::removeNotification( 89 WatchTower* watch_tower, std::shared_ptr<void> notification ) 90 { 91 LOG(logDEBUG) << "WatchTower::removeNotification"; 92 93 std::lock_guard<std::mutex> lock( watch_tower->observers_mutex_ ); 94 95 auto file = 96 watch_tower->observed_file_list_.removeCallback( notification ); 97 98 if ( file ) 99 { 100 driver_->removeFile( file->file_wd_ ); 101 driver_->removeSymlink( file->symlink_wd_ ); 102 } 103 } 104 105 // Run in its own thread 106 void INotifyWatchTower::run() 107 { 108 struct pollfd fds[1]; 109 110 fds[0].fd = inotify_fd; 111 fds[0].events = POLLIN; 112 113 while ( running_ ) 114 { 115 std::vector<ObservedFile*> files = waitAndProcessEvents( observed_file_list_, observers_mutex_ ); 116 117 } 118 } 119 120 namespace { 121 bool isSymLink( const std::string& file_name ) 122 { 123 struct stat buf; 124 125 lstat( file_name.c_str(), &buf ); 126 return ( S_ISLNK(buf.st_mode) ); 127 } 128 }; 129