1 module ws.inotify;
2 
3 
4 import
5     core.sys.linux.sys.inotify,
6     core.sys.posix.sys.time,
7     core.sys.posix.unistd,
8     core.sys.posix.sys.select,
9     core.stdc.errno,
10     std.string,
11     std.stdio,
12     std.algorithm,
13     std.conv,
14     ws.event;
15 
16 
17 version(Posix):
18 
19 __gshared:
20 
21 
22 class Inotify {
23 
24     enum EVENT_BUFFER_LENGTH = (inotify_event.sizeof + 16) * 1024;
25 
26     static int inotify = -1;
27     static timeval timeOut;
28     static fd_set descriptorSet;
29 
30     static Watcher[int] staticWatchers;
31 
32     enum {
33         Add,
34         Remove,
35         Modify
36     }
37 
38 
39     shared static this(){
40         if(inotify >= 0)
41             return;
42         inotify = inotify_init;
43         if(inotify < 0)
44                 throw new Exception("Failed to initialize inotify: %s".format(errno));
45             timeOut.tv_sec = 1;
46             timeOut.tv_usec = 0;
47             FD_ZERO(&descriptorSet);
48     }
49 
50 
51     static class Watcher {
52         this(){
53             event = new Event!(string, string, int);
54         }
55         string directory;
56         Event!(string, string, int) event;
57     }
58 
59 
60     static void watch(string directory, void delegate(string, string, int) event){
61         assert(inotify >= 0);
62         foreach(wd, watcher; staticWatchers){
63             if(watcher.directory == directory){
64                 watcher.event ~= event;
65                 return;
66             }
67         }
68         if(staticWatchers.values.find!(a => a.directory == directory).length){
69             return;
70         }
71         int wd = inotify_add_watch(inotify, directory.toStringz, IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE);
72         if(wd < 0)
73             throw new Exception("inotify error in %s: %s".format(directory, errno));
74         auto watcher = new Watcher;
75         watcher.directory = directory;
76         watcher.event ~= event;
77         staticWatchers[wd] = watcher;
78     }
79 
80     static void update(){
81         FD_SET(inotify, &descriptorSet);
82         int ret = select(inotify + 1, &descriptorSet, null, null, &timeOut);
83         if(ret < 0){
84             perror("select");
85         }else if(FD_ISSET(inotify, &descriptorSet)){
86             ssize_t len, i = 0;
87             byte[EVENT_BUFFER_LENGTH] buff = 0;
88             len = read(inotify, buff.ptr, buff.length);
89             while(i < len){
90                 auto pevent = cast(inotify_event*)&buff[i];
91                 auto watcher = staticWatchers[pevent.wd];
92                 watcher.event(
93                     watcher.directory,
94                     (cast(char*)&pevent.name).to!string,
95                     pevent.mask & IN_CLOSE_WRITE ? Modify
96                         : pevent.mask & (IN_MOVED_TO | IN_CREATE) ? Add
97                         : pevent.mask & (IN_MOVED_FROM | IN_DELETE) ? Remove
98                         : -1);
99                 i += inotify_event.sizeof + pevent.len;
100             }
101         }
102 
103     }
104 
105 }
106