05d307055a400a5e627115cde2aade9066dfea28391229df9d40901dbc179030910679c209577aa8ed917abc64df88e2a2d9ab5ebba3971a95b53e6a6a6503 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include <memory>
  2. #include <poll.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <sys/stat.h>
  6. #include "InotifyBackend.hh"
  7. #define INOTIFY_MASK \
  8. IN_ATTRIB | IN_CREATE | IN_DELETE | \
  9. IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | \
  10. IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR | IN_EXCL_UNLINK
  11. #define BUFFER_SIZE 8192
  12. #define CONVERT_TIME(ts) ((uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec)
  13. void InotifyBackend::start() {
  14. // Create a pipe that we will write to when we want to end the thread.
  15. int err = pipe2(mPipe, O_CLOEXEC | O_NONBLOCK);
  16. if (err == -1) {
  17. throw std::runtime_error(std::string("Unable to open pipe: ") + strerror(errno));
  18. }
  19. // Init inotify file descriptor.
  20. mInotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
  21. if (mInotify == -1) {
  22. throw std::runtime_error(std::string("Unable to initialize inotify: ") + strerror(errno));
  23. }
  24. pollfd pollfds[2];
  25. pollfds[0].fd = mPipe[0];
  26. pollfds[0].events = POLLIN;
  27. pollfds[0].revents = 0;
  28. pollfds[1].fd = mInotify;
  29. pollfds[1].events = POLLIN;
  30. pollfds[1].revents = 0;
  31. notifyStarted();
  32. // Loop until we get an event from the pipe.
  33. while (true) {
  34. int result = poll(pollfds, 2, 500);
  35. if (result < 0) {
  36. throw std::runtime_error(std::string("Unable to poll: ") + strerror(errno));
  37. }
  38. if (pollfds[0].revents) {
  39. break;
  40. }
  41. if (pollfds[1].revents) {
  42. handleEvents();
  43. }
  44. }
  45. close(mPipe[0]);
  46. close(mPipe[1]);
  47. close(mInotify);
  48. mEndedSignal.notify();
  49. }
  50. InotifyBackend::~InotifyBackend() {
  51. write(mPipe[1], "X", 1);
  52. mEndedSignal.wait();
  53. }
  54. // This function is called by Backend::watch which takes a lock on mMutex
  55. void InotifyBackend::subscribe(WatcherRef watcher) {
  56. // Build a full directory tree recursively, and watch each directory.
  57. std::shared_ptr<DirTree> tree = getTree(watcher);
  58. for (auto it = tree->entries.begin(); it != tree->entries.end(); it++) {
  59. if (it->second.isDir) {
  60. bool success = watchDir(watcher, it->second.path, tree);
  61. if (!success) {
  62. throw WatcherError(std::string("inotify_add_watch on '") + it->second.path + std::string("' failed: ") + strerror(errno), watcher);
  63. }
  64. }
  65. }
  66. }
  67. bool InotifyBackend::watchDir(WatcherRef watcher, std::string path, std::shared_ptr<DirTree> tree) {
  68. int wd = inotify_add_watch(mInotify, path.c_str(), INOTIFY_MASK);
  69. if (wd == -1) {
  70. return false;
  71. }
  72. std::shared_ptr<InotifySubscription> sub = std::make_shared<InotifySubscription>();
  73. sub->tree = tree;
  74. sub->path = path;
  75. sub->watcher = watcher;
  76. mSubscriptions.emplace(wd, sub);
  77. return true;
  78. }
  79. void InotifyBackend::handleEvents() {
  80. char buf[BUFFER_SIZE] __attribute__ ((aligned(__alignof__(struct inotify_event))));;
  81. struct inotify_event *event;
  82. // Track all of the watchers that are touched so we can notify them at the end of the events.
  83. std::unordered_set<WatcherRef> watchers;
  84. while (true) {
  85. int n = read(mInotify, &buf, BUFFER_SIZE);
  86. if (n < 0) {
  87. if (errno == EAGAIN || errno == EWOULDBLOCK) {
  88. break;
  89. }
  90. throw std::runtime_error(std::string("Error reading from inotify: ") + strerror(errno));
  91. }
  92. if (n == 0) {
  93. break;
  94. }
  95. for (char *ptr = buf; ptr < buf + n; ptr += sizeof(*event) + event->len) {
  96. event = (struct inotify_event *)ptr;
  97. if ((event->mask & IN_Q_OVERFLOW) == IN_Q_OVERFLOW) {
  98. // overflow
  99. continue;
  100. }
  101. handleEvent(event, watchers);
  102. }
  103. }
  104. for (auto it = watchers.begin(); it != watchers.end(); it++) {
  105. (*it)->notify();
  106. }
  107. }
  108. void InotifyBackend::handleEvent(struct inotify_event *event, std::unordered_set<WatcherRef> &watchers) {
  109. std::unique_lock<std::mutex> lock(mMutex);
  110. // Find the subscriptions for this watch descriptor
  111. auto range = mSubscriptions.equal_range(event->wd);
  112. std::unordered_set<std::shared_ptr<InotifySubscription>> set;
  113. for (auto it = range.first; it != range.second; it++) {
  114. set.insert(it->second);
  115. }
  116. for (auto it = set.begin(); it != set.end(); it++) {
  117. if (handleSubscription(event, *it)) {
  118. watchers.insert((*it)->watcher);
  119. }
  120. }
  121. }
  122. bool InotifyBackend::handleSubscription(struct inotify_event *event, std::shared_ptr<InotifySubscription> sub) {
  123. // Build full path and check if its in our ignore list.
  124. std::shared_ptr<Watcher> watcher = sub->watcher;
  125. std::string path = std::string(sub->path);
  126. bool isDir = event->mask & IN_ISDIR;
  127. if (event->len > 0) {
  128. path += "/" + std::string(event->name);
  129. }
  130. if (watcher->isIgnored(path)) {
  131. return false;
  132. }
  133. // If this is a create, check if it's a directory and start watching if it is.
  134. // In any case, keep the directory tree up to date.
  135. if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
  136. watcher->mEvents.create(path);
  137. struct stat st;
  138. // Use lstat to avoid resolving symbolic links that we cannot watch anyway
  139. // https://github.com/parcel-bundler/watcher/issues/76
  140. lstat(path.c_str(), &st);
  141. DirEntry *entry = sub->tree->add(path, CONVERT_TIME(st.st_mtim), S_ISDIR(st.st_mode));
  142. if (entry->isDir) {
  143. bool success = watchDir(watcher, path, sub->tree);
  144. if (!success) {
  145. sub->tree->remove(path);
  146. return false;
  147. }
  148. }
  149. } else if (event->mask & (IN_MODIFY | IN_ATTRIB)) {
  150. watcher->mEvents.update(path);
  151. struct stat st;
  152. stat(path.c_str(), &st);
  153. sub->tree->update(path, CONVERT_TIME(st.st_mtim));
  154. } else if (event->mask & (IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF)) {
  155. bool isSelfEvent = (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF));
  156. // Ignore delete/move self events unless this is the recursive watch root
  157. if (isSelfEvent && path != watcher->mDir) {
  158. return false;
  159. }
  160. // If the entry being deleted/moved is a directory, remove it from the list of subscriptions
  161. // XXX: self events don't have the IN_ISDIR mask
  162. if (isSelfEvent || isDir) {
  163. for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
  164. if (it->second->path == path) {
  165. it = mSubscriptions.erase(it);
  166. } else {
  167. ++it;
  168. }
  169. }
  170. }
  171. watcher->mEvents.remove(path);
  172. sub->tree->remove(path);
  173. }
  174. return true;
  175. }
  176. // This function is called by Backend::unwatch which takes a lock on mMutex
  177. void InotifyBackend::unsubscribe(WatcherRef watcher) {
  178. // Find any subscriptions pointing to this watcher, and remove them.
  179. for (auto it = mSubscriptions.begin(); it != mSubscriptions.end();) {
  180. if (it->second->watcher.get() == watcher.get()) {
  181. if (mSubscriptions.count(it->first) == 1) {
  182. int err = inotify_rm_watch(mInotify, it->first);
  183. if (err == -1) {
  184. throw WatcherError(std::string("Unable to remove watcher: ") + strerror(errno), watcher);
  185. }
  186. }
  187. it = mSubscriptions.erase(it);
  188. } else {
  189. it++;
  190. }
  191. }
  192. }