1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#include <errno.h> |
6 |
|
|
#include <fcntl.h> |
7 |
|
|
#include <linux/limits.h> |
8 |
|
|
#include <poll.h> |
9 |
|
|
#include <sys/inotify.h> |
10 |
|
|
#include <sys/time.h> |
11 |
|
|
#include <sys/types.h> |
12 |
|
|
#include <unistd.h> |
13 |
|
|
|
14 |
|
|
#include <cassert> |
15 |
|
|
#include <string> |
16 |
|
|
#include <vector> |
17 |
|
|
|
18 |
|
|
#include "file_watcher_inotify.h" |
19 |
|
|
#include "logging.h" |
20 |
|
|
#include "util/posix.h" |
21 |
|
|
|
22 |
|
|
namespace file_watcher { |
23 |
|
|
|
24 |
|
4 |
FileWatcherInotify::FileWatcherInotify() {} |
25 |
|
|
|
26 |
✗✓ |
8 |
FileWatcherInotify::~FileWatcherInotify() {} |
27 |
|
|
|
28 |
|
4 |
bool FileWatcherInotify::RunEventLoop(const FileWatcher::HandlerMap& handlers, |
29 |
|
|
int read_pipe, int write_pipe) { |
30 |
|
4 |
inotify_fd_ = inotify_init1(IN_NONBLOCK); |
31 |
✗✓ |
4 |
assert(inotify_fd_ >= 0); |
32 |
|
|
|
33 |
✓✓ |
8 |
for (FileWatcher::HandlerMap::const_iterator it = handlers.begin(); |
34 |
|
|
it != handlers.end(); ++it) { |
35 |
|
4 |
RegisterFilter(it->first, it->second); |
36 |
|
|
} |
37 |
|
|
|
38 |
|
|
// Use the control pipe to signal readiness to the main thread, |
39 |
|
|
// before continuing with the event loop. |
40 |
|
4 |
WritePipe(write_pipe, "s", 1); |
41 |
|
|
|
42 |
|
|
struct pollfd poll_set[2]; |
43 |
|
|
|
44 |
|
4 |
poll_set[0].fd = read_pipe; |
45 |
|
4 |
poll_set[0].events = POLLHUP | POLLIN; |
46 |
|
4 |
poll_set[0].revents = 0; |
47 |
|
4 |
poll_set[1].fd = inotify_fd_; |
48 |
|
4 |
poll_set[1].events = POLLIN; |
49 |
|
4 |
poll_set[1].revents = 0; |
50 |
|
|
|
51 |
|
4 |
bool stop = false; |
52 |
✓✓ |
17 |
while (!stop) { |
53 |
|
9 |
int ready = poll(poll_set, 2, -1); |
54 |
✗✓ |
9 |
if (ready == -1) { |
55 |
|
|
if (errno == EINTR) { |
56 |
|
|
continue; |
57 |
|
|
} |
58 |
|
|
LogCvmfs(kLogCvmfs, kLogSyslogErr, |
59 |
|
|
"FileWatcherInotify - Could not poll events. Errno: %d", errno); |
60 |
|
|
return false; |
61 |
|
|
} |
62 |
✗✓ |
9 |
if (ready == 0) { |
63 |
|
|
continue; |
64 |
|
|
} |
65 |
|
|
|
66 |
✗✓ |
9 |
if (poll_set[0].revents & POLLHUP) { |
67 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n"); |
68 |
|
|
stop = true; |
69 |
|
|
continue; |
70 |
|
|
} |
71 |
✓✓ |
9 |
if (poll_set[0].revents & POLLIN) { |
72 |
|
|
char buffer[1]; |
73 |
|
4 |
ReadPipe(read_pipe, &buffer, 1); |
74 |
|
4 |
LogCvmfs(kLogCvmfs, kLogDebug, "FileWatcherInotify - Stopping.\n"); |
75 |
|
4 |
stop = true; |
76 |
|
4 |
continue; |
77 |
|
|
} |
78 |
|
|
|
79 |
|
5 |
const size_t event_size = sizeof(struct inotify_event); |
80 |
|
|
// We need a large enough buffer for an event with the largest path name |
81 |
|
5 |
const size_t buffer_size = event_size + PATH_MAX + 1; |
82 |
|
|
char buffer[buffer_size]; |
83 |
✓✗ |
5 |
if (poll_set[1].revents & POLLIN) { |
84 |
|
5 |
int len = read(inotify_fd_, buffer, buffer_size); |
85 |
✗✓ |
5 |
assert(len > 0); |
86 |
|
5 |
int i = 0; |
87 |
✓✓ |
18 |
while (i < len) { |
88 |
|
|
struct inotify_event* inotify_event = |
89 |
|
8 |
reinterpret_cast<struct inotify_event*>(&buffer[i]); |
90 |
|
|
std::map<int, WatchRecord>::const_iterator it = |
91 |
|
8 |
watch_records_.find(inotify_event->wd); |
92 |
✓✓ |
8 |
if (it != watch_records_.end()) { |
93 |
|
6 |
WatchRecord current_record = it->second; |
94 |
|
6 |
file_watcher::Event event = file_watcher::kInvalid; |
95 |
✓✓ |
6 |
if (inotify_event->mask & IN_DELETE_SELF) { |
96 |
|
2 |
event = file_watcher::kDeleted; |
97 |
✓✓ |
4 |
} else if (inotify_event->mask & IN_MODIFY) { |
98 |
|
|
// Modified |
99 |
|
2 |
event = file_watcher::kModified; |
100 |
✗✓ |
2 |
} else if (inotify_event->mask & IN_MOVE_SELF) { |
101 |
|
|
// Renamed |
102 |
|
|
event = file_watcher::kRenamed; |
103 |
✓✗ |
2 |
} else if (inotify_event->mask & IN_ATTRIB) { |
104 |
|
|
// Attributes |
105 |
|
2 |
event = file_watcher::kAttributes; |
106 |
|
|
} else if (inotify_event->mask & IN_IGNORED) { |
107 |
|
|
// An IN_IGNORED event is generated after a file is deleted and the |
108 |
|
|
// watch is removed |
109 |
|
|
event = file_watcher::kIgnored; |
110 |
|
|
} |
111 |
|
6 |
bool clear_handler = true; |
112 |
✓✗✓✗
|
12 |
if (event != file_watcher::kInvalid && |
113 |
|
|
event != file_watcher::kIgnored) { |
114 |
|
|
current_record.handler_->Handle(current_record.file_path_, event, |
115 |
|
6 |
&clear_handler); |
116 |
|
|
} else { |
117 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
118 |
|
|
"FileWatcherInotify - Unknown event 0x%x\n", |
119 |
|
|
inotify_event->mask); |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
// Perform post-handling actions (i.e. remove, reset filter) |
123 |
✓✓ |
6 |
if (event == file_watcher::kDeleted) { |
124 |
|
2 |
watch_records_.erase(inotify_event->wd); |
125 |
✗✓ |
2 |
if (!clear_handler) { |
126 |
|
|
RegisterFilter(current_record.file_path_, |
127 |
|
|
current_record.handler_); |
128 |
|
|
} |
129 |
|
|
} |
130 |
|
|
} else { |
131 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
132 |
|
|
"FileWatcherInotify - Unknown event ident: %ld", |
133 |
|
2 |
inotify_event->wd); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
8 |
i += event_size + inotify_event->len; |
137 |
|
|
} |
138 |
|
|
} |
139 |
|
|
} |
140 |
|
|
|
141 |
|
4 |
watch_records_.clear(); |
142 |
|
|
|
143 |
|
4 |
close(inotify_fd_); |
144 |
|
|
|
145 |
|
4 |
return true; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
4 |
int FileWatcherInotify::TryRegisterFilter(const std::string& file_path) { |
149 |
|
|
return inotify_add_watch( |
150 |
|
|
inotify_fd_, file_path.c_str(), |
151 |
|
4 |
IN_ATTRIB | IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
} // namespace file_watcher |