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