LCOV - code coverage report
Current view: top level - source/common/filesystem/inotify - watcher_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 21 68 30.9 %
Date: 2024-01-05 06:35:25 Functions: 3 5 60.0 %

          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

Generated by: LCOV version 1.15