Skip to content

Instantly share code, notes, and snippets.

@alexeiz
Created February 11, 2014 05:21
Show Gist options
  • Save alexeiz/8929643 to your computer and use it in GitHub Desktop.
Save alexeiz/8929643 to your computer and use it in GitHub Desktop.
Watch for inode changes with <sys/inotify.h>.
#include <boost/exception/all.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator.hpp>
#include <exception>
#include <string>
#include <iostream>
#include <array>
#include <tuple>
#include <sys/inotify.h>
struct errinfo_message;
using message_info = boost::error_info<errinfo_message, std::string>;
struct system_error
: virtual boost::exception
, virtual std::exception
{};
#define throw_system_error(message) \
BOOST_THROW_EXCEPTION(system_error() \
<< message_info(message) \
<< boost::errinfo_errno(errno)) \
class inotify_event_iterator
: public boost::iterator_facade<inotify_event_iterator,
inotify_event const,
boost::forward_traversal_tag>
{
public:
inotify_event_iterator()
: event_{nullptr}
, event_end_{nullptr}
{}
explicit
inotify_event_iterator(char const * ev_start, char const * ev_end)
: event_{ev_start}
, event_end_{ev_end}
{
check_end();
}
private:
void check_end()
{
if (event_ >= event_end_)
event_ = event_end_ = nullptr;
}
private:
friend class boost::iterator_core_access;
void increment()
{
event_ += sizeof(inotify_event) + dereference().len;
check_end();
}
inotify_event const & dereference() const
{
return *reinterpret_cast<inotify_event const *>(event_);
}
bool equal(inotify_event_iterator const & other) const
{
return event_ == other.event_;
}
private:
char const * event_;
char const * event_end_;
};
class file_notif
{
public:
using event = std::tuple<int, std::string>;
public:
file_notif(std::string const & path)
: notif_fd_{inotify_init()}
{
if (notif_fd_ == -1)
throw_system_error("inotify_init failed");
watch_fd_ = inotify_add_watch(notif_fd_,
path.c_str(),
IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF);
if (watch_fd_ == -1)
throw_system_error("inotify_add_watch failed");
}
~file_notif()
{
inotify_rm_watch(notif_fd_, watch_fd_);
}
std::vector<event> wait() const
{
std::size_t const buf_len = 1024;
std::array<char, buf_len> buf;
ssize_t numread = read(notif_fd_, &buf[0], buf.size());
std::vector<event> events;
for (auto & ev : boost::make_iterator_range(inotify_event_iterator{&buf[0], &buf[0] + numread},
inotify_event_iterator{}))
{
events.emplace_back(ev.mask, ev.name);
}
return events;
}
private:
int notif_fd_;
int watch_fd_;
};
int main(int argc, char * argv[])
try
{
file_notif fn{argv[1]};
for (;;)
{
std::vector<file_notif::event> events = fn.wait();
for (auto & ev : events)
{
std::cout << std::get<0>(ev)
<< " : "
<< std::get<1>(ev)
<< std::endl;
}
}
}
catch (boost::exception & e)
{
std::cerr << boost::diagnostic_information(e) << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment