Skip to content

Instantly share code, notes, and snippets.

@giuliomoro
Created February 8, 2024 21:18
Show Gist options
  • Save giuliomoro/8b4c0625845502bd042eddc1e15efe19 to your computer and use it in GitHub Desktop.
Save giuliomoro/8b4c0625845502bd042eddc1e15efe19 to your computer and use it in GitHub Desktop.
inotify watcher
#include <errno.h>
#include <poll.h>
#include <unistd.h>
#include <string.h>
std::vector<InotifyPath> watchPaths(const std::vector<std::string>& paths, unsigned int timeoutMs, int flags)
{
// largely taken from main inotify
std::vector<InotifyPath> files;
std::vector<int> wd;
// Create the file descriptor for accessing the inotify API
int fd = inotify_init1(IN_NONBLOCK);
if (fd == -1) {
perror("inotify_init1");
return {};
}
for(auto& p : paths)
{
int w = inotify_add_watch(fd, p.c_str(), flags);
if(-1 == w) {
fprintf(stderr, "Cannot watch '%s': %s\n",
p.c_str(), strerror(errno));
return {};
} else {
wd.push_back(w);
}
}
// Prepare for polling
nfds_t nfds = 1;
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
// Wait for events
int poll_num = poll(fds, nfds, timeoutMs);
if (poll_num == -1) {
if (errno == EINTR)
return {};
perror("poll");
}
if (poll_num <= 0)
return {};
if (!(fds[0].revents & POLLIN))
return {};
{
/* Read all available inotify events from the file descriptor 'fd'.
wd is the table of watch descriptors for the directories in argv.
argc is the length of wd and argv.
argv is the list of watched directories.
Entry 0 of wd and argv is unused. */
/* Some systems cannot read integer variables if they are not
properly aligned. On other systems, incorrect alignment may
decrease performance. Hence, the buffer used for reading from
the inotify file descriptor should have the same alignment as
struct inotify_event. */
__attribute__ ((aligned(__alignof__(struct inotify_event))))
const struct inotify_event *event;
/* Loop while events can be read from inotify file descriptor. */
while(1) {
/* Read some events. */
char buf[4096];
ssize_t len = read(fd, buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
return {};
}
/* If the nonblocking read() found no events to read, then
it returns -1 with errno set to EAGAIN. In that case,
we exit the loop. */
if (len <= 0)
break;
/* Loop over all events in the buffer */
for (char *ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len)
{
event = (const struct inotify_event *) ptr;
files.push_back({std::string(event->name), event->mask});
}
}
}
close(fd);
return files;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment