1 | // |
2 | // DirectoryWatcher.h |
3 | // |
4 | // Library: Foundation |
5 | // Package: Filesystem |
6 | // Module: DirectoryWatcher |
7 | // |
8 | // Definition of the DirectoryWatcher class. |
9 | // |
10 | // Copyright (c) 2012, Applied Informatics Software Engineering GmbH. |
11 | // and Contributors. |
12 | // |
13 | // SPDX-License-Identifier: BSL-1.0 |
14 | // |
15 | |
16 | |
17 | #ifndef Foundation_DirectoryWatcher_INCLUDED |
18 | #define Foundation_DirectoryWatcher_INCLUDED |
19 | |
20 | |
21 | #include "Poco/Foundation.h" |
22 | |
23 | |
24 | #ifndef POCO_NO_INOTIFY |
25 | |
26 | |
27 | #include "Poco/File.h" |
28 | #include "Poco/BasicEvent.h" |
29 | #include "Poco/Runnable.h" |
30 | #include "Poco/Thread.h" |
31 | #include "Poco/AtomicCounter.h" |
32 | |
33 | |
34 | namespace Poco { |
35 | |
36 | |
37 | class DirectoryWatcherStrategy; |
38 | |
39 | |
40 | class Foundation_API DirectoryWatcher: protected Runnable |
41 | /// This class is used to get notifications about changes |
42 | /// to the filesystem, more specifically, to a specific |
43 | /// directory. Changes to a directory are reported via |
44 | /// events. |
45 | /// |
46 | /// A thread will be created that watches the specified |
47 | /// directory for changes. Events are reported in the context |
48 | /// of this thread. |
49 | /// |
50 | /// Note that changes to files in subdirectories of the watched |
51 | /// directory are not reported. Separate DirectoryWatcher objects |
52 | /// must be created for these directories if they should be watched. |
53 | /// |
54 | /// Changes to file attributes are not reported. |
55 | /// |
56 | /// On Windows, this class is implemented using FindFirstChangeNotification()/FindNextChangeNotification(). |
57 | /// On Linux, this class is implemented using inotify. |
58 | /// On FreeBSD and Darwin (Mac OS X, iOS), this class uses kevent/kqueue. |
59 | /// On all other platforms, the watched directory is periodically scanned |
60 | /// for changes. This can negatively affect performance if done too often. |
61 | /// Therefore, the interval in which scans are done can be specified in |
62 | /// the constructor. Note that periodic scanning will also be done on FreeBSD |
63 | /// and Darwin if events for changes to files (DW_ITEM_MODIFIED) are enabled. |
64 | /// To avoid problems (e.g. with network shares notifications), scanning |
65 | /// can be forced as the only mechanism, regardless of platform default. |
66 | /// |
67 | /// DW_ITEM_MOVED_FROM and DW_ITEM_MOVED_TO events will only be reported |
68 | /// on Linux. On other platforms, a file rename or move operation |
69 | /// will be reported via a DW_ITEM_REMOVED and a DW_ITEM_ADDED event. |
70 | /// The order of these two events is not defined. |
71 | /// |
72 | /// An event mask can be specified to enable only certain events. |
73 | { |
74 | public: |
75 | enum DirectoryEventType |
76 | { |
77 | DW_ITEM_ADDED = 1, |
78 | /// A new item has been created and added to the directory. |
79 | |
80 | DW_ITEM_REMOVED = 2, |
81 | /// An item has been removed from the directory. |
82 | |
83 | DW_ITEM_MODIFIED = 4, |
84 | /// An item has been modified. |
85 | |
86 | DW_ITEM_MOVED_FROM = 8, |
87 | /// An item has been renamed or moved. This event delivers the old name. |
88 | |
89 | DW_ITEM_MOVED_TO = 16 |
90 | /// An item has been renamed or moved. This event delivers the new name. |
91 | }; |
92 | |
93 | enum DirectoryEventMask |
94 | { |
95 | DW_FILTER_ENABLE_ALL = 31, |
96 | /// Enables all event types. |
97 | |
98 | DW_FILTER_DISABLE_ALL = 0 |
99 | /// Disables all event types. |
100 | }; |
101 | |
102 | enum |
103 | { |
104 | DW_DEFAULT_SCAN_INTERVAL = 5 /// Default scan interval for platforms that don't provide a native notification mechanism. |
105 | }; |
106 | |
107 | struct DirectoryEvent |
108 | { |
109 | DirectoryEvent(const File& f, DirectoryEventType ev): |
110 | item(f), |
111 | event(ev) |
112 | { |
113 | } |
114 | |
115 | const File& item; /// The directory or file that has been changed. |
116 | DirectoryEventType event; /// The kind of event. |
117 | }; |
118 | |
119 | BasicEvent<const DirectoryEvent> itemAdded; |
120 | /// Fired when a file or directory has been created or added to the directory. |
121 | |
122 | BasicEvent<const DirectoryEvent> itemRemoved; |
123 | /// Fired when a file or directory has been removed from the directory. |
124 | |
125 | BasicEvent<const DirectoryEvent> itemModified; |
126 | /// Fired when a file or directory has been modified. |
127 | |
128 | BasicEvent<const DirectoryEvent> itemMovedFrom; |
129 | /// Fired when a file or directory has been renamed. This event delivers the old name. |
130 | |
131 | BasicEvent<const DirectoryEvent> itemMovedTo; |
132 | /// Fired when a file or directory has been moved. This event delivers the new name. |
133 | |
134 | BasicEvent<const Exception> scanError; |
135 | /// Fired when an error occurs while scanning for changes. |
136 | |
137 | DirectoryWatcher(const std::string& path, |
138 | int eventMask = DW_FILTER_ENABLE_ALL, |
139 | int scanInterval = DW_DEFAULT_SCAN_INTERVAL, |
140 | bool forceScan = false); |
141 | /// Creates a DirectoryWatcher for the directory given in path. |
142 | /// To enable only specific events, an eventMask can be specified by |
143 | /// OR-ing the desired event IDs (e.g., DW_ITEM_ADDED | DW_ITEM_MODIFIED). |
144 | /// On platforms where no native filesystem notifications are available, |
145 | /// scanInterval specifies the interval in seconds between scans |
146 | /// of the directory. Native notification mechanism can also be disabled |
147 | /// (i.e. replaced with scanning) by setting forceScan to true. |
148 | |
149 | DirectoryWatcher(const File& directory, |
150 | int eventMask = DW_FILTER_ENABLE_ALL, |
151 | int scanInterval = DW_DEFAULT_SCAN_INTERVAL, |
152 | bool forceScan = false); |
153 | /// Creates a DirectoryWatcher for the specified directory |
154 | /// To enable only specific events, an eventMask can be specified by |
155 | /// OR-ing the desired event IDs (e.g., DW_ITEM_ADDED | DW_ITEM_MODIFIED). |
156 | /// On platforms where no native filesystem notifications are available, |
157 | /// scanInterval specifies the interval in seconds between scans |
158 | /// of the directory. Native notification mechanism can also be disabled |
159 | /// (i.e. replaced with scanning) by setting forceScan to true. |
160 | |
161 | ~DirectoryWatcher(); |
162 | /// Destroys the DirectoryWatcher. |
163 | |
164 | void suspendEvents(); |
165 | /// Suspends sending of events. Can be called multiple times, but every |
166 | /// call to suspendEvent() must be matched by a call to resumeEvents(). |
167 | |
168 | void resumeEvents(); |
169 | /// Resumes events, after they have been suspended with a call to suspendEvents(). |
170 | |
171 | bool eventsSuspended() const; |
172 | /// Returns true iff events are suspended. |
173 | |
174 | int eventMask() const; |
175 | /// Returns the value of the eventMask passed to the constructor. |
176 | |
177 | int scanInterval() const; |
178 | /// Returns the scan interval in seconds. |
179 | |
180 | const File& directory() const; |
181 | /// Returns the directory being watched. |
182 | |
183 | bool supportsMoveEvents() const; |
184 | /// Returns true iff the platform supports DW_ITEM_MOVED_FROM/itemMovedFrom and |
185 | /// DW_ITEM_MOVED_TO/itemMovedTo events. |
186 | |
187 | protected: |
188 | void init(); |
189 | void stop(); |
190 | void run(); |
191 | |
192 | private: |
193 | DirectoryWatcher(); |
194 | DirectoryWatcher(const DirectoryWatcher&); |
195 | DirectoryWatcher& operator = (const DirectoryWatcher&); |
196 | |
197 | Thread _thread; |
198 | File _directory; |
199 | int _eventMask; |
200 | AtomicCounter _eventsSuspended; |
201 | int _scanInterval; |
202 | bool _forceScan; |
203 | DirectoryWatcherStrategy* _pStrategy; |
204 | }; |
205 | |
206 | |
207 | // |
208 | // inlines |
209 | // |
210 | |
211 | |
212 | inline bool DirectoryWatcher::eventsSuspended() const |
213 | { |
214 | return _eventsSuspended.value() > 0; |
215 | } |
216 | |
217 | |
218 | inline int DirectoryWatcher::eventMask() const |
219 | { |
220 | return _eventMask; |
221 | } |
222 | |
223 | |
224 | inline int DirectoryWatcher::scanInterval() const |
225 | { |
226 | return _scanInterval; |
227 | } |
228 | |
229 | |
230 | inline const File& DirectoryWatcher::directory() const |
231 | { |
232 | return _directory; |
233 | } |
234 | |
235 | |
236 | } // namespace Poco |
237 | |
238 | |
239 | #endif // POCO_NO_INOTIFY |
240 | |
241 | |
242 | #endif // Foundation_DirectoryWatcher_INCLUDED |
243 | |
244 | |