1 /*******************************************************************************
2 
3     Linux File System event file descriptor.
4     See http://man7.org/linux/man-pages/man7/inotify.7.html
5 
6     Copyright:
7         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8         All rights reserved.
9 
10     License:
11         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12         Alternatively, this file may be distributed under the terms of the Tango
13         3-Clause BSD License (see LICENSE_BSD.txt for details).
14 
15 *******************************************************************************/
16 
17 module ocean.sys.Inotify;
18 
19 import ocean.io.model.IConduit: ISelectable;
20 import ocean.sys.ErrnoException;
21 import ocean.text.util.StringC;
22 
23 import core.stdc.errno: EAGAIN, EWOULDBLOCK, errno;
24 import core.sys.linux.sys.inotify;
25 import core.sys.posix.sys.types: ssize_t;
26 import core.sys.posix.unistd: read, close;
27 
28 /*******************************************************************************
29 
30     Inotify fd class
31 
32 *******************************************************************************/
33 
34 public class Inotify : ISelectable
35 {
36     import ocean.sys.CloseOnExec;
37 
38     /***************************************************************************
39 
40         Exception class, thrown on errors with inotify functions
41 
42     ***************************************************************************/
43 
44     static public class InotifyException : ErrnoException { }
45 
46 
47     /***************************************************************************
48 
49         Inotify exception instance.
50 
51     ***************************************************************************/
52 
53     private InotifyException e;
54 
55 
56     /***************************************************************************
57 
58         Integer file descriptor provided by the operating system and used to
59         manage the Inotify instance.
60 
61     ***************************************************************************/
62 
63     private int fd;
64 
65 
66     /***************************************************************************
67 
68         Struct that enables the reading of events from iterator.
69 
70     ***************************************************************************/
71 
72     private static struct EventsIterator
73     {
74         /***********************************************************************
75 
76             The inotify instance which the events will be fetched
77 
78         ***********************************************************************/
79 
80         private Inotify outer;
81 
82 
83         /***********************************************************************
84 
85             Iterator Operator overload - Iterates per inotify event fetched from
86             the inotify instance of this struct.
87 
88             Returns:
89                 Result of each iteration
90 
91             Throws:
92                 upon failure to read inotify events
93 
94         ***********************************************************************/
95 
96         public int opApply ( scope int delegate ( ref inotify_event* ) dg )
97         {
98             //255 is the default max filename length in linux)
99             char[inotify_event.sizeof + 255 + 1] buffer_temp;
100             void[] buffer = buffer_temp;
101 
102             int result = 0;
103 
104             ssize_t read_bytes;
105             read_loop: while ( (read_bytes = read(this.outer.fd, buffer.ptr, buffer.length)) > 0 )
106             {
107                 inotify_event *i_event;
108 
109                 for ( uint i; i < read_bytes; i += inotify_event.sizeof + i_event.len  )
110                 {
111                     i_event = cast(inotify_event*) &buffer[i];
112                     result = dg(i_event);
113 
114                     if (result)
115                     {
116                         break read_loop;
117                     }
118                 }
119             }
120 
121             return result;
122         }
123     }
124 
125 
126     /***************************************************************************
127 
128         Constructor.
129 
130         Throws:
131             upon failure to create a inotify instance (fd)
132 
133     ***************************************************************************/
134 
135     public this ( )
136     {
137         this(new InotifyException);
138     }
139 
140 
141     /***************************************************************************
142 
143         Constructor. Creates a Inotify file descriptor.
144 
145         Params:
146             e = inotify exception instance to be used internally
147 
148         Throws:
149             upon failure to create a inotify instance (fd)
150 
151     ***************************************************************************/
152 
153     public this ( InotifyException e )
154     {
155         this.e = e;
156 
157         this.fd = this.e.enforceRet!(inotify_init1)(&verify)
158             .call(setCloExec(IN_NONBLOCK, IN_CLOEXEC));
159     }
160 
161 
162     /***************************************************************************
163 
164         Required by ISelectable interface.
165 
166         Returns:
167             file descriptor used to manage inotify event
168 
169     ***************************************************************************/
170 
171     public Handle fileHandle ( )
172     {
173         return cast(Handle)this.fd;
174     }
175 
176 
177     /***************************************************************************
178 
179         Manipulates  the  "watch  list"  associated  with an inotify instance.
180         Each item ("watch") in the watch list specifies the pathname of a file
181         or directory, along with some set of events that the kernel should
182         monitor for the file referred to by that pathname. Either creates a new
183         watch item, or modifies an existing watch. Each watch has a unique
184         "watch descriptor", which is returned by this function.
185 
186         params:
187             path   = File path to watch (directories are also supported)
188             events = Inotify events that will be watched (bit mask)
189 
190         return:
191              Unique "watch descriptor"
192 
193         Throws:
194             upon failure to add the "watch descriptor"
195 
196     ***************************************************************************/
197 
198     public uint addWatch ( char[] path, uint events )
199     {
200         return cast(uint) this.e.enforceRet!(.inotify_add_watch)(&verify)
201                       .call(this.fd, StringC.toCString(path), events);
202     }
203 
204 
205     /***************************************************************************
206 
207         Removes the provided item from the inotify instance.
208 
209         Returns:
210             wd = "watch descriptor" that was removed (is no longer watched)
211 
212         Throws:
213             upon failure to unwatch the "watch descriptor"
214 
215     ***************************************************************************/
216 
217     public uint rmWatch ( int wd )
218     {
219         return cast(uint) this.e.enforceRet!(.inotify_rm_watch)(&verify).call(this.fd, wd);
220     }
221 
222 
223     /***************************************************************************
224 
225         Reads the events associated to the inotify file descriptor, and return
226         in the form of iterator
227 
228         Returns:
229             EventsIterator containing all inotify events
230 
231         Throws:
232             upon failure to read inotify events
233 
234     ***************************************************************************/
235 
236     public EventsIterator readEvents ( )
237     {
238         EventsIterator it;
239         it.outer = this;
240 
241         return it;
242     }
243 
244 
245     /***************************************************************************
246 
247         Handy function to verify the success of system calls
248 
249         Params:
250             fd = returned code by the system call
251 
252         Returns:
253             True in case of success. False, otherwise.
254 
255     ***************************************************************************/
256 
257     private static bool verify ( int fd )
258     {
259         return fd >= 0;
260     }
261 
262 
263     /***************************************************************************
264 
265         Destructor. Destroys the inotify file descriptor.
266 
267     ***************************************************************************/
268 
269     ~this ( )
270     {
271         close(this.fd);
272     }
273 }