Line data Source code
1 : #include <sys/inotify.h> 2 : 3 : #include <cstdint> 4 : #include <string> 5 : 6 : #include "envoy/api/api.h" 7 : #include "envoy/common/exception.h" 8 : #include "envoy/event/dispatcher.h" 9 : #include "envoy/event/file_event.h" 10 : 11 : #include "source/common/common/assert.h" 12 : #include "source/common/common/fmt.h" 13 : #include "source/common/common/utility.h" 14 : #include "source/common/filesystem/watcher_impl.h" 15 : 16 : namespace Envoy { 17 : namespace Filesystem { 18 : 19 : WatcherImpl::WatcherImpl(Event::Dispatcher& dispatcher, Filesystem::Instance& file_system) 20 70 : : file_system_(file_system) { 21 70 : inotify_fd_ = inotify_init1(IN_NONBLOCK); 22 70 : RELEASE_ASSERT(inotify_fd_ >= 0, 23 70 : "Consider increasing value of user.max_inotify_watches via sysctl"); 24 70 : inotify_event_ = dispatcher.createFileEvent( 25 70 : inotify_fd_, 26 70 : [this](uint32_t events) -> void { 27 0 : ASSERT(events == Event::FileReadyType::Read); 28 0 : onInotifyEvent(); 29 0 : }, 30 70 : Event::FileTriggerType::Edge, Event::FileReadyType::Read); 31 70 : } 32 : 33 70 : WatcherImpl::~WatcherImpl() { close(inotify_fd_); } 34 : 35 70 : void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) { 36 : // Because of general inotify pain, we always watch the directory that the file lives in, 37 : // and then synthetically raise per file events. 38 70 : auto result_or_error = file_system_.splitPathFromFilename(path); 39 70 : THROW_IF_STATUS_NOT_OK(result_or_error, throw); 40 70 : const PathSplitResult result = result_or_error.value(); 41 : 42 70 : const uint32_t watch_mask = IN_MODIFY | IN_MOVED_TO; 43 70 : int watch_fd = inotify_add_watch(inotify_fd_, std::string(result.directory_).c_str(), watch_mask); 44 70 : if (watch_fd == -1) { 45 0 : throwEnvoyExceptionOrPanic( 46 0 : fmt::format("unable to add filesystem watch for file {}: {}", path, errorDetails(errno))); 47 0 : } 48 : 49 70 : ENVOY_LOG(debug, "added watch for directory: '{}' file: '{}' fd: {}", result.directory_, 50 70 : result.file_, watch_fd); 51 70 : callback_map_[watch_fd].watches_.push_back({std::string(result.file_), events, callback}); 52 70 : } 53 : 54 0 : void WatcherImpl::onInotifyEvent() { 55 0 : while (true) { 56 0 : uint8_t buffer[sizeof(inotify_event) + NAME_MAX + 1]; 57 0 : ssize_t rc = read(inotify_fd_, &buffer, sizeof(buffer)); 58 0 : if (rc == -1 && errno == EAGAIN) { 59 0 : return; 60 0 : } 61 0 : RELEASE_ASSERT(rc >= 0, ""); 62 : 63 0 : const size_t event_count = rc; 64 0 : size_t index = 0; 65 0 : while (index < event_count) { 66 0 : auto* file_event = reinterpret_cast<inotify_event*>(&buffer[index]); 67 0 : ASSERT(callback_map_.count(file_event->wd) == 1); 68 : 69 0 : std::string file; 70 0 : if (file_event->len > 0) { 71 0 : file.assign(file_event->name); 72 0 : } 73 : 74 0 : ENVOY_LOG(debug, "notification: fd: {} mask: {:x} file: {}", file_event->wd, file_event->mask, 75 0 : file); 76 : 77 0 : uint32_t events = 0; 78 0 : if (file_event->mask & IN_MODIFY) { 79 0 : events |= Events::Modified; 80 0 : } 81 0 : if (file_event->mask & IN_MOVED_TO) { 82 0 : events |= Events::MovedTo; 83 0 : } 84 : 85 0 : for (FileWatch& watch : callback_map_[file_event->wd].watches_) { 86 0 : if (watch.events_ & events) { 87 0 : if (watch.file_ == file) { 88 0 : ENVOY_LOG(debug, "matched callback: file: {}", file); 89 0 : watch.cb_(events); 90 0 : } else if (watch.file_.empty()) { 91 0 : ENVOY_LOG(debug, "matched callback: directory: {}", file); 92 0 : watch.cb_(events); 93 0 : } 94 0 : } 95 0 : } 96 : 97 0 : index += sizeof(inotify_event) + file_event->len; 98 0 : } 99 0 : } 100 0 : } 101 : 102 : } // namespace Filesystem 103 : } // namespace Envoy