Created
February 11, 2014 05:21
-
-
Save alexeiz/8929643 to your computer and use it in GitHub Desktop.
Watch for inode changes with <sys/inotify.h>.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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