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() : 107*5751f354SAlexander Hirsch // Use an empty deleter since we don't really own this. 108*5751f354SAlexander Hirsch heartBeat_ { this, [] (void*) {} } 1093104b268SNicolas Bonnefon { } 110*5751f354SAlexander Hirsch ObservedFileList( const ObservedFileList& ) = delete; 111c540156cSNicolas Bonnefon ~ObservedFileList() = default; 112c540156cSNicolas Bonnefon 1133104b268SNicolas Bonnefon // The functions return a pointer to the existing file (if exists) 1143104b268SNicolas Bonnefon // but keep ownership of the object. 115f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByName( const std::string& file_name ); 116f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByFileOrSymlinkWd( 117f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 118f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ); 119f09fa651SNicolas Bonnefon ObservedFile<Driver>* searchByDirWdAndName( 120f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ); 121933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> searchByDirWd( 122933a5744SNicolas Bonnefon typename Driver::DirId id ); 123c540156cSNicolas Bonnefon 1243104b268SNicolas Bonnefon // Add a new file, the list returns a pointer to the added file, 1253104b268SNicolas Bonnefon // but has ownership of the file. 126f09fa651SNicolas Bonnefon ObservedFile<Driver>* addNewObservedFile( ObservedFile<Driver> new_observed ); 127c540156cSNicolas Bonnefon // Remove a callback, remove and returns the file object if 128c540156cSNicolas Bonnefon // it was the last callback on this object, nullptr if not. 129c540156cSNicolas Bonnefon // The caller has ownership of the object. 130f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> removeCallback( 131c540156cSNicolas Bonnefon std::shared_ptr<void> callback ); 132c540156cSNicolas Bonnefon 133c540156cSNicolas Bonnefon // Return the watched directory if it is watched, or nullptr 134f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectory( const std::string& dir_name ); 1353104b268SNicolas Bonnefon // Create a new watched directory for dir_name, the client is passed 1363104b268SNicolas Bonnefon // shared ownership and have to keep the shared_ptr (the list only 1373104b268SNicolas Bonnefon // maintain a weak link). 1383104b268SNicolas Bonnefon // The remove notification is called just before the reference to 1393104b268SNicolas Bonnefon // the directory is destroyed. 1403104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectory( 1413104b268SNicolas Bonnefon const std::string& dir_name, 1423104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 143c540156cSNicolas Bonnefon 1443104b268SNicolas Bonnefon // Similar to previous functions but extract the name of the 1453104b268SNicolas Bonnefon // directory from the file name. 146f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> watchedDirectoryForFile( const std::string& file_name ); 1473104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> addWatchedDirectoryForFile( const std::string& file_name, 1483104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ); 1493104b268SNicolas Bonnefon 1503104b268SNicolas Bonnefon // Removal of directories is done when there is no shared reference 1513104b268SNicolas Bonnefon // left (RAII) 1523104b268SNicolas Bonnefon 1533104b268SNicolas Bonnefon // Number of watched directories (for tests) 1543104b268SNicolas Bonnefon unsigned int numberWatchedDirectories() const; 155c540156cSNicolas Bonnefon 156fcaa7557SNicolas Bonnefon // Iterator 157fcaa7557SNicolas Bonnefon template<typename Container> 158fcaa7557SNicolas Bonnefon class iterator : std::iterator<std::input_iterator_tag, ObservedFile<Driver>> { 159fcaa7557SNicolas Bonnefon public: 160fcaa7557SNicolas Bonnefon iterator( Container* list, 161fcaa7557SNicolas Bonnefon const typename Container::iterator& iter ) 162fcaa7557SNicolas Bonnefon { list_ = list; pos_ = iter; } 163fcaa7557SNicolas Bonnefon 164fcaa7557SNicolas Bonnefon iterator operator++() 165fcaa7557SNicolas Bonnefon { ++pos_; return *this; } 166fcaa7557SNicolas Bonnefon 167fcaa7557SNicolas Bonnefon bool operator==( const iterator& other ) 168fcaa7557SNicolas Bonnefon { return ( pos_ == other.pos_ ); } 169fcaa7557SNicolas Bonnefon 170fcaa7557SNicolas Bonnefon bool operator!=( const iterator& other ) 171fcaa7557SNicolas Bonnefon { return ! operator==( other ); } 172fcaa7557SNicolas Bonnefon 173fcaa7557SNicolas Bonnefon typename Container::iterator operator*() 174fcaa7557SNicolas Bonnefon { return pos_; } 175fcaa7557SNicolas Bonnefon 176fcaa7557SNicolas Bonnefon private: 177fcaa7557SNicolas Bonnefon Container* list_; 178fcaa7557SNicolas Bonnefon typename Container::iterator pos_; 179fcaa7557SNicolas Bonnefon }; 180fcaa7557SNicolas Bonnefon 181fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> begin() 182fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.begin() ); } 183fcaa7557SNicolas Bonnefon iterator<std::list<ObservedFile<Driver>>> end() 184fcaa7557SNicolas Bonnefon { return iterator<std::list<ObservedFile<Driver>>>( &observed_files_, observed_files_.end() ); } 185fcaa7557SNicolas Bonnefon 186c540156cSNicolas Bonnefon private: 187c540156cSNicolas Bonnefon // List of observed files 188f09fa651SNicolas Bonnefon std::list<ObservedFile<Driver>> observed_files_; 189c540156cSNicolas Bonnefon 190c540156cSNicolas Bonnefon // List of observed dirs, key-ed by name 191f09fa651SNicolas Bonnefon std::map<std::string, std::weak_ptr<ObservedDir<Driver>>> observed_dirs_; 192c540156cSNicolas Bonnefon 193c540156cSNicolas Bonnefon // Map the inotify file (including symlinks) wds to the observed file 194f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_file_wd_; 195c540156cSNicolas Bonnefon // Map the inotify directory wds to the observed files 196f09fa651SNicolas Bonnefon std::map<int, ObservedFile<Driver>*> by_dir_wd_; 1973104b268SNicolas Bonnefon 198*5751f354SAlexander Hirsch // A shared_ptr to self. 199*5751f354SAlexander Hirsch // weak_ptr's from this can check if the list is still alive. 200*5751f354SAlexander Hirsch // This probably shouldn't be copied. 201*5751f354SAlexander Hirsch std::shared_ptr<ObservedFileList> heartBeat_; 2023104b268SNicolas Bonnefon 2033104b268SNicolas Bonnefon // Clean all reference to any expired directory 2043104b268SNicolas Bonnefon void cleanRefsToExpiredDirs(); 205c540156cSNicolas Bonnefon }; 206c540156cSNicolas Bonnefon 207f09fa651SNicolas Bonnefon namespace { 208f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ); 209f09fa651SNicolas Bonnefon }; 210f09fa651SNicolas Bonnefon 211f09fa651SNicolas Bonnefon // ObservedFileList class 212f09fa651SNicolas Bonnefon template <typename Driver> 213f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByName( 214f09fa651SNicolas Bonnefon const std::string& file_name ) 215f09fa651SNicolas Bonnefon { 216f09fa651SNicolas Bonnefon // Look for an existing observer on this file 217f09fa651SNicolas Bonnefon auto existing_observer = observed_files_.begin(); 218f09fa651SNicolas Bonnefon for ( ; existing_observer != observed_files_.end(); ++existing_observer ) 219f09fa651SNicolas Bonnefon { 220f09fa651SNicolas Bonnefon if ( existing_observer->file_name_ == file_name ) 221f09fa651SNicolas Bonnefon { 222f09fa651SNicolas Bonnefon LOG(logDEBUG) << "Found " << file_name; 223f09fa651SNicolas Bonnefon break; 224f09fa651SNicolas Bonnefon } 225f09fa651SNicolas Bonnefon } 226f09fa651SNicolas Bonnefon 227f09fa651SNicolas Bonnefon if ( existing_observer != observed_files_.end() ) 228f09fa651SNicolas Bonnefon return &( *existing_observer ); 229f09fa651SNicolas Bonnefon else 230f09fa651SNicolas Bonnefon return nullptr; 231f09fa651SNicolas Bonnefon } 232f09fa651SNicolas Bonnefon 233f09fa651SNicolas Bonnefon template <typename Driver> 234f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByFileOrSymlinkWd( 235f09fa651SNicolas Bonnefon typename Driver::FileId file_id, 236f09fa651SNicolas Bonnefon typename Driver::SymlinkId symlink_id ) 237f09fa651SNicolas Bonnefon { 238f09fa651SNicolas Bonnefon auto result = find_if( observed_files_.begin(), observed_files_.end(), 239f09fa651SNicolas Bonnefon [file_id, symlink_id] (ObservedFile<Driver> file) -> bool { 240f09fa651SNicolas Bonnefon return ( file_id == file.file_id_ ) || 241f09fa651SNicolas Bonnefon ( symlink_id == file.symlink_id_ ); 242f09fa651SNicolas Bonnefon } ); 243f09fa651SNicolas Bonnefon 244f09fa651SNicolas Bonnefon if ( result != observed_files_.end() ) 245f09fa651SNicolas Bonnefon return &( *result ); 246f09fa651SNicolas Bonnefon else 247f09fa651SNicolas Bonnefon return nullptr; 248f09fa651SNicolas Bonnefon } 249f09fa651SNicolas Bonnefon 250f09fa651SNicolas Bonnefon template <typename Driver> 251f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByDirWdAndName( 252f09fa651SNicolas Bonnefon typename Driver::DirId id, const char* name ) 253f09fa651SNicolas Bonnefon { 254f09fa651SNicolas Bonnefon auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(), 255f09fa651SNicolas Bonnefon [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool { 256f09fa651SNicolas Bonnefon if ( auto dir = d.second.lock() ) { 257f09fa651SNicolas Bonnefon return ( id == dir->dir_id_ ); 258f09fa651SNicolas Bonnefon } 259f09fa651SNicolas Bonnefon else { 260f09fa651SNicolas Bonnefon return false; } } ); 261f09fa651SNicolas Bonnefon 262f09fa651SNicolas Bonnefon if ( dir != observed_dirs_.end() ) { 263f09fa651SNicolas Bonnefon std::string path = dir->first + "/" + name; 264f09fa651SNicolas Bonnefon 265f09fa651SNicolas Bonnefon // LOG(logDEBUG) << "Testing path: " << path; 266f09fa651SNicolas Bonnefon 267f09fa651SNicolas Bonnefon // Looking for the path in the files we are watching 268f09fa651SNicolas Bonnefon return searchByName( path ); 269f09fa651SNicolas Bonnefon } 270f09fa651SNicolas Bonnefon else { 271f09fa651SNicolas Bonnefon return nullptr; 272f09fa651SNicolas Bonnefon } 273f09fa651SNicolas Bonnefon } 274f09fa651SNicolas Bonnefon 275f09fa651SNicolas Bonnefon template <typename Driver> 276933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> ObservedFileList<Driver>::searchByDirWd( 277933a5744SNicolas Bonnefon typename Driver::DirId id ) 278933a5744SNicolas Bonnefon { 279933a5744SNicolas Bonnefon std::vector<ObservedFile<Driver>*> result; 280933a5744SNicolas Bonnefon 281933a5744SNicolas Bonnefon auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(), 282933a5744SNicolas Bonnefon [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool { 283933a5744SNicolas Bonnefon if ( auto dir = d.second.lock() ) { 284933a5744SNicolas Bonnefon return ( id == dir->dir_id_ ); 285933a5744SNicolas Bonnefon } 286933a5744SNicolas Bonnefon else { 287933a5744SNicolas Bonnefon return false; } } ); 288933a5744SNicolas Bonnefon 289933a5744SNicolas Bonnefon if ( dir != observed_dirs_.end() ) { 290933a5744SNicolas Bonnefon for ( auto i = observed_files_.begin(); i != observed_files_.end(); ++i ) { 291933a5744SNicolas Bonnefon if ( auto d = dir->second.lock() ) { 292933a5744SNicolas Bonnefon if ( d.get() == i->dir_.get() ) { 293933a5744SNicolas Bonnefon result.push_back( &(*i) ); 294933a5744SNicolas Bonnefon } 295933a5744SNicolas Bonnefon } 296933a5744SNicolas Bonnefon } 297933a5744SNicolas Bonnefon } 298933a5744SNicolas Bonnefon 299933a5744SNicolas Bonnefon return result; 300933a5744SNicolas Bonnefon } 301933a5744SNicolas Bonnefon 302933a5744SNicolas Bonnefon template <typename Driver> 303f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::addNewObservedFile( 304f09fa651SNicolas Bonnefon ObservedFile<Driver> new_observed ) 305f09fa651SNicolas Bonnefon { 306f09fa651SNicolas Bonnefon auto new_file = observed_files_.insert( std::begin( observed_files_ ), new_observed ); 307f09fa651SNicolas Bonnefon 308f09fa651SNicolas Bonnefon return &( *new_file ); 309f09fa651SNicolas Bonnefon } 310f09fa651SNicolas Bonnefon 311f09fa651SNicolas Bonnefon template <typename Driver> 312f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> ObservedFileList<Driver>::removeCallback( 313f09fa651SNicolas Bonnefon std::shared_ptr<void> callback ) 314f09fa651SNicolas Bonnefon { 315f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> returned_file = nullptr; 316f09fa651SNicolas Bonnefon 317fcaa7557SNicolas Bonnefon for ( auto observer = std::begin( observed_files_ ); 318fcaa7557SNicolas Bonnefon observer != std::end( observed_files_ ); ) 319f09fa651SNicolas Bonnefon { 320f09fa651SNicolas Bonnefon std::vector<std::shared_ptr<void>>& callbacks = observer->callbacks; 321f09fa651SNicolas Bonnefon callbacks.erase( std::remove( 322f09fa651SNicolas Bonnefon std::begin( callbacks ), std::end( callbacks ), callback ), 323f09fa651SNicolas Bonnefon std::end( callbacks ) ); 324f09fa651SNicolas Bonnefon 325f09fa651SNicolas Bonnefon /* See if all notifications have been deleted for this file */ 326f09fa651SNicolas Bonnefon if ( callbacks.empty() ) { 3273104b268SNicolas Bonnefon LOG(logDEBUG) << "Empty notification list for " << observer->file_name_ 3283104b268SNicolas Bonnefon << ", removing the watched file"; 329f09fa651SNicolas Bonnefon returned_file = std::make_shared<ObservedFile<Driver>>( *observer ); 330f09fa651SNicolas Bonnefon observer = observed_files_.erase( observer ); 331f09fa651SNicolas Bonnefon } 332f09fa651SNicolas Bonnefon else { 333f09fa651SNicolas Bonnefon ++observer; 334f09fa651SNicolas Bonnefon } 335f09fa651SNicolas Bonnefon } 336f09fa651SNicolas Bonnefon 337f09fa651SNicolas Bonnefon return returned_file; 338f09fa651SNicolas Bonnefon } 339f09fa651SNicolas Bonnefon 340f09fa651SNicolas Bonnefon template <typename Driver> 341f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectory( 342f09fa651SNicolas Bonnefon const std::string& dir_name ) 343f09fa651SNicolas Bonnefon { 344f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = nullptr; 345f09fa651SNicolas Bonnefon 346f09fa651SNicolas Bonnefon if ( observed_dirs_.find( dir_name ) != std::end( observed_dirs_ ) ) 347f09fa651SNicolas Bonnefon dir = observed_dirs_[ dir_name ].lock(); 348f09fa651SNicolas Bonnefon 349f09fa651SNicolas Bonnefon return dir; 350f09fa651SNicolas Bonnefon } 351f09fa651SNicolas Bonnefon 352f09fa651SNicolas Bonnefon template <typename Driver> 353f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectory( 3543104b268SNicolas Bonnefon const std::string& dir_name, 3553104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 356f09fa651SNicolas Bonnefon { 357*5751f354SAlexander Hirsch std::weak_ptr<ObservedFileList> weakHeartBeat(heartBeat_); 3583104b268SNicolas Bonnefon 3593104b268SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> dir = { 3603104b268SNicolas Bonnefon new ObservedDir<Driver>( dir_name ), 361*5751f354SAlexander Hirsch [remove_notification, weakHeartBeat] (ObservedDir<Driver>* d) { 362*5751f354SAlexander Hirsch if ( auto list = weakHeartBeat.lock() ) { 3633104b268SNicolas Bonnefon remove_notification( d ); 364*5751f354SAlexander Hirsch list->cleanRefsToExpiredDirs(); 3653104b268SNicolas Bonnefon } 3663104b268SNicolas Bonnefon delete d; } }; 367f09fa651SNicolas Bonnefon 368f09fa651SNicolas Bonnefon observed_dirs_[ dir_name ] = std::weak_ptr<ObservedDir<Driver>>( dir ); 369f09fa651SNicolas Bonnefon 370f09fa651SNicolas Bonnefon return dir; 371f09fa651SNicolas Bonnefon } 372f09fa651SNicolas Bonnefon 373f09fa651SNicolas Bonnefon template <typename Driver> 374f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectoryForFile( 375f09fa651SNicolas Bonnefon const std::string& file_name ) 376f09fa651SNicolas Bonnefon { 377f09fa651SNicolas Bonnefon return watchedDirectory( directory_path( file_name ) ); 378f09fa651SNicolas Bonnefon } 379f09fa651SNicolas Bonnefon 380f09fa651SNicolas Bonnefon template <typename Driver> 381f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectoryForFile( 3823104b268SNicolas Bonnefon const std::string& file_name, 3833104b268SNicolas Bonnefon std::function<void( ObservedDir<Driver>* )> remove_notification ) 384f09fa651SNicolas Bonnefon { 3853104b268SNicolas Bonnefon return addWatchedDirectory( directory_path( file_name ), 3863104b268SNicolas Bonnefon remove_notification ); 3873104b268SNicolas Bonnefon } 3883104b268SNicolas Bonnefon 3893104b268SNicolas Bonnefon template <typename Driver> 3903104b268SNicolas Bonnefon unsigned int ObservedFileList<Driver>::numberWatchedDirectories() const 3913104b268SNicolas Bonnefon { 3923104b268SNicolas Bonnefon return observed_dirs_.size(); 3933104b268SNicolas Bonnefon } 3943104b268SNicolas Bonnefon 3953104b268SNicolas Bonnefon // Private functions 3963104b268SNicolas Bonnefon template <typename Driver> 3973104b268SNicolas Bonnefon void ObservedFileList<Driver>::cleanRefsToExpiredDirs() 3983104b268SNicolas Bonnefon { 3993104b268SNicolas Bonnefon for ( auto it = std::begin( observed_dirs_ ); 4003104b268SNicolas Bonnefon it != std::end( observed_dirs_ ); ) 4013104b268SNicolas Bonnefon { 4023104b268SNicolas Bonnefon if ( it->second.expired() ) { 4033104b268SNicolas Bonnefon it = observed_dirs_.erase( it ); 4043104b268SNicolas Bonnefon } 4053104b268SNicolas Bonnefon else { 4063104b268SNicolas Bonnefon ++it; 4073104b268SNicolas Bonnefon } 4083104b268SNicolas Bonnefon } 409f09fa651SNicolas Bonnefon } 410f09fa651SNicolas Bonnefon 411f09fa651SNicolas Bonnefon namespace { 412f09fa651SNicolas Bonnefon std::string directory_path( const std::string& path ) 413f09fa651SNicolas Bonnefon { 414f09fa651SNicolas Bonnefon size_t slash_pos = path.rfind( '/' ); 415f09fa651SNicolas Bonnefon 416f09fa651SNicolas Bonnefon #ifdef _WIN32 417f09fa651SNicolas Bonnefon if ( slash_pos == std::string::npos ) { 418f09fa651SNicolas Bonnefon slash_pos = path.rfind( '\\' ); 419f09fa651SNicolas Bonnefon } 420f09fa651SNicolas Bonnefon 421f09fa651SNicolas Bonnefon // We need to include the final slash on Windows 422f09fa651SNicolas Bonnefon ++slash_pos; 423f09fa651SNicolas Bonnefon #endif 424f09fa651SNicolas Bonnefon 425f09fa651SNicolas Bonnefon return std::string( path, 0, slash_pos ); 426f09fa651SNicolas Bonnefon } 427f09fa651SNicolas Bonnefon }; 428c540156cSNicolas Bonnefon #endif 429