forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathwatcher_impl.cc
More file actions
126 lines (108 loc) · 4.45 KB
/
watcher_impl.cc
File metadata and controls
126 lines (108 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <sys/inotify.h>
#include <cstdint>
#include <string>
#include "envoy/api/api.h"
#include "envoy/common/exception.h"
#include "envoy/event/dispatcher.h"
#include "envoy/event/file_event.h"
#include "source/common/common/assert.h"
#include "source/common/common/fmt.h"
#include "source/common/common/thread.h"
#include "source/common/common/utility.h"
#include "source/common/filesystem/watcher_impl.h"
namespace Envoy {
namespace Filesystem {
WatcherImpl::WatcherImpl(Event::Dispatcher& dispatcher, Filesystem::Instance& file_system)
: file_system_(file_system) {
inotify_fd_ = inotify_init1(IN_NONBLOCK);
RELEASE_ASSERT(inotify_fd_ >= 0, "Consider increasing value of fs.inotify.max_user_watches "
"and/or fs.inotify.max_user_instances via sysctl");
inotify_event_ = dispatcher.createFileEvent(
inotify_fd_,
[this](uint32_t events) {
ASSERT(events == Event::FileReadyType::Read);
return onInotifyEvent();
},
Event::FileTriggerType::Edge, Event::FileReadyType::Read);
}
WatcherImpl::~WatcherImpl() { close(inotify_fd_); }
absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) {
// Because of general inotify pain, we always watch the directory that the file lives in,
// and then synthetically raise per file events.
auto result_or_error = file_system_.splitPathFromFilename(path);
RETURN_IF_NOT_OK_REF(result_or_error.status());
const PathSplitResult result = result_or_error.value();
const uint32_t watch_mask = IN_MODIFY | IN_MOVED_TO;
int watch_fd = inotify_add_watch(inotify_fd_, std::string(result.directory_).c_str(), watch_mask);
if (watch_fd == -1) {
return absl::InvalidArgumentError(
fmt::format("unable to add filesystem watch for file {}: {}", path, errorDetails(errno)));
}
ENVOY_LOG(debug, "added watch for directory: '{}' file: '{}' fd: {}", result.directory_,
result.file_, watch_fd);
callback_map_[watch_fd].watches_.push_back({std::string(result.file_), events, callback});
return absl::OkStatus();
}
void WatcherImpl::callAndLogOnError(OnChangedCb& cb, uint32_t events, const std::string& file) {
TRY_ASSERT_MAIN_THREAD {
const absl::Status status = cb(events);
if (!status.ok()) {
ENVOY_LOG(warn, "Filesystem watch callback for '{}' returned error: {}", file,
status.message());
}
}
END_TRY
MULTI_CATCH(
const std::exception& e,
{
ENVOY_LOG(warn, "Filesystem watch callback for '{}' threw exception: {}", file, e.what());
},
{ ENVOY_LOG(warn, "Filesystem watch callback for '{}' threw unknown exception", file); });
}
absl::Status WatcherImpl::onInotifyEvent() {
while (true) {
// The buffer needs to be suitably aligned to store the first inotify_event structure.
// If there are multiple events returned by the read call, the kernel is responsible for
// properly aligning subsequent inotify_event structures (per `man inotify`).
alignas(inotify_event) uint8_t buffer[sizeof(inotify_event) + NAME_MAX + 1];
ssize_t rc = read(inotify_fd_, &buffer, sizeof(buffer));
if (rc == -1 && errno == EAGAIN) {
return absl::OkStatus();
}
RELEASE_ASSERT(rc >= 0, "");
const size_t event_count = rc;
size_t index = 0;
while (index < event_count) {
auto* file_event = reinterpret_cast<inotify_event*>(&buffer[index]);
ASSERT(callback_map_.count(file_event->wd) == 1);
std::string file;
if (file_event->len > 0) {
file.assign(file_event->name);
}
ENVOY_LOG(debug, "notification: fd: {} mask: {:x} file: {}", file_event->wd, file_event->mask,
file);
uint32_t events = 0;
if (file_event->mask & IN_MODIFY) {
events |= Events::Modified;
}
if (file_event->mask & IN_MOVED_TO) {
events |= Events::MovedTo;
}
for (FileWatch& watch : callback_map_[file_event->wd].watches_) {
if (watch.events_ & events) {
if (watch.file_ == file) {
ENVOY_LOG(debug, "matched callback: file: {}", file);
callAndLogOnError(watch.cb_, events, file);
} else if (watch.file_.empty()) {
ENVOY_LOG(debug, "matched callback: directory: {}", file);
callAndLogOnError(watch.cb_, events, file);
}
}
}
index += sizeof(inotify_event) + file_event->len;
}
}
return absl::OkStatus();
}
} // namespace Filesystem
} // namespace Envoy