]> git.tdb.fi Git - libs/core.git/blob - source/fs/unix/filemonitor.cpp
Use C++11 features with containers
[libs/core.git] / source / fs / unix / filemonitor.cpp
1 #include <sys/inotify.h>
2 #include <linux/limits.h>
3 #include <msp/core/algorithm.h>
4 #include <msp/core/systemerror.h>
5 #include <msp/io/handle_private.h>
6 #include "filemonitor.h"
7 #include "filemonitor_platform.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace FS {
13
14 INotify::INotify()
15 {
16         *fd = inotify_init();
17         mode = IO::M_READ;
18         set_events(IO::P_INPUT);
19 }
20
21 INotify::~INotify()
22 {
23         IO::sys_close(fd);
24 }
25
26 int INotify::add_watch(const FS::Path &path, int ev)
27 {
28         int ret = inotify_add_watch(*fd, path.c_str(), ev);
29         if(ret==-1)
30                 throw system_error("inotify_add_watch");
31         return ret;
32 }
33
34 void INotify::remove_watch(int wd)
35 {
36         int ret = inotify_rm_watch(*fd, wd);
37         if(ret==-1)
38                 throw system_error("inotify_rm_watch");
39 }
40
41 size_t INotify::do_write(const char *, size_t)
42 {
43         check_access(IO::M_WRITE);
44         return 0;
45 }
46
47 size_t INotify::do_read(char *buf, size_t size)
48 {
49         return IO::sys_read(fd, buf, size);
50 }
51
52
53 void FileMonitor::platform_use_event_dispatcher()
54 {
55         event_disp->add(priv->inotify);
56 }
57
58 void FileMonitor::prepare_file(MonitoredFile &file)
59 {
60         file.tag = priv->inotify.add_watch(file.path, IN_MODIFY|IN_CLOSE_WRITE|IN_DELETE_SELF);
61 }
62
63 void FileMonitor::cleanup_file(MonitoredFile &file)
64 {
65         if(file.tag!=-1)
66                 priv->inotify.remove_watch(file.tag);
67 }
68
69 void FileMonitor::tick()
70 {
71         bool first = true;
72         while(1)
73         {
74                 if(!first && !IO::poll(priv->inotify, IO::P_INPUT, Time::zero))
75                         break;
76
77                 first = false;
78                 priv->events_available();
79         }
80 }
81
82 void FileMonitor::tick(const Time::TimeDelta &timeout)
83 {
84         if(IO::poll(priv->inotify, IO::P_INPUT, timeout))
85                 tick();
86 }
87
88
89 FileMonitor::Private::Private(FileMonitor &m):
90         monitor(m)
91 {
92         inotify.signal_data_available.connect(sigc::mem_fun(this, &Private::events_available));
93 }
94
95 void FileMonitor::Private::events_available()
96 {
97         vector<FS::Path> changed_files;
98         char event_buf[sizeof(struct inotify_event)+NAME_MAX+1];
99         unsigned len = inotify.read(event_buf, sizeof(event_buf));
100         for(unsigned i=0; i<len; )
101         {
102                 struct inotify_event *event = reinterpret_cast<struct inotify_event *>(event_buf+i);
103                 auto j = find_member(monitor.files, event->wd, &MonitoredFile::tag);
104                 if(j!=monitor.files.end())
105                 {
106                         if(event->mask&IN_MODIFY)
107                                 j->modified = true;
108                         if(((event->mask&IN_CLOSE_WRITE) && j->modified) || (event->mask&IN_DELETE_SELF))
109                         {
110                                 j->modified = false;
111                                 changed_files.push_back(j->path);
112                         }
113                         if(event->mask&IN_IGNORED)
114                                 j->tag = -1;
115                 }
116                 i += sizeof(struct inotify_event)+event->len;
117         }
118
119         for(const FS::Path &p: changed_files)
120                 monitor.signal_file_modified.emit(p);
121
122         for(auto j=monitor.files.begin(); j!=monitor.files.end(); )
123         {
124                 if(j->tag==-1)
125                         monitor.files.erase(j++);
126                 else
127                         ++j;
128         }
129 }
130
131 } // namespace FS
132 } // namespace Msp