xref: /glogg/src/watchtower.h (revision 84b2179eb3774e43dfb52da96116070d4545d8c7)
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