[0c6b92a] | 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 | }
|
---|