2016-12-05 17:10:12 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <atomic>
|
|
|
|
#include <sys/inotify.h>
|
|
|
|
#include <chrono>
|
2016-12-05 20:32:04 +08:00
|
|
|
#include <thread>
|
2016-12-05 17:10:12 +08:00
|
|
|
// TODO: remove experimental from here once that becomes possible
|
|
|
|
#include <experimental/filesystem>
|
|
|
|
|
|
|
|
#include "utils/exceptions/basic_exception.hpp"
|
|
|
|
#include "utils/exceptions/not_yet_implemented.hpp"
|
|
|
|
|
|
|
|
// TODO: remove experimental from here once it becomes possible
|
|
|
|
namespace fs = std::experimental::filesystem;
|
|
|
|
|
|
|
|
namespace utils
|
|
|
|
{
|
|
|
|
|
|
|
|
using ms = std::chrono::milliseconds;
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* File System Event Types
|
|
|
|
*/
|
2016-12-05 17:10:12 +08:00
|
|
|
enum class FSEvent : int
|
|
|
|
{
|
|
|
|
Created = 0x1,
|
|
|
|
Modified = 0x2,
|
|
|
|
Deleted = 0x4
|
|
|
|
};
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* Custom exception
|
|
|
|
*/
|
2016-12-05 17:10:12 +08:00
|
|
|
class FSWatcherException : public BasicException
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using BasicException::BasicException;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* File System Watcher
|
|
|
|
*
|
2016-12-05 20:32:04 +08:00
|
|
|
* The idea is to create wrapper of inotify or any other file system
|
|
|
|
* notificatino system.
|
|
|
|
*
|
2016-12-05 17:10:12 +08:00
|
|
|
* parameters:
|
2016-12-05 20:32:04 +08:00
|
|
|
* * interval - time between two check for the new file system events
|
2016-12-05 17:10:12 +08:00
|
|
|
*/
|
|
|
|
class FSWatcher
|
|
|
|
{
|
2016-12-05 20:32:04 +08:00
|
|
|
// watch descriptor type
|
2016-12-05 17:10:12 +08:00
|
|
|
using wd_t = int;
|
2016-12-05 20:32:04 +08:00
|
|
|
|
|
|
|
// callback type (the code that will be notified will be notified
|
|
|
|
// through callback of this type
|
2016-12-05 17:10:12 +08:00
|
|
|
using callback_t = std::function<void(fs::path &path, FSEvent event)>;
|
2016-12-05 20:32:04 +08:00
|
|
|
|
|
|
|
// storage type for all subscribers
|
2016-12-05 17:10:12 +08:00
|
|
|
// <path, watch descriptor, callback>
|
|
|
|
using entry_t = std::tuple<fs::path, wd_t, callback_t>;
|
|
|
|
|
|
|
|
public:
|
2016-12-05 20:32:04 +08:00
|
|
|
FSWatcher(ms interval = ms(100)) : interval_(interval) { init(); }
|
2016-12-05 17:10:12 +08:00
|
|
|
~FSWatcher() = default;
|
|
|
|
|
|
|
|
// copy and move constructors and assignemnt operators are deleted because
|
|
|
|
// std::atomic can't be copied or moved
|
|
|
|
FSWatcher(const FSWatcher &other) = delete;
|
|
|
|
FSWatcher(FSWatcher &&other) = delete;
|
|
|
|
FSWatcher &operator=(const FSWatcher &) = delete;
|
|
|
|
FSWatcher &operator=(FSWatcher &&) = delete;
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* Initialize file system notification system.
|
|
|
|
*/
|
2016-12-05 17:10:12 +08:00
|
|
|
void init()
|
|
|
|
{
|
|
|
|
inotify_fd_ = inotify_init();
|
|
|
|
if (inotify_fd_ == -1)
|
|
|
|
throw FSWatcherException("Unable to initialize inotify");
|
|
|
|
}
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* Add subscriber
|
|
|
|
*
|
|
|
|
* parameters:
|
|
|
|
* * path: path to a file which will be monitored
|
|
|
|
* * type: type of events
|
|
|
|
* * callback: callback that will be called on specified event type
|
|
|
|
*/
|
|
|
|
void watch(const fs::path &path, FSEvent, callback_t callback)
|
2016-12-05 17:10:12 +08:00
|
|
|
{
|
2016-12-05 20:32:04 +08:00
|
|
|
// TODO: instead IN_ALL_EVENTS pass FSEvent
|
2016-12-05 17:10:12 +08:00
|
|
|
int wd = inotify_add_watch(inotify_fd_, path.c_str(), IN_ALL_EVENTS);
|
|
|
|
if (wd == -1)
|
|
|
|
throw FSWatcherException("Unable to add watch");
|
|
|
|
entries_.emplace_back(std::make_tuple(path, wd, callback));
|
|
|
|
}
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* Remove subscriber on specified path and type
|
|
|
|
*/
|
|
|
|
void unwatch(const fs::path &path, FSEvent)
|
2016-12-05 17:10:12 +08:00
|
|
|
{
|
|
|
|
// iterate through all entries and remove specified watch descriptor
|
|
|
|
for (auto &entry : entries_)
|
|
|
|
{
|
|
|
|
// if paths are not equal pass
|
|
|
|
if (std::get<fs::path>(entry) != path)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// get watch descriptor and remove it from the watching
|
|
|
|
auto status = inotify_rm_watch(inotify_fd_, std::get<wd_t>(entry));
|
|
|
|
if (status == -1)
|
|
|
|
throw FSWatcherException("Unable to remove watch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
/*
|
|
|
|
* Remove all subscribers.
|
|
|
|
*/
|
2016-12-05 17:10:12 +08:00
|
|
|
void unwatchAll()
|
|
|
|
{
|
|
|
|
// iterate through all entries and remove all watch descriptors
|
|
|
|
for (auto &entry : entries_)
|
|
|
|
{
|
|
|
|
auto status = inotify_rm_watch(inotify_fd_, std::get<wd_t>(entry));
|
|
|
|
if (status == -1)
|
|
|
|
throw FSWatcherException("Unable to remove watch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start watching
|
|
|
|
*/
|
|
|
|
void start()
|
|
|
|
{
|
2016-12-05 20:32:04 +08:00
|
|
|
throw NotYetImplemented("FSWatch::start");
|
2016-12-05 17:10:12 +08:00
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
is_running_.store(true);
|
2016-12-05 17:10:12 +08:00
|
|
|
|
2016-12-05 20:32:04 +08:00
|
|
|
dispatch_thread_ = std::thread([this]() {
|
|
|
|
while (is_running_.load()) {
|
|
|
|
std::this_thread::sleep_for(interval_);
|
|
|
|
// TODO implement file system event processing
|
|
|
|
}
|
|
|
|
});
|
2016-12-05 17:10:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop watching
|
|
|
|
*/
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
is_running_.store(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int inotify_fd_;
|
|
|
|
std::atomic<bool> is_running_;
|
|
|
|
ms interval_;
|
|
|
|
std::vector<entry_t> entries_;
|
2016-12-05 20:32:04 +08:00
|
|
|
std::thread dispatch_thread_;
|
2016-12-05 17:10:12 +08:00
|
|
|
};
|
|
|
|
}
|