xref: /glogg/src/watchtowerlist.h (revision 933a57448ddbe318abe6bf4495141eabeab59586)
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