xref: /glogg/src/watchtowerlist.h (revision f09fa65124f80be4a92fab17a1cccc63d18936a5)
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>
13c540156cSNicolas Bonnefon 
14*f09fa651SNicolas Bonnefon #include "log.h"
15b278d183SNicolas Bonnefon 
16c540156cSNicolas Bonnefon // Utility classes
17c540156cSNicolas Bonnefon struct ProtocolInfo {
18c540156cSNicolas Bonnefon     // Win32 notification variables
19c540156cSNicolas Bonnefon     static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096;
20c540156cSNicolas Bonnefon 
21c540156cSNicolas Bonnefon     void* handle_;
22c540156cSNicolas Bonnefon     static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE;
23c540156cSNicolas Bonnefon     char buffer_[buffer_length_];
24c540156cSNicolas Bonnefon };
25c540156cSNicolas Bonnefon 
26c540156cSNicolas Bonnefon // List of files and observers
27*f09fa651SNicolas Bonnefon template <typename Driver>
28c540156cSNicolas Bonnefon struct ObservedDir {
29c540156cSNicolas Bonnefon     ObservedDir( const std::string this_path ) : path { this_path } {}
30c540156cSNicolas Bonnefon 
31c540156cSNicolas Bonnefon     // Returns the address of the protocol specific informations
32c540156cSNicolas Bonnefon     ProtocolInfo* protocolInfo() { return &protocol_info_; }
33c540156cSNicolas Bonnefon 
34c540156cSNicolas Bonnefon     std::string path;
35*f09fa651SNicolas Bonnefon     typename Driver::DirId dir_id_;
36c540156cSNicolas Bonnefon     // Contains data specific to the protocol (inotify/Win32...)
37c540156cSNicolas Bonnefon     ProtocolInfo protocol_info_;
38c540156cSNicolas Bonnefon };
39c540156cSNicolas Bonnefon 
40*f09fa651SNicolas Bonnefon template <typename Driver>
41c540156cSNicolas Bonnefon struct ObservedFile {
42c540156cSNicolas Bonnefon     ObservedFile(
43c540156cSNicolas Bonnefon             const std::string& file_name,
44c540156cSNicolas Bonnefon             std::shared_ptr<void> callback,
45*f09fa651SNicolas Bonnefon             typename Driver::FileId file_id,
46*f09fa651SNicolas Bonnefon             typename Driver::SymlinkId symlink_id )
47b278d183SNicolas Bonnefon         : file_name_( file_name ) {
48c540156cSNicolas Bonnefon         addCallback( callback );
49c540156cSNicolas Bonnefon 
50b278d183SNicolas Bonnefon         file_id_    = file_id;
51b278d183SNicolas Bonnefon         symlink_id_ = symlink_id;
52c540156cSNicolas Bonnefon         dir_        = nullptr;
53c540156cSNicolas Bonnefon     }
54c540156cSNicolas Bonnefon 
55c540156cSNicolas Bonnefon     void addCallback( std::shared_ptr<void> callback ) {
56c540156cSNicolas Bonnefon         callbacks.push_back( callback );
57c540156cSNicolas Bonnefon     }
58c540156cSNicolas Bonnefon 
59c540156cSNicolas Bonnefon     std::string file_name_;
60c540156cSNicolas Bonnefon     // List of callbacks for this file
61c540156cSNicolas Bonnefon     std::vector<std::shared_ptr<void>> callbacks;
62c540156cSNicolas Bonnefon 
63c540156cSNicolas Bonnefon     // watch descriptor for the file itself
64*f09fa651SNicolas Bonnefon     typename Driver::FileId file_id_;
65c540156cSNicolas Bonnefon     // watch descriptor for the symlink (if file is a symlink)
66*f09fa651SNicolas Bonnefon     typename Driver::SymlinkId symlink_id_;
67c540156cSNicolas Bonnefon 
68c540156cSNicolas Bonnefon     // link to the dir containing the file
69*f09fa651SNicolas Bonnefon     std::shared_ptr<ObservedDir<Driver>> dir_;
70c540156cSNicolas Bonnefon };
71c540156cSNicolas Bonnefon 
72c540156cSNicolas Bonnefon // A list of the observed files and directories
73c540156cSNicolas Bonnefon // This class is not thread safe
74*f09fa651SNicolas Bonnefon template<typename Driver>
75c540156cSNicolas Bonnefon class ObservedFileList {
76c540156cSNicolas Bonnefon     public:
77c540156cSNicolas Bonnefon         ObservedFileList() = default;
78c540156cSNicolas Bonnefon         ~ObservedFileList() = default;
79c540156cSNicolas Bonnefon 
80*f09fa651SNicolas Bonnefon         ObservedFile<Driver>* searchByName( const std::string& file_name );
81*f09fa651SNicolas Bonnefon         ObservedFile<Driver>* searchByFileOrSymlinkWd(
82*f09fa651SNicolas Bonnefon                 typename Driver::FileId file_id,
83*f09fa651SNicolas Bonnefon                 typename Driver::SymlinkId symlink_id );
84*f09fa651SNicolas Bonnefon         ObservedFile<Driver>* searchByDirWdAndName(
85*f09fa651SNicolas Bonnefon                 typename Driver::DirId id, const char* name );
86c540156cSNicolas Bonnefon 
87*f09fa651SNicolas Bonnefon         ObservedFile<Driver>* addNewObservedFile( ObservedFile<Driver> new_observed );
88c540156cSNicolas Bonnefon         // Remove a callback, remove and returns the file object if
89c540156cSNicolas Bonnefon         // it was the last callback on this object, nullptr if not.
90c540156cSNicolas Bonnefon         // The caller has ownership of the object.
91*f09fa651SNicolas Bonnefon         std::shared_ptr<ObservedFile<Driver>> removeCallback(
92c540156cSNicolas Bonnefon                 std::shared_ptr<void> callback );
93c540156cSNicolas Bonnefon 
94c540156cSNicolas Bonnefon         // Return the watched directory if it is watched, or nullptr
95*f09fa651SNicolas Bonnefon         std::shared_ptr<ObservedDir<Driver>> watchedDirectory( const std::string& dir_name );
96c540156cSNicolas Bonnefon         // Create a new watched directory for dir_name
97*f09fa651SNicolas Bonnefon         std::shared_ptr<ObservedDir<Driver>> addWatchedDirectory( const std::string& dir_name );
98c540156cSNicolas Bonnefon 
99*f09fa651SNicolas Bonnefon         std::shared_ptr<ObservedDir<Driver>> watchedDirectoryForFile( const std::string& file_name );
100*f09fa651SNicolas Bonnefon         std::shared_ptr<ObservedDir<Driver>> addWatchedDirectoryForFile( const std::string& file_name );
101c540156cSNicolas Bonnefon 
102c540156cSNicolas Bonnefon     private:
103c540156cSNicolas Bonnefon         // List of observed files
104*f09fa651SNicolas Bonnefon         std::list<ObservedFile<Driver>> observed_files_;
105c540156cSNicolas Bonnefon 
106c540156cSNicolas Bonnefon         // List of observed dirs, key-ed by name
107*f09fa651SNicolas Bonnefon         std::map<std::string, std::weak_ptr<ObservedDir<Driver>>> observed_dirs_;
108c540156cSNicolas Bonnefon 
109c540156cSNicolas Bonnefon         // Map the inotify file (including symlinks) wds to the observed file
110*f09fa651SNicolas Bonnefon         std::map<int, ObservedFile<Driver>*> by_file_wd_;
111c540156cSNicolas Bonnefon         // Map the inotify directory wds to the observed files
112*f09fa651SNicolas Bonnefon         std::map<int, ObservedFile<Driver>*> by_dir_wd_;
113c540156cSNicolas Bonnefon };
114c540156cSNicolas Bonnefon 
115*f09fa651SNicolas Bonnefon namespace {
116*f09fa651SNicolas Bonnefon     std::string directory_path( const std::string& path );
117*f09fa651SNicolas Bonnefon };
118*f09fa651SNicolas Bonnefon 
119*f09fa651SNicolas Bonnefon // ObservedFileList class
120*f09fa651SNicolas Bonnefon template <typename Driver>
121*f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByName(
122*f09fa651SNicolas Bonnefon         const std::string& file_name )
123*f09fa651SNicolas Bonnefon {
124*f09fa651SNicolas Bonnefon     // Look for an existing observer on this file
125*f09fa651SNicolas Bonnefon     auto existing_observer = observed_files_.begin();
126*f09fa651SNicolas Bonnefon     for ( ; existing_observer != observed_files_.end(); ++existing_observer )
127*f09fa651SNicolas Bonnefon     {
128*f09fa651SNicolas Bonnefon         if ( existing_observer->file_name_ == file_name )
129*f09fa651SNicolas Bonnefon         {
130*f09fa651SNicolas Bonnefon             LOG(logDEBUG) << "Found " << file_name;
131*f09fa651SNicolas Bonnefon             break;
132*f09fa651SNicolas Bonnefon         }
133*f09fa651SNicolas Bonnefon     }
134*f09fa651SNicolas Bonnefon 
135*f09fa651SNicolas Bonnefon     if ( existing_observer != observed_files_.end() )
136*f09fa651SNicolas Bonnefon         return &( *existing_observer );
137*f09fa651SNicolas Bonnefon     else
138*f09fa651SNicolas Bonnefon         return nullptr;
139*f09fa651SNicolas Bonnefon }
140*f09fa651SNicolas Bonnefon 
141*f09fa651SNicolas Bonnefon template <typename Driver>
142*f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByFileOrSymlinkWd(
143*f09fa651SNicolas Bonnefon         typename Driver::FileId file_id,
144*f09fa651SNicolas Bonnefon         typename Driver::SymlinkId symlink_id )
145*f09fa651SNicolas Bonnefon {
146*f09fa651SNicolas Bonnefon     auto result = find_if( observed_files_.begin(), observed_files_.end(),
147*f09fa651SNicolas Bonnefon             [file_id, symlink_id] (ObservedFile<Driver> file) -> bool {
148*f09fa651SNicolas Bonnefon                 return ( file_id == file.file_id_ ) ||
149*f09fa651SNicolas Bonnefon                     ( symlink_id == file.symlink_id_ );
150*f09fa651SNicolas Bonnefon                 } );
151*f09fa651SNicolas Bonnefon 
152*f09fa651SNicolas Bonnefon     if ( result != observed_files_.end() )
153*f09fa651SNicolas Bonnefon         return &( *result );
154*f09fa651SNicolas Bonnefon     else
155*f09fa651SNicolas Bonnefon         return nullptr;
156*f09fa651SNicolas Bonnefon }
157*f09fa651SNicolas Bonnefon 
158*f09fa651SNicolas Bonnefon template <typename Driver>
159*f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::searchByDirWdAndName(
160*f09fa651SNicolas Bonnefon         typename Driver::DirId id, const char* name )
161*f09fa651SNicolas Bonnefon {
162*f09fa651SNicolas Bonnefon     auto dir = find_if( observed_dirs_.begin(), observed_dirs_.end(),
163*f09fa651SNicolas Bonnefon             [id] (std::pair<std::string,std::weak_ptr<ObservedDir<Driver>>> d) -> bool {
164*f09fa651SNicolas Bonnefon             if ( auto dir = d.second.lock() ) {
165*f09fa651SNicolas Bonnefon                 return ( id == dir->dir_id_ );
166*f09fa651SNicolas Bonnefon             }
167*f09fa651SNicolas Bonnefon             else {
168*f09fa651SNicolas Bonnefon                 return false; } } );
169*f09fa651SNicolas Bonnefon 
170*f09fa651SNicolas Bonnefon     if ( dir != observed_dirs_.end() ) {
171*f09fa651SNicolas Bonnefon         std::string path = dir->first + "/" + name;
172*f09fa651SNicolas Bonnefon 
173*f09fa651SNicolas Bonnefon         // LOG(logDEBUG) << "Testing path: " << path;
174*f09fa651SNicolas Bonnefon 
175*f09fa651SNicolas Bonnefon         // Looking for the path in the files we are watching
176*f09fa651SNicolas Bonnefon         return searchByName( path );
177*f09fa651SNicolas Bonnefon     }
178*f09fa651SNicolas Bonnefon     else {
179*f09fa651SNicolas Bonnefon         return nullptr;
180*f09fa651SNicolas Bonnefon     }
181*f09fa651SNicolas Bonnefon }
182*f09fa651SNicolas Bonnefon 
183*f09fa651SNicolas Bonnefon template <typename Driver>
184*f09fa651SNicolas Bonnefon ObservedFile<Driver>* ObservedFileList<Driver>::addNewObservedFile(
185*f09fa651SNicolas Bonnefon         ObservedFile<Driver> new_observed )
186*f09fa651SNicolas Bonnefon {
187*f09fa651SNicolas Bonnefon     auto new_file = observed_files_.insert( std::begin( observed_files_ ), new_observed );
188*f09fa651SNicolas Bonnefon 
189*f09fa651SNicolas Bonnefon     return &( *new_file );
190*f09fa651SNicolas Bonnefon }
191*f09fa651SNicolas Bonnefon 
192*f09fa651SNicolas Bonnefon template <typename Driver>
193*f09fa651SNicolas Bonnefon std::shared_ptr<ObservedFile<Driver>> ObservedFileList<Driver>::removeCallback(
194*f09fa651SNicolas Bonnefon         std::shared_ptr<void> callback )
195*f09fa651SNicolas Bonnefon {
196*f09fa651SNicolas Bonnefon     std::shared_ptr<ObservedFile<Driver>> returned_file = nullptr;
197*f09fa651SNicolas Bonnefon 
198*f09fa651SNicolas Bonnefon     for ( auto observer = begin( observed_files_ );
199*f09fa651SNicolas Bonnefon             observer != end( observed_files_ ); )
200*f09fa651SNicolas Bonnefon     {
201*f09fa651SNicolas Bonnefon         LOG(logDEBUG) << "Examining entry for " << observer->file_name_;
202*f09fa651SNicolas Bonnefon 
203*f09fa651SNicolas Bonnefon         std::vector<std::shared_ptr<void>>& callbacks = observer->callbacks;
204*f09fa651SNicolas Bonnefon         callbacks.erase( std::remove(
205*f09fa651SNicolas Bonnefon                     std::begin( callbacks ), std::end( callbacks ), callback ),
206*f09fa651SNicolas Bonnefon                 std::end( callbacks ) );
207*f09fa651SNicolas Bonnefon 
208*f09fa651SNicolas Bonnefon         /* See if all notifications have been deleted for this file */
209*f09fa651SNicolas Bonnefon         if ( callbacks.empty() ) {
210*f09fa651SNicolas Bonnefon             LOG(logDEBUG) << "Empty notification list, removing the watched file";
211*f09fa651SNicolas Bonnefon             returned_file = std::make_shared<ObservedFile<Driver>>( *observer );
212*f09fa651SNicolas Bonnefon             observer = observed_files_.erase( observer );
213*f09fa651SNicolas Bonnefon         }
214*f09fa651SNicolas Bonnefon         else {
215*f09fa651SNicolas Bonnefon             ++observer;
216*f09fa651SNicolas Bonnefon         }
217*f09fa651SNicolas Bonnefon     }
218*f09fa651SNicolas Bonnefon 
219*f09fa651SNicolas Bonnefon     return returned_file;
220*f09fa651SNicolas Bonnefon }
221*f09fa651SNicolas Bonnefon 
222*f09fa651SNicolas Bonnefon template <typename Driver>
223*f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectory(
224*f09fa651SNicolas Bonnefon         const std::string& dir_name )
225*f09fa651SNicolas Bonnefon {
226*f09fa651SNicolas Bonnefon     std::shared_ptr<ObservedDir<Driver>> dir = nullptr;
227*f09fa651SNicolas Bonnefon 
228*f09fa651SNicolas Bonnefon     if ( observed_dirs_.find( dir_name ) != std::end( observed_dirs_ ) )
229*f09fa651SNicolas Bonnefon         dir = observed_dirs_[ dir_name ].lock();
230*f09fa651SNicolas Bonnefon 
231*f09fa651SNicolas Bonnefon     return dir;
232*f09fa651SNicolas Bonnefon }
233*f09fa651SNicolas Bonnefon 
234*f09fa651SNicolas Bonnefon template <typename Driver>
235*f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectory(
236*f09fa651SNicolas Bonnefon         const std::string& dir_name )
237*f09fa651SNicolas Bonnefon {
238*f09fa651SNicolas Bonnefon     auto dir = std::make_shared<ObservedDir<Driver>>( dir_name );
239*f09fa651SNicolas Bonnefon 
240*f09fa651SNicolas Bonnefon     observed_dirs_[ dir_name ] = std::weak_ptr<ObservedDir<Driver>>( dir );
241*f09fa651SNicolas Bonnefon 
242*f09fa651SNicolas Bonnefon     return dir;
243*f09fa651SNicolas Bonnefon }
244*f09fa651SNicolas Bonnefon 
245*f09fa651SNicolas Bonnefon template <typename Driver>
246*f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::watchedDirectoryForFile(
247*f09fa651SNicolas Bonnefon         const std::string& file_name )
248*f09fa651SNicolas Bonnefon {
249*f09fa651SNicolas Bonnefon     return watchedDirectory( directory_path( file_name ) );
250*f09fa651SNicolas Bonnefon }
251*f09fa651SNicolas Bonnefon 
252*f09fa651SNicolas Bonnefon template <typename Driver>
253*f09fa651SNicolas Bonnefon std::shared_ptr<ObservedDir<Driver>> ObservedFileList<Driver>::addWatchedDirectoryForFile(
254*f09fa651SNicolas Bonnefon         const std::string& file_name )
255*f09fa651SNicolas Bonnefon {
256*f09fa651SNicolas Bonnefon     return addWatchedDirectory( directory_path( file_name ) );
257*f09fa651SNicolas Bonnefon }
258*f09fa651SNicolas Bonnefon 
259*f09fa651SNicolas Bonnefon namespace {
260*f09fa651SNicolas Bonnefon     std::string directory_path( const std::string& path )
261*f09fa651SNicolas Bonnefon     {
262*f09fa651SNicolas Bonnefon         size_t slash_pos = path.rfind( '/' );
263*f09fa651SNicolas Bonnefon 
264*f09fa651SNicolas Bonnefon #ifdef _WIN32
265*f09fa651SNicolas Bonnefon         if ( slash_pos == std::string::npos ) {
266*f09fa651SNicolas Bonnefon             slash_pos = path.rfind( '\\' );
267*f09fa651SNicolas Bonnefon         }
268*f09fa651SNicolas Bonnefon 
269*f09fa651SNicolas Bonnefon         // We need to include the final slash on Windows
270*f09fa651SNicolas Bonnefon         ++slash_pos;
271*f09fa651SNicolas Bonnefon         LOG(logDEBUG) << "Pos = " << slash_pos;
272*f09fa651SNicolas Bonnefon #endif
273*f09fa651SNicolas Bonnefon 
274*f09fa651SNicolas Bonnefon         return std::string( path, 0, slash_pos );
275*f09fa651SNicolas Bonnefon     }
276*f09fa651SNicolas Bonnefon };
277c540156cSNicolas Bonnefon #endif
278