]> git.tdb.fi Git - libs/core.git/blobdiff - source/fs/unix/filemonitor.cpp
Add a class for monitoring changes in files
[libs/core.git] / source / fs / unix / filemonitor.cpp
diff --git a/source/fs/unix/filemonitor.cpp b/source/fs/unix/filemonitor.cpp
new file mode 100644 (file)
index 0000000..70cfc4e
--- /dev/null
@@ -0,0 +1,131 @@
+#include <sys/inotify.h>
+#include <msp/core/systemerror.h>
+#include <msp/io/handle_private.h>
+#include "filemonitor.h"
+#include "filemonitor_platform.h"
+
+using namespace std;
+
+namespace Msp {
+namespace FS {
+
+INotify::INotify()
+{
+       *fd = inotify_init();
+       mode = IO::M_READ;
+       set_events(IO::P_INPUT);
+}
+
+INotify::~INotify()
+{
+       IO::sys_close(fd);
+}
+
+int INotify::add_watch(const FS::Path &path, int ev)
+{
+       int ret = inotify_add_watch(*fd, path.c_str(), ev);
+       if(ret==-1)
+               throw system_error("inotify_add_watch");
+       return ret;
+}
+
+void INotify::remove_watch(int wd)
+{
+       int ret = inotify_rm_watch(*fd, wd);
+       if(ret==-1)
+               throw system_error("inotify_rm_watch");
+}
+
+unsigned INotify::do_write(const char *, unsigned)
+{
+       check_access(IO::M_WRITE);
+       return 0;
+}
+
+unsigned INotify::do_read(char *buf, unsigned size)
+{
+       return IO::sys_read(fd, buf, size);
+}
+
+
+void FileMonitor::platform_use_event_dispatcher()
+{
+       event_disp->add(priv->inotify);
+}
+
+void FileMonitor::prepare_file(MonitoredFile &file)
+{
+       file.tag = priv->inotify.add_watch(file.path, IN_MODIFY|IN_CLOSE_WRITE|IN_DELETE_SELF);
+}
+
+void FileMonitor::cleanup_file(MonitoredFile &file)
+{
+       if(file.tag!=-1)
+               priv->inotify.remove_watch(file.tag);
+}
+
+void FileMonitor::tick()
+{
+       bool first = true;
+       while(1)
+       {
+               if(!first && !IO::poll(priv->inotify, IO::P_INPUT, Time::zero))
+                       break;
+
+               first = false;
+               priv->events_available();
+       }
+}
+
+void FileMonitor::tick(const Time::TimeDelta &timeout)
+{
+       if(IO::poll(priv->inotify, IO::P_INPUT, timeout))
+               tick();
+}
+
+
+FileMonitor::Private::Private(FileMonitor &m):
+       monitor(m)
+{
+       inotify.signal_data_available.connect(sigc::mem_fun(this, &Private::events_available));
+}
+
+void FileMonitor::Private::events_available()
+{
+       vector<FS::Path> changed_files;
+       char event_buf[sizeof(struct inotify_event)+NAME_MAX+1];
+       unsigned len = inotify.read(event_buf, sizeof(event_buf));
+       for(unsigned i=0; i<len; )
+       {
+               struct inotify_event *event = reinterpret_cast<struct inotify_event *>(event_buf+i);
+               for(vector<MonitoredFile>::iterator j=monitor.files.begin(); j!=monitor.files.end(); ++j)
+                       if(j->tag==event->wd)
+                       {
+                               if(event->mask&IN_MODIFY)
+                                       j->modified = true;
+                               if(((event->mask&IN_CLOSE_WRITE) && j->modified) || (event->mask&IN_DELETE_SELF))
+                               {
+                                       j->modified = false;
+                                       changed_files.push_back(j->path);
+                               }
+                               if(event->mask&IN_IGNORED)
+                                       j->tag = -1;
+                               break;
+                       }
+               i += sizeof(struct inotify_event)+event->len;
+       }
+
+       for(vector<FS::Path>::const_iterator i=changed_files.begin(); i!=changed_files.end(); ++i)
+               monitor.signal_file_modified.emit(*i);
+
+       for(vector<MonitoredFile>::iterator j=monitor.files.begin(); j!=monitor.files.end(); )
+       {
+               if(j->tag==-1)
+                       monitor.files.erase(j++);
+               else
+                       ++j;
+       }
+}
+
+} // namespace FS
+} // namespace Msp