1 #ifndef WATCHTOWER_H 2 #define WATCHTOWER_H 3 4 #include <memory> 5 #include <functional> 6 #include <string> 7 #include <vector> 8 #include <map> 9 #include <list> 10 11 class WatchTower { 12 public: 13 // Registration object to implement RAII 14 using Registration = std::shared_ptr<void>; 15 16 // Create an empty watchtower 17 WatchTower() {}; 18 // Destroy the object 19 virtual ~WatchTower() {}; 20 21 // Add a file to the notification list. notification will be called when 22 // the file is modified, moved or deleted. 23 // Lifetime of the notification is tied to the Registration object returned. 24 // Note the notification function is called with a mutex held and in a 25 // third party thread, beware of races! 26 virtual Registration addFile( const std::string& file_name, 27 std::function<void()> notification ) = 0; 28 29 protected: 30 // Win32 notification variables 31 static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 32 33 // Utility classes 34 struct ProtocolInfo { 35 void* handle_; 36 static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 37 char buffer_[buffer_length_]; 38 }; 39 40 // List of files and observers 41 struct ObservedDir { 42 ObservedDir( const std::string this_path ) : path { this_path } {} 43 44 // Returns the address of the protocol specific informations 45 ProtocolInfo* protocolInfo() { return &protocol_info_; } 46 47 std::string path; 48 int dir_wd_; 49 // Contains data specific to the protocol (inotify/Win32...) 50 ProtocolInfo protocol_info_; 51 }; 52 53 struct ObservedFile { 54 ObservedFile( 55 const std::string& file_name, 56 std::shared_ptr<void> callback, 57 int file_wd, 58 int symlink_wd ) : file_name_( file_name ) { 59 addCallback( callback ); 60 61 file_wd_ = file_wd; 62 symlink_wd_ = symlink_wd; 63 dir_ = nullptr; 64 } 65 66 void addCallback( std::shared_ptr<void> callback ) { 67 callbacks.push_back( callback ); 68 } 69 70 std::string file_name_; 71 // List of callbacks for this file 72 std::vector<std::shared_ptr<void>> callbacks; 73 74 // watch descriptor for the file itself 75 int file_wd_; 76 // watch descriptor for the symlink (if file is a symlink) 77 int symlink_wd_; 78 79 // link to the dir containing the file 80 std::shared_ptr<ObservedDir> dir_; 81 }; 82 83 // A list of the observed files and directories 84 // This class is not thread safe 85 class ObservedFileList { 86 public: 87 ObservedFileList() = default; 88 ~ObservedFileList() = default; 89 90 ObservedFile* searchByName( const std::string& file_name ); 91 ObservedFile* searchByFileOrSymlinkWd( int wd ); 92 ObservedFile* searchByDirWdAndName( int wd, const char* name ); 93 94 ObservedFile* addNewObservedFile( ObservedFile new_observed ); 95 // Remove a callback, remove and returns the file object if 96 // it was the last callback on this object, nullptr if not. 97 // The caller has ownership of the object. 98 std::shared_ptr<ObservedFile> removeCallback( 99 std::shared_ptr<void> callback ); 100 101 // Return the watched directory if it is watched, or nullptr 102 std::shared_ptr<ObservedDir> watchedDirectory( const std::string& dir_name ); 103 // Create a new watched directory for dir_name 104 std::shared_ptr<ObservedDir> addWatchedDirectory( const std::string& dir_name ); 105 106 std::shared_ptr<ObservedDir> watchedDirectoryForFile( const std::string& file_name ); 107 std::shared_ptr<ObservedDir> addWatchedDirectoryForFile( const std::string& file_name ); 108 109 private: 110 // List of observed files 111 std::list<ObservedFile> observed_files_; 112 113 // List of observed dirs, key-ed by name 114 std::map<std::string, std::weak_ptr<ObservedDir>> observed_dirs_; 115 116 // Map the inotify file (including symlinks) wds to the observed file 117 std::map<int, ObservedFile*> by_file_wd_; 118 // Map the inotify directory wds to the observed files 119 std::map<int, ObservedFile*> by_dir_wd_; 120 }; 121 }; 122 123 #endif 124