Mercurial > minori
annotate src/core/filesystem.cc @ 401:2f89797b6a44
filesystem: add linux inotify watcher
I don't really like this, but eh
| author | Paper <paper@tflc.us> |
|---|---|
| date | Fri, 07 Nov 2025 18:28:36 -0500 |
| parents | a0bc3ae5164a |
| children | d859306e2db4 |
| rev | line source |
|---|---|
| 36 | 1 #include "core/filesystem.h" |
| 15 | 2 #include "core/config.h" |
| 62 | 3 #include "core/strings.h" |
| 250 | 4 |
| 5 #include <QStandardPaths> | |
| 6 | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
7 #include <filesystem> |
| 2 | 8 |
| 398 | 9 #ifdef WIN32 |
| 10 # include <windows.h> | |
|
401
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
11 #elif defined(linux) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
12 /* ehhhh */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
13 # include <fcntl.h> |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
14 # include <unistd.h> |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
15 # include <sys/inotify.h> |
| 398 | 16 #endif |
| 17 | |
|
401
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
18 #include <iostream> |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
19 |
| 11 | 20 namespace Filesystem { |
| 21 | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
22 /* this runs fs::create_directories() on the |
|
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
23 PARENT directory. */ |
| 369 | 24 void CreateDirectories(const std::filesystem::path &path) |
| 25 { | |
|
158
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
26 if (path.empty()) |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
27 return; |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
28 |
| 369 | 29 const auto &parent = path.parent_path(); |
|
158
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
30 if (!std::filesystem::exists(parent)) |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
31 std::filesystem::create_directories(parent); |
| 11 | 32 } |
| 33 | |
| 369 | 34 std::filesystem::path GetDotPath() |
| 35 { | |
| 250 | 36 /* |
| 37 * Windows: ~/AppData/Roaming/Minori | |
| 38 * macOS: ~/Library/Application Support/Minori | |
| 39 * ...: ~/.config/minori | |
| 40 * | |
| 41 * FIXME: are windows and mac properly cased? | |
| 258 | 42 */ |
| 9 | 43 #ifdef WIN32 |
| 250 | 44 return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); |
| 45 #else | |
| 46 return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); | |
| 47 #endif | |
|
45
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
48 } |
|
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
49 |
| 369 | 50 std::filesystem::path GetConfigPath() |
| 51 { | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
52 return GetDotPath() / CONFIG_NAME; |
|
118
39521c47c7a3
*: another huge megacommit, SORRY
Paper <mrpapersonic@gmail.com>
parents:
106
diff
changeset
|
53 } |
|
39521c47c7a3
*: another huge megacommit, SORRY
Paper <mrpapersonic@gmail.com>
parents:
106
diff
changeset
|
54 |
| 369 | 55 std::filesystem::path GetAnimeDBPath() |
| 56 { | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
57 return GetDotPath() / "anime" / "db.json"; |
|
45
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
58 } |
|
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
59 |
| 369 | 60 std::filesystem::path GetTorrentsPath() |
| 61 { | |
|
230
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
62 return GetDotPath() / "torrents"; |
|
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
63 } |
|
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
64 |
| 378 | 65 std::filesystem::path GetAnimePostersPath() |
| 66 { | |
| 67 return GetDotPath() / "anime" / "posters"; | |
| 68 } | |
| 69 | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
70 /* ------------------------------------------------------------------------ */ |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
71 /* This is the "base class" for basically every watcher. */ |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
72 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
73 class Watcher : public IWatcher { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
74 public: |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
75 Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler) |
| 393 | 76 : path_(path), handler_(handler), opaque_(opaque) |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
77 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
78 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
79 |
| 393 | 80 virtual ~Watcher() override {} |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
81 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
82 protected: |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
83 std::filesystem::path path_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
84 EventHandler handler_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
85 void *opaque_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
86 }; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
87 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
88 /* ------------------------------------------------------------------------ */ |
|
391
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
89 /* Filesystem watcher. |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
90 * This is the portable version for non-Windows; however, pretty much |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
91 * every implementation should be based on this in some shape or form. */ |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
92 |
| 398 | 93 /* |
| 94 We can't use std::recursive_directory_iterator! | |
| 95 Why? Well, to put it blunt, it sucks. The main reason | |
| 96 being is that if it finds a single directory it cannot | |
| 97 recurse into, instead of safely recovering, it just | |
| 98 completely stops. I am dumbfounded at this behavior, but | |
| 99 nevertheless it means we simply can't use it, and must | |
| 100 resort to old-fashioned recursion. --paper | |
| 101 */ | |
| 102 static void IterateDirectory(const std::filesystem::path &path, bool recursive, | |
| 103 std::function<void(const std::filesystem::path &path)> func) | |
| 104 { | |
| 105 std::error_code ec; | |
| 106 static const std::filesystem::directory_iterator end; | |
| 107 | |
| 108 for (std::filesystem::directory_iterator item(path, ec); item != end; item.increment(ec)) { | |
| 109 if (ec) | |
| 110 continue; | |
| 111 | |
| 112 std::filesystem::path p = item->path(); | |
| 113 | |
| 114 /* Hand the path off to the listener */ | |
| 115 func(p); | |
| 116 | |
| 117 /* If we're dealing with another directory, recurse into it. */ | |
| 118 if (recursive && item->is_directory()) | |
| 119 IterateDirectory(p, true, func); | |
| 120 } | |
| 121 } | |
| 122 | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
123 class StdFilesystemWatcher : public Watcher { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
124 public: |
| 398 | 125 StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler, bool recursive) |
| 126 : Watcher(opaque, path, handler), recursive_(recursive) | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
127 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
128 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
129 |
| 393 | 130 virtual ~StdFilesystemWatcher() override {} |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
131 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
132 virtual void Process() override |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
133 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
134 /* Untoggle all paths. This allows us to only ever |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
135 * iterate over the directory ONCE. */ |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
136 UntoggleAllPaths(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
137 |
| 398 | 138 /* Start iterating directories. If we're recursive, this |
| 139 * will go through the whole tree. */ | |
| 140 IterateDirectory(path_, recursive_, [this](const std::filesystem::path &p) { | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
141 if (FindAndTogglePath(p)) |
| 398 | 142 return; |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
143 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
144 handler_(opaque_, p, Event::Created); |
| 398 | 145 paths_.insert({p, true}); |
| 146 }); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
147 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
148 DeleteUntoggledPaths(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
149 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
150 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
151 protected: |
|
391
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
152 bool FindAndTogglePath(const std::filesystem::path &p) |
|
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
153 { |
| 398 | 154 auto it = paths_.find(p); |
| 155 if (it == paths_.end()) | |
| 156 return false; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
157 |
| 398 | 158 it->second = true; |
| 159 return true; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
160 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
161 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
162 void UntoggleAllPaths() |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
163 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
164 for (auto &pp : paths_) |
| 398 | 165 pp.second = false; |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
166 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
167 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
168 void DeleteUntoggledPaths() |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
169 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
170 auto it = paths_.begin(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
171 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
172 while (it != paths_.end()) { |
| 398 | 173 if (!it->second) { |
| 174 handler_(opaque_, it->first, Event::Deleted); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
175 it = paths_.erase(it); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
176 } else { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
177 it++; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
178 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
179 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
180 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
181 |
| 398 | 182 /* unordered hashmap, path[found] */ |
| 183 std::unordered_map<std::filesystem::path, bool> paths_; | |
| 184 bool recursive_; | |
| 185 }; | |
| 186 | |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
187 /* There does actually exist an API in Qt to do file system watching. |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
188 * However, it is of little use for us, since it |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
189 * a) isn't recursive |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
190 * b) requires constant conversion to and from QString, which takes |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
191 * up valuable system resources |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
192 * c) uses signals etc. that are a pain in the ass to use in an |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
193 * event-processing logic like we do here. */ |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
194 |
| 398 | 195 #ifdef WIN32 |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
196 /* On Windows, we can ask the OS whether the directory tree has changed or not. |
| 398 | 197 * This is great for us! */ |
| 198 class Win32Watcher : public StdFilesystemWatcher { | |
| 199 public: | |
| 200 Win32Watcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | |
| 201 : StdFilesystemWatcher(opaque, path, handler, recursive), dirwatcher_(INVALID_HANDLE_VALUE), first_(true) | |
| 202 { | |
| 203 } | |
| 204 | |
| 205 virtual ~Win32Watcher() override | |
| 206 { | |
| 207 // delete handle | |
| 208 if (dirwatcher_ != INVALID_HANDLE_VALUE) | |
| 209 FindCloseChangeNotification(dirwatcher_); | |
| 210 } | |
| 211 | |
| 212 virtual void Process() override | |
| 213 { | |
| 214 if (first_ || dirwatcher_ == INVALID_HANDLE_VALUE) { | |
| 215 /* We want to create this right before iteration so | |
| 216 * we minimize possible race conditions while also | |
| 217 * reducing unnecessary processing */ | |
| 218 TryCreateDirWatcher(); | |
| 219 StdFilesystemWatcher::Process(); | |
| 220 first_ = false; | |
| 221 return; | |
| 222 } | |
| 223 | |
| 224 /* We have a valid handle */ | |
| 225 if (WaitForSingleObject(dirwatcher_, 0) != WAIT_OBJECT_0) | |
| 226 return; | |
| 227 | |
| 228 StdFilesystemWatcher::Process(); | |
| 229 FindNextChangeNotification(dirwatcher_); | |
| 230 } | |
| 231 | |
| 232 protected: | |
| 233 bool TryCreateDirWatcher() | |
| 234 { | |
| 235 dirwatcher_ = FindFirstChangeNotificationW(Watcher::path_.wstring().c_str(), recursive_, | |
| 236 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME); | |
| 237 | |
| 238 return (dirwatcher_ != INVALID_HANDLE_VALUE); | |
| 239 } | |
| 240 | |
| 241 /* variables */ | |
| 242 HANDLE dirwatcher_; | |
| 243 bool first_; | |
| 244 }; | |
| 245 | |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
246 /* I think "Vista" is actually a misnomer, MSDN says that ReadDirectoryChangesW |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
247 * has existed since Windows XP. |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
248 * |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
249 * Anyway, this is a version that doesn't need to keep the whole directory |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
250 * tree in-memory if it is available. */ |
| 398 | 251 class Win32WatcherVista : public Win32Watcher { |
| 252 public: | |
| 253 Win32WatcherVista(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | |
| 254 : Win32Watcher(opaque, path, handler, recursive), dirhandle_(INVALID_HANDLE_VALUE) | |
| 255 { | |
| 256 overlapped_.hEvent = nullptr; | |
| 257 } | |
| 258 | |
| 259 virtual ~Win32WatcherVista() override | |
| 260 { | |
| 261 if (dirhandle_ != INVALID_HANDLE_VALUE) | |
| 262 CloseHandle(dirhandle_); | |
| 263 | |
| 264 if (overlapped_.hEvent != nullptr) | |
| 265 CloseHandle(overlapped_.hEvent); | |
| 266 } | |
| 267 | |
| 268 virtual void Process() override | |
| 269 { | |
| 270 if (first_) { | |
| 271 if (TryCreateHandles()) { | |
| 272 /* Queue a directory read asynchronously. On the next run, this will | |
| 273 * be waited on. */ | |
| 274 QueueDirectoryRead(); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
275 |
| 398 | 276 /* Avoid running Win32Watcher::Process, as it will read the whole |
| 277 * directory tree into memory. Instead, iterate through the directory | |
| 278 * ourselves. */ | |
| 279 IterateDirectory(path_, recursive_, | |
| 280 [this](const std::filesystem::path &p) { handler_(opaque_, p, Event::Created); }); | |
| 281 } else { | |
| 282 /* Uh oh; we might have to fall back to Win32Watcher. Call into it to | |
| 283 * load the tree into memory. */ | |
| 284 Win32Watcher::Process(); | |
| 285 } | |
| 286 | |
| 287 first_ = false; | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 if (!HandlesAreValid()) { | |
| 292 /* If we're here, we already fell back into Win32Watcher, so just | |
| 293 * call back into again. On the slim chance TryCreateHandles() might | |
| 294 * succeed, call it as well... */ | |
| 295 if (TryCreateHandles()) | |
| 296 QueueDirectoryRead(); | |
| 297 Win32Watcher::Process(); | |
| 298 return; | |
| 299 } | |
| 300 | |
| 301 if (WaitForSingleObject(overlapped_.hEvent, 0) != WAIT_OBJECT_0) | |
| 302 return; | |
| 303 | |
| 304 FILE_NOTIFY_INFORMATION *ev = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(change_buf_); | |
| 305 | |
| 306 for (;;) { | |
| 307 /* Tack on the file name to the end of our path */ | |
| 308 std::filesystem::path p = Watcher::path_ / std::wstring(ev->FileName, ev->FileNameLength / sizeof(WCHAR)); | |
| 309 | |
| 310 switch (ev->Action) { | |
| 311 case FILE_ACTION_ADDED: | |
| 312 case FILE_ACTION_RENAMED_NEW_NAME: | |
| 313 /* File was added */ | |
| 314 handler_(opaque_, p, Event::Created); | |
| 315 break; | |
| 316 case FILE_ACTION_REMOVED: | |
| 317 case FILE_ACTION_RENAMED_OLD_NAME: | |
| 318 /* File was removed */ | |
| 319 handler_(opaque_, p, Event::Deleted); | |
| 320 break; | |
| 321 } | |
| 322 | |
| 323 if (ev->NextEntryOffset) { | |
| 324 /* ugh ugh ugh ugh */ | |
| 325 ev = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(reinterpret_cast<char *>(ev) + ev->NextEntryOffset); | |
| 326 } else { | |
| 327 break; | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 /* Queue a directory read for the next call */ | |
| 332 QueueDirectoryRead(); | |
| 333 } | |
| 334 | |
| 335 protected: | |
| 336 bool HandlesAreValid() { return (overlapped_.hEvent && dirhandle_ != INVALID_HANDLE_VALUE); } | |
| 337 | |
| 338 bool TryCreateHandles() | |
| 339 { | |
| 340 if (!overlapped_.hEvent) { | |
| 341 overlapped_.hEvent = CreateEventW(nullptr, FALSE, 0, nullptr); | |
| 342 if (!overlapped_.hEvent) | |
| 343 return false; | |
| 344 } | |
| 345 | |
| 346 if (dirhandle_ == INVALID_HANDLE_VALUE) { | |
| 347 dirhandle_ = | |
| 348 CreateFileW(Watcher::path_.wstring().c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| 349 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); | |
| 350 if (dirhandle_ == INVALID_HANDLE_VALUE) | |
| 351 return false; | |
| 352 } | |
| 353 | |
| 354 /* We're done here */ | |
| 355 return true; | |
| 356 } | |
| 357 | |
| 358 bool QueueDirectoryRead() | |
| 359 { | |
| 360 return ReadDirectoryChangesW(dirhandle_, change_buf_, sizeof(change_buf_), TRUE, | |
| 361 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, nullptr, &overlapped_, | |
| 362 nullptr); | |
| 363 } | |
| 364 | |
| 365 HANDLE dirhandle_; | |
| 366 OVERLAPPED overlapped_; | |
| 367 alignas(FILE_NOTIFY_INFORMATION) char change_buf_[4096]; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
368 }; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
369 |
| 398 | 370 using DefaultWatcher = Win32WatcherVista; |
|
401
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
371 #elif defined(__linux__) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
372 /* Inotify watcher */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
373 class InotifyWatcher : public StdFilesystemWatcher { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
374 public: |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
375 InotifyWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
376 : StdFilesystemWatcher(opaque, path, handler, recursive) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
377 , first_(true) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
378 , giveup_(false) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
379 , ev_(nullptr) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
380 , ev_size_(0) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
381 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
382 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
383 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
384 virtual ~InotifyWatcher() override |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
385 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
386 /* We don't need to free our watch descriptors; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
387 * they are automatically free'd up by the kernel */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
388 std::free(ev_); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
389 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
390 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
391 virtual void Process() override |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
392 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
393 if (giveup_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
394 StdFilesystemWatcher::Process(); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
395 return; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
396 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
397 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
398 if (first_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
399 /* Try creating the file descriptor */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
400 if (TryCreateFd()) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
401 /* Add toplevel directory */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
402 AddWatchDescriptor(Watcher::path_); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
403 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
404 /* Yay, we don't need to keep the dir structure in memory */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
405 IterateDirectory(Watcher::path_, recursive_, [this](const std::filesystem::path &p) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
406 /* hmm, we're stat'ing the file twice now... */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
407 if (std::filesystem::is_directory(p)) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
408 AddWatchDescriptor(p); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
409 handler_(opaque_, p, Event::Created); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
410 }); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
411 } else { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
412 /* Uh oh */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
413 giveup_ = true; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
414 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
415 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
416 if (giveup_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
417 /* ;p */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
418 StdFilesystemWatcher::Process(); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
419 return; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
420 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
421 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
422 first_ = false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
423 return; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
424 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
425 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
426 if (!fd_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
427 /* oh what the hell */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
428 giveup_ = true; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
429 StdFilesystemWatcher::Process(); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
430 return; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
431 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
432 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
433 /* Read in everything */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
434 for (;;) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
435 int r; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
436 do { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
437 ev_size_ = (ev_size_) ? (ev_size_ * 2) : (sizeof(struct inotify_event) + 4096); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
438 ev_ = reinterpret_cast<struct inotify_event *>(std::realloc(ev_, ev_size_)); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
439 if (!ev_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
440 /* uh oh */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
441 ev_size_ = 0; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
442 return; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
443 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
444 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
445 r = read(fd_.get(), ev_, ev_size_); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
446 } while (r == 0 || (r < 0 && errno == EINVAL)); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
447 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
448 if (r < 0 && errno == EAGAIN) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
449 /* No more events to process */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
450 break; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
451 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
452 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
453 for (int i = 0; i < r; /* ok */) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
454 struct inotify_event *ev = reinterpret_cast<struct inotify_event *>(reinterpret_cast<char *>(ev_) + i); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
455 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
456 if (ev->mask & (IN_DELETE_SELF)) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
457 /* Watched directory has been deleted */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
458 RemoveWatchDescriptor(ev->wd); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
459 continue; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
460 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
461 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
462 if (ev->mask & (IN_MOVE|IN_CREATE|IN_DELETE)) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
463 std::filesystem::path p = wds_[ev->wd] / ev->name; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
464 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
465 if (ev->mask & (IN_MOVED_TO|IN_CREATE)) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
466 if (std::filesystem::is_directory(p)) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
467 AddWatchDescriptor(p); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
468 handler_(opaque_, p, Event::Created); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
469 } else if (ev->mask & (IN_MOVED_FROM|IN_DELETE)) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
470 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
471 handler_(opaque_, wds_[ev->wd] / ev->name, Event::Deleted); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
472 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
473 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
474 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
475 i += sizeof(inotify_event) + ev->len; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
476 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
477 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
478 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
479 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
480 protected: |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
481 /* File descriptor helper. Mostly follows std::unique_ptr, |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
482 * but has a function for toggling non-blocking */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
483 struct FileDescriptor { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
484 public: |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
485 FileDescriptor() : fd_(-1) { } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
486 ~FileDescriptor() { reset(); } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
487 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
488 int get() { return fd_; } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
489 operator bool() { return (fd_ != -1); } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
490 void reset(int fd = -1) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
491 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
492 /* Close anything we already have */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
493 if (fd_ != -1) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
494 ::close(fd_); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
495 /* Put the new one in */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
496 fd_ = fd; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
497 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
498 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
499 bool SetNonBlocking(bool on) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
500 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
501 if (fd_ < 0) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
502 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
503 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
504 int x = fcntl(fd_, F_GETFL); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
505 if (x < 0) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
506 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
507 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
508 if (on) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
509 x |= O_NONBLOCK; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
510 } else { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
511 x &= ~O_NONBLOCK; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
512 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
513 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
514 int r = fcntl(fd_, F_SETFL, x); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
515 if (r < 0) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
516 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
517 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
518 return true; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
519 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
520 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
521 private: |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
522 int fd_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
523 }; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
524 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
525 bool TryCreateFd() |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
526 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
527 if (giveup_) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
528 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
529 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
530 if (!fd_) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
531 #ifdef HAVE_INOTIFY_INIT1 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
532 fd_.reset(inotify_init1(IN_NONBLOCK)); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
533 #else |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
534 fd_.reset(inotify_init()); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
535 #endif |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
536 if (!fd_) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
537 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
538 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
539 #ifndef HAVE_INOTIFY_INIT1 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
540 /* Very old linux */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
541 if (!fd_.SetNonBlocking(true)) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
542 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
543 #endif |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
544 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
545 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
546 return !!fd_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
547 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
548 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
549 bool AddWatchDescriptor(const std::filesystem::path &p) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
550 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
551 if (!fd_ || giveup_) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
552 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
553 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
554 int wd = inotify_add_watch(fd_.get(), p.string().c_str(), IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE_SELF); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
555 if (wd < 0) { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
556 /* Don't even try to watch any more */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
557 giveup_ = true; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
558 return false; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
559 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
560 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
561 /* Add to our list; these IDs (should be) unique */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
562 wds_[wd] = p; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
563 return true; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
564 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
565 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
566 void RemoveWatchDescriptor(int wd) |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
567 { |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
568 inotify_rm_watch(fd_.get(), wd); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
569 wds_.erase(wd); |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
570 } |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
571 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
572 /* variables */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
573 FileDescriptor fd_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
574 std::unordered_map<int, std::filesystem::path> wds_; /* watch descriptors */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
575 bool first_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
576 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
577 /* set this variable if we've completely run out of |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
578 * resources (watch descriptors) and need to fall |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
579 * back to std::filesystem or fear ultimate damage */ |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
580 bool giveup_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
581 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
582 struct inotify_event *ev_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
583 std::size_t ev_size_; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
584 }; |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
585 |
|
2f89797b6a44
filesystem: add linux inotify watcher
Paper <paper@tflc.us>
parents:
399
diff
changeset
|
586 using DefaultWatcher = InotifyWatcher; |
| 398 | 587 #else |
| 588 using DefaultWatcher = StdFilesystemWatcher; | |
| 589 #endif | |
| 590 | |
| 393 | 591 IWatcher *GetRecursiveFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
592 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
593 /* .... :) */ |
| 398 | 594 return new DefaultWatcher(opaque, path, handler, true); |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
595 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
596 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
597 IWatcher *GetFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
598 { |
| 398 | 599 return new DefaultWatcher(opaque, path, handler, false); |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
600 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
601 |
| 15 | 602 } // namespace Filesystem |
