1c540156cSNicolas Bonnefon #ifndef WATCHTOWERLIST_H 2c540156cSNicolas Bonnefon #define WATCHTOWERLIST_H 3c540156cSNicolas Bonnefon 4c540156cSNicolas Bonnefon // Utility classes for the WatchTower implementations 5c540156cSNicolas Bonnefon 6c540156cSNicolas Bonnefon #include <functional> 7c540156cSNicolas Bonnefon #include <string> 8c540156cSNicolas Bonnefon #include <vector> 9c540156cSNicolas Bonnefon #include <map> 10c540156cSNicolas Bonnefon #include <list> 11c540156cSNicolas Bonnefon #include <memory> 12c540156cSNicolas Bonnefon #include <algorithm> 13fcaa7557SNicolas Bonnefon #include <chrono> 14c540156cSNicolas Bonnefon 15f09fa651SNicolas Bonnefon #include "log.h" 16b278d183SNicolas Bonnefon 17c540156cSNicolas Bonnefon // Utility classes 18c540156cSNicolas Bonnefon struct ProtocolInfo { 19c540156cSNicolas Bonnefon // Win32 notification variables 20c540156cSNicolas Bonnefon static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 21c540156cSNicolas Bonnefon 22c540156cSNicolas Bonnefon void* handle_; 23c540156cSNicolas Bonnefon static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 24c540156cSNicolas Bonnefon char buffer_[buffer_length_]; 25c540156cSNicolas Bonnefon }; 26c540156cSNicolas Bonnefon 27c540156cSNicolas Bonnefon // List of files and observers 28f09fa651SNicolas Bonnefon template <typename Driver> 29c540156cSNicolas Bonnefon struct ObservedDir { 30c540156cSNicolas Bonnefon ObservedDir( const std::string this_path ) : path { this_path } {} 31c540156cSNicolas Bonnefon 32c540156cSNicolas Bonnefon // Returns the address of the protocol specific informations 33c540156cSNicolas Bonnefon ProtocolInfo* protocolInfo() { return &protocol_info_; } 34c540156cSNicolas Bonnefon 35c540156cSNicolas Bonnefon std::string path; 36f09fa651SNicolas Bonnefon typename Driver::DirId dir_id_; 37c540156cSNicolas Bonnefon // Contains data specific to the protocol (inotify/Win32...) 38c540156cSNicolas Bonnefon ProtocolInfo protocol_info_; 39c540156cSNicolas Bonnefon }; 40c540156cSNicolas Bonnefon 41f09fa651SNicolas Bonnefon template <typename Driver> 42c540156cSNicolas Bonnefon struct ObservedFile { 43c540156cSNicolas Bonnefon ObservedFile( 44c540156cSNicolas Bonnefon const std::string& file_name, 45c540156cSNicolas Bonnefon std::shared_ptr<void> callback, 46f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 47f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ) 48b278d183SNicolas Bonnefon : file_name_( file_name ) { 49c540156cSNicolas Bonnefon addCallback( callback ); 50c540156cSNicolas Bonnefon 51b278d183SNicolas Bonnefon file_id_ = file_id; 52b278d183SNicolas Bonnefon symlink_id_ = symlink_id; 53c540156cSNicolas Bonnefon dir_ = nullptr; 54fcaa7557SNicolas Bonnefon 55fcaa7557SNicolas Bonnefon markAsChanged(); 56c540156cSNicolas Bonnefon } 57c540156cSNicolas Bonnefon 58c540156cSNicolas Bonnefon void addCallback( std::shared_ptr<void> callback ) { 59c540156cSNicolas Bonnefon callbacks.push_back( callback ); 60c540156cSNicolas Bonnefon } 61c540156cSNicolas Bonnefon 62fcaa7557SNicolas Bonnefon // Records the file has changed 63fcaa7557SNicolas Bonnefon void markAsChanged() { 64fcaa7557SNicolas Bonnefon change_token_.readFromFile( file_name_ ); 65fcaa7557SNicolas Bonnefon last_check_time_ = std::chrono::steady_clock::now(); 66fcaa7557SNicolas Bonnefon } 67fcaa7557SNicolas Bonnefon 68fcaa7557SNicolas Bonnefon // Returns whether a file has changed 69fcaa7557SNicolas Bonnefon // (for polling) 70fcaa7557SNicolas Bonnefon bool hasChanged() { 71fcaa7557SNicolas Bonnefon typename Driver::FileChangeToken new_token( file_name_ ); 72fcaa7557SNicolas Bonnefon last_check_time_ = std::chrono::steady_clock::now(); 73fcaa7557SNicolas Bonnefon return change_token_ != new_token; 74fcaa7557SNicolas Bonnefon } 75fcaa7557SNicolas Bonnefon 76fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point timeForLastCheck() { 77fcaa7557SNicolas Bonnefon return last_check_time_; 78fcaa7557SNicolas Bonnefon } 79fcaa7557SNicolas Bonnefon 80c540156cSNicolas Bonnefon std::string file_name_; 81c540156cSNicolas Bonnefon // List of callbacks for this file 82c540156cSNicolas Bonnefon std::vector<std::shared_ptr<void>> callbacks; 83c540156cSNicolas Bonnefon 84c540156cSNicolas Bonnefon // watch descriptor for the file itself 85f09fa651SNicolas Bonnefon typename Driver::FileId file_id_; 86c540156cSNicolas Bonnefon // watch descriptor for the symlink (if file is a symlink) 87f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id_; 88c540156cSNicolas Bonnefon 89c540156cSNicolas Bonnefon // link to the dir containing the file 90f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir_; 91fcaa7557SNicolas Bonnefon 92fcaa7557SNicolas Bonnefon // token to identify modification 93fcaa7557SNicolas Bonnefon // (the token change when the file is modified, this is used when polling) 94fcaa7557SNicolas Bonnefon typename Driver::FileChangeToken change_token_; 95fcaa7557SNicolas Bonnefon 96fcaa7557SNicolas Bonnefon // Last time a check has been done 97fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point last_check_time_ = 98fcaa7557SNicolas Bonnefon std::chrono::steady_clock::time_point::min(); 99c540156cSNicolas Bonnefon }; 100c540156cSNicolas Bonnefon 101c540156cSNicolas Bonnefon // A list of the observed files and directories 102c540156cSNicolas Bonnefon // This class is not thread safe 103f09fa651SNicolas Bonnefon template<typename Driver> 104c540156cSNicolas Bonnefon class ObservedFileList { 105c540156cSNicolas Bonnefon public: 1063104b268SNicolas Bonnefon ObservedFileList() : 1073104b268SNicolas Bonnefon heartBeat_ { std::shared_ptr<void>((void*) 0xDEADC0DE, [] (void*) {}) } 1083104b268SNicolas Bonnefon { } 109c540156cSNicolas Bonnefon ~ObservedFileList() = default; 110c540156cSNicolas Bonnefon 1113104b268SNicolas Bonnefon // The functions return a pointer to the existing file (if exists) 1123104b268SNicolas Bonnefon // but keep ownership of the object. 113f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByName( const std::string& file_name ); 114f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByFileOrSymlinkWd( 115f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 116f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ); 117f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByDirWdAndName( 118f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ); 119*933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> searchByDirWd( 120*933a5744SNicolas Bonnefon typename Driver::DirId id ); 121c540156cSNicolas Bonnefon 1223104b268SNicolas Bonnefon // Add a new file, the list returns a pointer to the added file, 1233104b268SNicolas Bonnefon // but has ownership of the file. 124f09fa651SNicolas Bonnefon ObservedFile<Driver>* addNewObservedFile( ObservedFile<Driver> new_observed ); 125c540156cSNicolas Bonnefon // Remove a callback, remove and returns the file object if 126c540156cSNicolas Bonnefon // it was the last callback on this object, nullptr if not. 127c540156cSNicolas Bonnefon // The caller has ownership of the object. 128f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> removeCallback( 129c540156cSNicolas Bonnefon std::shared_ptr<void> callback ); 130c540156cSNicolas Bonnefon 131c540156cSNicolas Bonnefon // Return the watched directory if it is watched, or nullptr 132f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectory( const std::string& dir_name ); 1333104b268SNicolas Bonnefon // Create a new watched directory for dir_name, the client is passed 1343104b268SNicolas Bonnefon // shared ownership and have to keep the shared_ptr (the list only 1353104b268SNicolas Bonnefon // maintain a weak link). 1363104b268SNicolas Bonnefon // The remove notification is called just before the reference to 1373104b268SNicolas Bonnefon // the directory is destroyed. 1383104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectory( 1393104b268SNicolas Bonnefon const std::string& dir_name, 1403104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 141c540156cSNicolas Bonnefon 1423104b268SNicolas Bonnefon // Similar to previous functions but extract the name of the 1433104b268SNicolas Bonnefon // directory from the file name. 144f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectoryForFile( const std::string& file_name ); 1453104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectoryForFile( const std::string& file_name, 1463104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 1473104b268SNicolas Bonnefon 1483104b268SNicolas Bonnefon // Removal of directories is done when there is no shared reference 1493104b268SNicolas Bonnefon // left (RAII) 1503104b268SNicolas Bonnefon 1513104b268SNicolas Bonnefon // Number of watched directories (for tests) 1523104b268SNicolas Bonnefon unsigned int numberWatchedDirectories() const; 153c540156cSNicolas Bonnefon 154fcaa7557SNicolas Bonnefon // Iterator 155fcaa7557SNicolas Bonnefon template<typename Container> 156fcaa7557SNicolas Bonnefon class iterator : std::iterator<std::input_iterator_tag, ObservedFile<Driver>> { 157fcaa7557SNicolas Bonnefon public: 158fcaa7557SNicolas Bonnefon iterator( Container* list, 159fcaa7557SNicolas Bonnefon const typename Container::iterator& iter ) 160fcaa7557SNicolas Bonnefon { list_ = list; pos_ = iter; } 161fcaa7557SNicolas Bonnefon 162fcaa7557SNicolas Bonnefon iterator operator++() 163fcaa7557SNicolas Bonnefon { ++pos_; return *this; } 164fcaa7557SNicolas Bonnefon 165fcaa7557SNicolas Bonnefon bool operator==( const iterator& other ) 166fcaa7557SNicolas Bonnefon { return ( pos_ == other.pos_ ); } 167fcaa7557SNicolas Bonnefon 168fcaa7557SNicolas Bonnefon bool operator!=( const iterator& other ) 169fcaa7557SNicolas Bonnefon { return ! operator==( other ); } 170fcaa7557SNicolas Bonnefon 171fcaa7557SNicolas Bonnefon typename Container::iterator operator*() 172fcaa7557SNicolas Bonnefon { return pos_; } 173fcaa7557SNicolas Bonnefon 174fcaa7557SNicolas Bonnefon private: 175fcaa7557SNicolas Bonnefon Container* list_; 176fcaa7557SNicolas Bonnefon typename Container::iterator pos_; 177fcaa7557SNicolas Bonnefon }; 178fcaa7557SNicolas Bonnefon 179fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> begin() 180fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.begin() ); } 181fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> end() 182fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.end() ); } 183fcaa7557SNicolas Bonnefon 184c540156cSNicolas Bonnefon private: 185c540156cSNicolas Bonnefon // List of observed files 186f09fa651SNicolas Bonnefon std::list<ObservedFile<Driver>> observed_files_; 187c540156cSNicolas Bonnefon 188c540156cSNicolas Bonnefon // List of observed dirs, key-ed by name 189f09fa651SNicolas Bonnefon std::map<std::string, std::weak_ptr<ObservedDir<Driver>>> observed_dirs_; 190c540156cSNicolas Bonnefon 191c540156cSNicolas Bonnefon // Map the inotify file (including symlinks) wds to the observed file 192f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_file_wd_; 193c540156cSNicolas Bonnefon // Map the inotify directory wds to the observed files 194f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_dir_wd_; 1953104b268SNicolas Bonnefon 1963104b268SNicolas Bonnefon // Heartbeat 1973104b268SNicolas Bonnefon std::shared_ptr<void> heartBeat_; 1983104b268SNicolas Bonnefon 1993104b268SNicolas Bonnefon // Clean all reference to any expired directory 2003104b268SNicolas Bonnefon void cleanRefsToExpiredDirs(); 201c540156cSNicolas Bonnefon }; 202c540156cSNicolas Bonnefon 203f09fa651SNicolas Bonnefon namespace { 204f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ); 205f09fa651SNicolas Bonnefon }; 206f09fa651SNicolas Bonnefon 207f09fa651SNicolas Bonnefon // ObservedFileList class 208f09fa651SNicolas Bonnefon template <typename Driver> 209f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByName( 210f09fa651SNicolas Bonnefon const std::string& file_name ) 211f09fa651SNicolas Bonnefon { 212f09fa651SNicolas Bonnefon // Look for an existing observer on this file 213f09fa651SNicolas Bonnefon auto existing_observer = observed_files_.begin(); 214f09fa651SNicolas Bonnefon for ( ; existing_observer != observed_files_.end(); ++existing_observer ) 215f09fa651SNicolas Bonnefon { 216f09fa651SNicolas Bonnefon if ( existing_observer->file_name_ == file_name ) 217f09fa651SNicolas Bonnefon { 218f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Found " << file_name; 219f09fa651SNicolas Bonnefon break; 220f09fa651SNicolas Bonnefon } 221f09fa651SNicolas Bonnefon } 222f09fa651SNicolas Bonnefon 223f09fa651SNicolas Bonnefon if ( existing_observer != observed_files_.end() ) 224f09fa651SNicolas Bonnefon return &( *existing_observer ); 225f09fa651SNicolas Bonnefon else 226f09fa651SNicolas Bonnefon return nullptr; 227f09fa651SNicolas Bonnefon } 228f09fa651SNicolas Bonnefon 229f09fa651SNicolas Bonnefon template <typename Driver> 230f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByFileOrSymlinkWd( 231f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 232f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ) 233f09fa651SNicolas Bonnefon { 234f09fa651SNicolas Bonnefon auto result = find_if( observed_files_.begin(), observed_files_.end(), 235f09fa651SNicolas Bonnefon [file_id, symlink_id] (ObservedFile<Driver> file) -> bool { 236f09fa651SNicolas Bonnefon return ( file_id == file.file_id_ ) || 237f09fa651SNicolas Bonnefon ( symlink_id == file.symlink_id_ ); 238f09fa651SNicolas Bonnefon } ); 239f09fa651SNicolas Bonnefon 240f09fa651SNicolas Bonnefon if ( result != observed_files_.end() ) 241f09fa651SNicolas Bonnefon return &( *result ); 242f09fa651SNicolas Bonnefon else 243f09fa651SNicolas Bonnefon return nullptr; 244f09fa651SNicolas Bonnefon } 245f09fa651SNicolas Bonnefon 246f09fa651SNicolas Bonnefon template <typename Driver> 247f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByDirWdAndName( 248f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ) 249f09fa651SNicolas Bonnefon { 250f09fa651SNicolas Bonnefon auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(), 251f09fa651SNicolas Bonnefon [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool { 252f09fa651SNicolas Bonnefon if ( auto dir = d.second.lock() ) { 253f09fa651SNicolas Bonnefon return ( id == dir->dir_id_ ); 254f09fa651SNicolas Bonnefon } 255f09fa651SNicolas Bonnefon else { 256f09fa651SNicolas Bonnefon return false; } } ); 257f09fa651SNicolas Bonnefon 258f09fa651SNicolas Bonnefon if ( dir != observed_dirs_.end() ) { 259f09fa651SNicolas Bonnefon std::string path = dir->first + "/" + name; 260f09fa651SNicolas Bonnefon 261f09fa651SNicolas Bonnefon // LOG(logDEBUG) << "Testing path: " << path; 262f09fa651SNicolas Bonnefon 263f09fa651SNicolas Bonnefon // Looking for the path in the files we are watching 264f09fa651SNicolas Bonnefon return searchByName( path ); 265f09fa651SNicolas Bonnefon } 266f09fa651SNicolas Bonnefon else { 267f09fa651SNicolas Bonnefon return nullptr; 268f09fa651SNicolas Bonnefon } 269f09fa651SNicolas Bonnefon } 270f09fa651SNicolas Bonnefon 271f09fa651SNicolas Bonnefon template <typename Driver> 272*933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> ObservedFileList<Driver>::searchByDirWd( 273*933a5744SNicolas Bonnefon typename Driver::DirId id ) 274*933a5744SNicolas Bonnefon { 275*933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> result; 276*933a5744SNicolas Bonnefon 277*933a5744SNicolas Bonnefon auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(), 278*933a5744SNicolas Bonnefon [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool { 279*933a5744SNicolas Bonnefon if ( auto dir = d.second.lock() ) { 280*933a5744SNicolas Bonnefon return ( id == dir->dir_id_ ); 281*933a5744SNicolas Bonnefon } 282*933a5744SNicolas Bonnefon else { 283*933a5744SNicolas Bonnefon return false; } } ); 284*933a5744SNicolas Bonnefon 285*933a5744SNicolas Bonnefon if ( dir != observed_dirs_.end() ) { 286*933a5744SNicolas Bonnefon for ( auto i = observed_files_.begin(); i != observed_files_.end(); ++i ) { 287*933a5744SNicolas Bonnefon if ( auto d = dir->second.lock() ) { 288*933a5744SNicolas Bonnefon if ( d.get() == i->dir_.get() ) { 289*933a5744SNicolas Bonnefon result.push_back( &(*i) ); 290*933a5744SNicolas Bonnefon } 291*933a5744SNicolas Bonnefon } 292*933a5744SNicolas Bonnefon } 293*933a5744SNicolas Bonnefon } 294*933a5744SNicolas Bonnefon 295*933a5744SNicolas Bonnefon return result; 296*933a5744SNicolas Bonnefon } 297*933a5744SNicolas Bonnefon 298*933a5744SNicolas Bonnefon template <typename Driver> 299f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::addNewObservedFile( 300f09fa651SNicolas Bonnefon ObservedFile<Driver> new_observed ) 301f09fa651SNicolas Bonnefon { 302f09fa651SNicolas Bonnefon auto new_file = observed_files_.insert( std::begin( observed_files_ ), new_observed ); 303f09fa651SNicolas Bonnefon 304f09fa651SNicolas Bonnefon return &( *new_file ); 305f09fa651SNicolas Bonnefon } 306f09fa651SNicolas Bonnefon 307f09fa651SNicolas Bonnefon template <typename Driver> 308f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> ObservedFileList<Driver>::removeCallback( 309f09fa651SNicolas Bonnefon std::shared_ptr<void> callback ) 310f09fa651SNicolas Bonnefon { 311f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> returned_file = nullptr; 312f09fa651SNicolas Bonnefon 313fcaa7557SNicolas Bonnefon for ( auto observer = std::begin( observed_files_ ); 314fcaa7557SNicolas Bonnefon observer != std::end( observed_files_ ); ) 315f09fa651SNicolas Bonnefon { 316f09fa651SNicolas Bonnefon std::vector<std::shared_ptr<void>>& callbacks = observer->callbacks; 317f09fa651SNicolas Bonnefon callbacks.erase( std::remove( 318f09fa651SNicolas Bonnefon std::begin( callbacks ), std::end( callbacks ), callback ), 319f09fa651SNicolas Bonnefon std::end( callbacks ) ); 320f09fa651SNicolas Bonnefon 321f09fa651SNicolas Bonnefon /* See if all notifications have been deleted for this file */ 322f09fa651SNicolas Bonnefon if ( callbacks.empty() ) { 3233104b268SNicolas Bonnefon LOG(logDEBUG) << "Empty notification list for " << observer->file_name_ 3243104b268SNicolas Bonnefon << ", removing the watched file"; 325f09fa651SNicolas Bonnefon returned_file = std::make_shared<ObservedFile<Driver>>( *observer ); 326f09fa651SNicolas Bonnefon observer = observed_files_.erase( observer ); 327f09fa651SNicolas Bonnefon } 328f09fa651SNicolas Bonnefon else { 329f09fa651SNicolas Bonnefon ++observer; 330f09fa651SNicolas Bonnefon } 331f09fa651SNicolas Bonnefon } 332f09fa651SNicolas Bonnefon 333f09fa651SNicolas Bonnefon return returned_file; 334f09fa651SNicolas Bonnefon } 335f09fa651SNicolas Bonnefon 336f09fa651SNicolas Bonnefon template <typename Driver> 337f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectory( 338f09fa651SNicolas Bonnefon const std::string& dir_name ) 339f09fa651SNicolas Bonnefon { 340f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = nullptr; 341f09fa651SNicolas Bonnefon 342f09fa651SNicolas Bonnefon if ( observed_dirs_.find( dir_name ) != std::end( observed_dirs_ ) ) 343f09fa651SNicolas Bonnefon dir = observed_dirs_[ dir_name ].lock(); 344f09fa651SNicolas Bonnefon 345f09fa651SNicolas Bonnefon return dir; 346f09fa651SNicolas Bonnefon } 347f09fa651SNicolas Bonnefon 348f09fa651SNicolas Bonnefon template <typename Driver> 349f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectory( 3503104b268SNicolas Bonnefon const std::string& dir_name, 3513104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 352f09fa651SNicolas Bonnefon { 3533104b268SNicolas Bonnefon std::weak_ptr<void> weakHeartBeat(heartBeat_); 3543104b268SNicolas Bonnefon 3553104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = { 3563104b268SNicolas Bonnefon new ObservedDir<Driver>( dir_name ), 3573104b268SNicolas Bonnefon [this, remove_notification, weakHeartBeat] (ObservedDir<Driver>* d) { 3583104b268SNicolas Bonnefon if ( auto heart_beat = weakHeartBeat.lock() ) { 3593104b268SNicolas Bonnefon remove_notification( d ); 360dfb2a39cSNicolas Bonnefon this->cleanRefsToExpiredDirs(); 3613104b268SNicolas Bonnefon } 3623104b268SNicolas Bonnefon delete d; } }; 363f09fa651SNicolas Bonnefon 364f09fa651SNicolas Bonnefon observed_dirs_[ dir_name ] = std::weak_ptr<ObservedDir<Driver>>( dir ); 365f09fa651SNicolas Bonnefon 366f09fa651SNicolas Bonnefon return dir; 367f09fa651SNicolas Bonnefon } 368f09fa651SNicolas Bonnefon 369f09fa651SNicolas Bonnefon template <typename Driver> 370f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectoryForFile( 371f09fa651SNicolas Bonnefon const std::string& file_name ) 372f09fa651SNicolas Bonnefon { 373f09fa651SNicolas Bonnefon return watchedDirectory( directory_path( file_name ) ); 374f09fa651SNicolas Bonnefon } 375f09fa651SNicolas Bonnefon 376f09fa651SNicolas Bonnefon template <typename Driver> 377f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectoryForFile( 3783104b268SNicolas Bonnefon const std::string& file_name, 3793104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 380f09fa651SNicolas Bonnefon { 3813104b268SNicolas Bonnefon return addWatchedDirectory( directory_path( file_name ), 3823104b268SNicolas Bonnefon remove_notification ); 3833104b268SNicolas Bonnefon } 3843104b268SNicolas Bonnefon 3853104b268SNicolas Bonnefon template <typename Driver> 3863104b268SNicolas Bonnefon unsigned int ObservedFileList<Driver>::numberWatchedDirectories() const 3873104b268SNicolas Bonnefon { 3883104b268SNicolas Bonnefon return observed_dirs_.size(); 3893104b268SNicolas Bonnefon } 3903104b268SNicolas Bonnefon 3913104b268SNicolas Bonnefon // Private functions 3923104b268SNicolas Bonnefon template <typename Driver> 3933104b268SNicolas Bonnefon void ObservedFileList<Driver>::cleanRefsToExpiredDirs() 3943104b268SNicolas Bonnefon { 3953104b268SNicolas Bonnefon for ( auto it = std::begin( observed_dirs_ ); 3963104b268SNicolas Bonnefon it != std::end( observed_dirs_ ); ) 3973104b268SNicolas Bonnefon { 3983104b268SNicolas Bonnefon if ( it->second.expired() ) { 3993104b268SNicolas Bonnefon it = observed_dirs_.erase( it ); 4003104b268SNicolas Bonnefon } 4013104b268SNicolas Bonnefon else { 4023104b268SNicolas Bonnefon ++it; 4033104b268SNicolas Bonnefon } 4043104b268SNicolas Bonnefon } 405f09fa651SNicolas Bonnefon } 406f09fa651SNicolas Bonnefon 407f09fa651SNicolas Bonnefon namespace { 408f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ) 409f09fa651SNicolas Bonnefon { 410f09fa651SNicolas Bonnefon size_t slash_pos = path.rfind( '/' ); 411f09fa651SNicolas Bonnefon 412f09fa651SNicolas Bonnefon #ifdef _WIN32 413f09fa651SNicolas Bonnefon if ( slash_pos == std::string::npos ) { 414f09fa651SNicolas Bonnefon slash_pos = path.rfind( '\\' ); 415f09fa651SNicolas Bonnefon } 416f09fa651SNicolas Bonnefon 417f09fa651SNicolas Bonnefon // We need to include the final slash on Windows 418f09fa651SNicolas Bonnefon ++slash_pos; 419f09fa651SNicolas Bonnefon #endif 420f09fa651SNicolas Bonnefon 421f09fa651SNicolas Bonnefon return std::string( path, 0, slash_pos ); 422f09fa651SNicolas Bonnefon } 423f09fa651SNicolas Bonnefon }; 424c540156cSNicolas Bonnefon #endif 425