1 | #include <sys/stat.h>
|
---|
2 | #include "WasmBackend.hh"
|
---|
3 |
|
---|
4 | #define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
|
---|
5 |
|
---|
6 | void WasmBackend::start() {
|
---|
7 | notifyStarted();
|
---|
8 | }
|
---|
9 |
|
---|
10 | void WasmBackend::subscribe(WatcherRef watcher) {
|
---|
11 | // Build a full directory tree recursively, and watch each directory.
|
---|
12 | std::shared_ptr<DirTree> tree = getTree(watcher);
|
---|
13 |
|
---|
14 | for (auto it = tree->entries.begin(); it != tree->entries.end(); it++) {
|
---|
15 | if (it->second.isDir) {
|
---|
16 | watchDir(watcher, it->second.path, tree);
|
---|
17 | }
|
---|
18 | }
|
---|
19 | }
|
---|
20 |
|
---|
21 | void WasmBackend::watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree) {
|
---|
22 | int wd = wasm_backend_add_watch(path.c_str(), (void *)this);
|
---|
23 | std::shared_ptr<WasmSubscription> sub = std::make_shared<WasmSubscription>();
|
---|
24 | sub->tree = tree;
|
---|
25 | sub->path = path;
|
---|
26 | sub->watcher = watcher;
|
---|
27 | mSubscriptions.emplace(wd, sub);
|
---|
28 | }
|
---|
29 |
|
---|
30 | extern "C" void wasm_backend_event_handler(void *backend, int wd, int type, char *filename) {
|
---|
31 | WasmBackend *b = (WasmBackend *)(backend);
|
---|
32 | b->handleEvent(wd, type, filename);
|
---|
33 | }
|
---|
34 |
|
---|
35 | void WasmBackend::handleEvent(int wd, int type, char *filename) {
|
---|
36 | // Find the subscriptions for this watch descriptor
|
---|
37 | auto range = mSubscriptions.equal_range(wd);
|
---|
38 | std::unordered_set<std::shared_ptr<WasmSubscription>> set;
|
---|
39 | for (auto it = range.first; it != range.second; it++) {
|
---|
40 | set.insert(it->second);
|
---|
41 | }
|
---|
42 |
|
---|
43 | for (auto it = set.begin(); it != set.end(); it++) {
|
---|
44 | if (handleSubscription(type, filename, *it)) {
|
---|
45 | (*it)->watcher->notify();
|
---|
46 | }
|
---|
47 | }
|
---|
48 | }
|
---|
49 |
|
---|
50 | bool WasmBackend::handleSubscription(int type, char *filename, std::shared_ptr<WasmSubscription> sub) {
|
---|
51 | // Build full path and check if its in our ignore list.
|
---|
52 | WatcherRef watcher = sub->watcher;
|
---|
53 | std::string path = std::string(sub->path);
|
---|
54 |
|
---|
55 | if (filename[0] != '\0') {
|
---|
56 | path += "/" + std::string(filename);
|
---|
57 | }
|
---|
58 |
|
---|
59 | if (watcher->isIgnored(path)) {
|
---|
60 | return false;
|
---|
61 | }
|
---|
62 |
|
---|
63 | if (type == 1) {
|
---|
64 | struct stat st;
|
---|
65 | stat(path.c_str(), &st);
|
---|
66 | sub->tree->update(path, CONVERT_TIME(st.st_mtim));
|
---|
67 | watcher->mEvents.update(path);
|
---|
68 | } else if (type == 2) {
|
---|
69 | // Determine if this is a create or delete depending on if the file exists or not.
|
---|
70 | struct stat st;
|
---|
71 | if (lstat(path.c_str(), &st)) {
|
---|
72 | // If the entry being deleted/moved is a directory, remove it from the list of subscriptions
|
---|
73 | DirEntry *entry = sub->tree->find(path);
|
---|
74 | if (!entry) {
|
---|
75 | return false;
|
---|
76 | }
|
---|
77 |
|
---|
78 | if (entry->isDir) {
|
---|
79 | std::string pathStart = path + DIR_SEP;
|
---|
80 | for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
---|
81 | if (it->second->path == path || it->second->path.rfind(pathStart, 0) == 0) {
|
---|
82 | wasm_backend_remove_watch(it->first);
|
---|
83 | it = mSubscriptions.erase(it);
|
---|
84 | } else {
|
---|
85 | ++it;
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | // Remove all sub-entries
|
---|
90 | for (auto it = sub->tree->entries.begin(); it != sub->tree->entries.end();) {
|
---|
91 | if (it->first.rfind(pathStart, 0) == 0) {
|
---|
92 | watcher->mEvents.remove(it->first);
|
---|
93 | it = sub->tree->entries.erase(it);
|
---|
94 | } else {
|
---|
95 | it++;
|
---|
96 | }
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | watcher->mEvents.remove(path);
|
---|
101 | sub->tree->remove(path);
|
---|
102 | } else if (sub->tree->find(path)) {
|
---|
103 | sub->tree->update(path, CONVERT_TIME(st.st_mtim));
|
---|
104 | watcher->mEvents.update(path);
|
---|
105 | } else {
|
---|
106 | watcher->mEvents.create(path);
|
---|
107 |
|
---|
108 | // If this is a create, check if it's a directory and start watching if it is.
|
---|
109 | DirEntry *entry = sub->tree->add(path, CONVERT_TIME(st.st_mtim), S_ISDIR(st.st_mode));
|
---|
110 | if (entry->isDir) {
|
---|
111 | watchDir(watcher, path, sub->tree);
|
---|
112 | }
|
---|
113 | }
|
---|
114 | }
|
---|
115 |
|
---|
116 | return true;
|
---|
117 | }
|
---|
118 |
|
---|
119 | void WasmBackend::unsubscribe(WatcherRef watcher) {
|
---|
120 | // Find any subscriptions pointing to this watcher, and remove them.
|
---|
121 | for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
|
---|
122 | if (it->second->watcher.get() == watcher.get()) {
|
---|
123 | if (mSubscriptions.count(it->first) == 1) {
|
---|
124 | wasm_backend_remove_watch(it->first);
|
---|
125 | }
|
---|
126 |
|
---|
127 | it = mSubscriptions.erase(it);
|
---|
128 | } else {
|
---|
129 | it++;
|
---|
130 | }
|
---|
131 | }
|
---|
132 | }
|
---|