Skip to content

Instantly share code, notes, and snippets.

@royratcliffe
Last active February 12, 2024 07:51
Show Gist options
  • Save royratcliffe/8a8f383386d55ba63812ad076f669812 to your computer and use it in GitHub Desktop.
Save royratcliffe/8a8f383386d55ba63812ad076f669812 to your computer and use it in GitHub Desktop.
inotifyxx
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/inotifyxx",
"args": [
"."
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
// https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/debug-launch.md
{
"name": "(ctest) Launch",
"type": "cppdbg",
"request": "launch",
"cwd": "${cmake.testWorkingDirectory}",
"program": "${cmake.testProgram}",
"args": [
"${cmake.testArgs}"
]
}
]
}
{
"files.associations": {
"map": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cwchar": "cpp",
"string": "cpp",
"exception": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"new": "cpp",
"ostream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"string_view": "cpp",
"array": "cpp",
"atomic": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"system_error": "cpp",
"iostream": "cpp",
"limits": "cpp",
"numbers": "cpp",
"typeinfo": "cpp"
},
"cmake.configureOnOpen": true,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"editor.renderFinalNewline": "dimmed"
}
cmake_minimum_required (VERSION 3.28)
project (inotifyxx)
add_executable (inotifyxx inotifyxx.cc)
set_property (TARGET inotifyxx PROPERTY CXX_STANDARD 20)
include (CTest)
create_test_sourcelist (Tests
tests.c
inotifyxx_test.cc
)
add_executable (tests ${Tests})
set_property (TARGET tests PROPERTY CXX_STANDARD 20)
set (TestsToRun ${Tests})
remove (TestsToRun tests.c)
foreach (test ${TestsToRun})
get_filename_component (TName ${test} NAME_WE)
add_test (NAME ${TName} COMMAND tests ${TName})
endforeach ()
#include "inotifyxx.hh"
#include <iostream>
int main(int argc, char **argv) {
sys::inotify inotify;
for (int optind = 1; optind < argc; optind++)
inotify.add_watch(argv[optind]);
for (;;)
if (inotify.poll(POLLIN, 1000) & POLLIN)
for (auto event : inotify.read()) {
std::cout << event.wd << "/" << event.name;
for (auto in : event.mask_to_strings())
std::cout << " " << in;
std::cout << std::endl;
}
return EXIT_SUCCESS;
}
#include <bit>
#include <bitset>
#include <cerrno>
#include <map>
#include <system_error>
#include <unordered_map>
#include <vector>
extern "C" {
#include <poll.h>
#include <sys/inotify.h>
#include <unistd.h>
}
namespace sys {
class inotify {
const int fd_;
std::unordered_map<int, std::string> wds_;
public:
inotify() : fd_{inotify_init1(IN_NONBLOCK)} {
if (fd_ < 0)
throw std::system_error(errno, std::system_category());
}
int add_watch(const std::string &pathname, uint32_t mask = IN_ALL_EVENTS) {
const int wd = inotify_add_watch(fd_, pathname.c_str(), mask);
if (wd < 0)
throw std::system_error(errno, std::system_category());
wds_.insert(std::make_pair(wd, pathname));
return wd;
}
virtual ~inotify() { close(fd_); }
int poll(short events = POLLIN, int timeout = 0) {
pollfd fds = {fd_, events, 0};
int rc = ::poll(&fds, 1, timeout);
if (rc < 0)
throw std::system_error(errno, std::system_category());
// Polling returns a non-zero successful number of pollings. Answer the
// returned events.
return rc == 1 ? fds.revents : 0;
}
struct event {
std::string wd;
uint32_t mask;
std::string name;
auto mask_to_strings() const {
std::vector<std::string> strings;
static const std::map<int, std::string> in{
{std::countr_zero(uint32_t(IN_ACCESS)), "ACCESS"},
{std::countr_zero(uint32_t(IN_MODIFY)), "MODIFY"},
{std::countr_zero(uint32_t(IN_ATTRIB)), "ATTRIB"},
{std::countr_zero(uint32_t(IN_CLOSE_WRITE)), "CLOSE_WRITE"},
{std::countr_zero(uint32_t(IN_CLOSE_NOWRITE)), "CLOSE_NOWRITE"},
{std::countr_zero(uint32_t(IN_OPEN)), "OPEN"},
{std::countr_zero(uint32_t(IN_MOVED_FROM)), "MOVED_FROM"},
{std::countr_zero(uint32_t(IN_MOVED_TO)), "MOVED_TO"},
{std::countr_zero(uint32_t(IN_CREATE)), "CREATE"},
{std::countr_zero(uint32_t(IN_DELETE)), "DELETE"},
{std::countr_zero(uint32_t(IN_DELETE_SELF)), "DELETE_SELF"},
{std::countr_zero(uint32_t(IN_MOVE_SELF)), "MOVE_SELF"},
{std::countr_zero(uint32_t(IN_UNMOUNT)), "UNMOUNT"},
{std::countr_zero(uint32_t(IN_Q_OVERFLOW)), "Q_OVERFLOW"},
{std::countr_zero(uint32_t(IN_IGNORED)), "IGNORED"},
{std::countr_zero(uint32_t(IN_ONLYDIR)), "ONLYDIR"},
{std::countr_zero(uint32_t(IN_DONT_FOLLOW)), "DONT_FOLLOW"},
{std::countr_zero(uint32_t(IN_EXCL_UNLINK)), "EXCL_UNLINK"},
{std::countr_zero(uint32_t(IN_MASK_CREATE)), "MASK_CREATE"},
{std::countr_zero(uint32_t(IN_MASK_ADD)), "MASK_ADD"},
{std::countr_zero(uint32_t(IN_ISDIR)), "ISDIR"},
{std::countr_zero(uint32_t(IN_ONESHOT)), "ONESHOT"}};
std::bitset<32> bitset{mask};
while (bitset.any()) {
auto count = std::countr_zero(bitset.to_ulong());
auto it = in.find(count);
strings.push_back(it == in.end()
? std::string("bit") + std::to_string(count)
: it->second);
bitset.reset(count);
}
return strings;
}
};
std::vector<event> read() {
char buf[BUFSIZ];
auto len = ::read(fd_, buf, sizeof(buf));
if (len < 0)
throw std::system_error(errno, std::system_category());
std::vector<event> events;
const inotify_event *event;
for (auto ptr = buf; ptr < buf + len;
ptr += sizeof(inotify_event) + event->len) {
event = reinterpret_cast<typeof(event)>(ptr);
// Assume that the event name carries a null terminator. The event's total
// length steps over the name and its terminator.
events.push_back(
inotify::event{.wd = wds_[event->wd],
.mask = event->mask,
.name = std::string(event->name)});
}
return events;
}
};
} // namespace sys
#include "inotifyxx.hh"
int inotifyxx_test(int argc, char **argv) { return EXIT_SUCCESS; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment