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