set close-on-exec by default under Ruby 2.0
[sleepy_penguin.git] / ext / sleepy_penguin / inotify.c
blob615550765c59bd5992071ca010e09f3c29eb85f8
1 #ifdef HAVE_SYS_INOTIFY_H
2 #include "sleepy_penguin.h"
3 #include <sys/inotify.h>
4 #include <sys/ioctl.h>
5 #include "missing_inotify.h"
7 struct inbuf {
8 size_t capa;
9 void *ptr;
12 static ID id_inotify_tmp, id_mask;
13 static VALUE cEvent, checks;
16 * call-seq:
17 * Inotify.new([flags]) -> Inotify IO object
19 * Flags may be any of the following as an Array of Symbols or Integer mask:
20 * - :NONBLOCK - sets the non-blocking flag on the descriptor watched.
21 * - :CLOEXEC - sets the close-on-exec flag
23 static VALUE s_new(int argc, VALUE *argv, VALUE klass)
25 VALUE _flags, rv;
26 int flags;
27 int fd;
29 rb_scan_args(argc, argv, "01", &_flags);
30 flags = rb_sp_get_flags(klass, _flags, RB_SP_CLOEXEC(IN_CLOEXEC));
32 fd = inotify_init1(flags);
33 if (fd == -1) {
34 if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
35 rb_gc();
36 fd = inotify_init1(flags);
38 if (fd == -1)
39 rb_sys_fail("inotify_init1");
42 rv = INT2FIX(fd);
43 rv = rb_call_super(1, &rv);
44 rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
46 return rv;
50 * call-seq:
51 * ino.add_watch(path, flags) -> Integer
53 * Adds a watch on an object specified by its +mask+, returns an unsigned
54 * Integer watch descriptor. +flags+ may be a mask of the following
55 * Inotify constants or array of their symbolic names.
57 * - :ACCESS - File was accessed (read) (*)
58 * - :ATTRIB - Metadata changed.
59 * - :CLOSE_WRITE - File opened for writing was closed (*)
60 * - :CLOSE_NOWRITE - File not opened for writing was closed (*)
61 * - :CREATE - File/directory created in watched directory (*)
62 * - :DELETE - File/directory deleted from watched directory (*)
63 * - :DELETE_SELF - Watched file/directory was itself deleted
64 * - :MODIFY - File was modified (*)
65 * - :MOVE_SELF - Watched file/directory was itself moved
66 * - :MOVED_FROM - File moved out of watched directory (*)
67 * - :MOVED_TO - File moved into watched directory (*)
68 * - :OPEN - File was opened (*)
70 * When monitoring a directory, the events marked with an asterisk (*)
71 * above can occur for files in the directory, in which case the name
72 * field in the Event structure identifies the name of the file in the
73 * directory.
75 * Shortcut flags:
77 * - :ALL_EVENTS - a bitmask of all the above events
78 * - :MOVE - :MOVED_FROM or :MOVED_TO
79 * - :CLOSE - :CLOSE_WRITE or :CLOSE_NOWRITE
81 * The following watch attributes may also be included in flags:
83 * - :DONT_FOLLOW - don't dereference symlinks (since Linux 2.6.15)
84 * - :EXCL_UNLINK - don't generate unlink events for children (since 2.6.36)
85 * - :MASK_ADD - add events to an existing watch mask if it exists
86 * - :ONESHOT - monitor for one event and then remove it from the watch
87 * - :ONLYDIR - only watch the pathname if it is a directory
89 static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
91 int fd = rb_sp_fileno(self);
92 const char *pathname = StringValueCStr(path);
93 uint32_t mask = rb_sp_get_uflags(self, vmask);
94 int rc = inotify_add_watch(fd, pathname, mask);
96 if (rc == -1)
97 rb_sys_fail("inotify_add_watch");
99 return UINT2NUM((uint32_t)rc);
103 * call-seq:
104 * ino.rm_watch(watch_descriptor) -> 0
106 * Removes a watch based on a watch descriptor Integer. The watch
107 * descriptor is a return value given by Inotify#add_watch
109 static VALUE rm_watch(VALUE self, VALUE vwd)
111 uint32_t wd = NUM2UINT(vwd);
112 int fd = rb_sp_fileno(self);
113 int rc = inotify_rm_watch(fd, wd);
115 if (rc == -1)
116 rb_sys_fail("inotify_rm_watch");
117 return INT2NUM(rc);
120 static size_t event_len(struct inotify_event *e)
122 return sizeof(struct inotify_event) + e->len;
125 static VALUE event_new(struct inotify_event *e)
127 VALUE wd = INT2NUM(e->wd);
128 VALUE mask = UINT2NUM(e->mask);
129 VALUE cookie = UINT2NUM(e->cookie);
130 VALUE name;
132 /* name may be zero-padded, so we do strlen() */
133 name = e->len ? rb_str_new(e->name, strlen(e->name)) : Qnil;
135 return rb_struct_new(cEvent, wd, mask, cookie, name);
138 struct inread_args {
139 int fd;
140 struct inbuf *inbuf;
143 static VALUE inread(void *ptr)
145 struct inread_args *args = ptr;
147 return (VALUE)read(args->fd, args->inbuf->ptr, args->inbuf->capa);
150 static void inbuf_grow(struct inbuf *inbuf, size_t size)
152 int err;
154 if (inbuf->capa >= size)
155 return;
156 free(inbuf->ptr);
157 err = posix_memalign(&inbuf->ptr, rb_sp_l1_cache_line_size, size);
158 if (err) {
159 errno = err;
160 rb_memerror();
162 inbuf->capa = size;
165 static void resize_internal_buffer(struct inread_args *args)
167 int newlen;
169 if (args->inbuf->capa > 0x10000)
170 rb_raise(rb_eRuntimeError, "path too long");
172 if (ioctl(args->fd, FIONREAD, &newlen) != 0)
173 rb_sys_fail("ioctl(inotify,FIONREAD)");
175 if (newlen > 0)
176 inbuf_grow(args->inbuf, (size_t)newlen);
178 if (newlen == 0) /* race: some other thread grabbed the data */
179 return;
181 rb_raise(rb_eRuntimeError,
182 "ioctl(inotify,FIONREAD) returned negative length: %d",
183 newlen);
187 * call-seq:
188 * ino.take([nonblock]) -> Inotify::Event or nil
190 * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
191 * is +true+.
193 static VALUE take(int argc, VALUE *argv, VALUE self)
195 static __thread struct inbuf inbuf;
197 struct inread_args args;
198 VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
199 struct inotify_event *e, *end;
200 ssize_t r;
201 VALUE rv = Qnil;
202 VALUE nonblock;
204 if (RARRAY_LEN(tmp) > 0)
205 return rb_ary_shift(tmp);
207 rb_scan_args(argc, argv, "01", &nonblock);
209 inbuf_grow(&inbuf, 128);
210 args.fd = rb_sp_fileno(self);
211 args.inbuf = &inbuf;
213 if (RTEST(nonblock))
214 rb_sp_set_nonblock(args.fd);
215 else
216 blocking_io_prepare(args.fd);
217 do {
218 r = (ssize_t)rb_sp_fd_region(inread, &args, args.fd);
219 if (r == 0 /* Linux < 2.6.21 */
221 (r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
223 resize_internal_buffer(&args);
224 } else if (r < 0) {
225 if (errno == EAGAIN && RTEST(nonblock))
226 return Qnil;
227 if (!rb_sp_wait(rb_io_wait_readable, self, &args.fd))
228 rb_sys_fail("read(inotify)");
229 } else {
230 /* buffer in userspace to minimize read() calls */
231 end = (struct inotify_event *)
232 ((char *)args.inbuf->ptr + r);
233 for (e = args.inbuf->ptr; e < end; ) {
234 VALUE event = event_new(e);
235 if (NIL_P(rv))
236 rv = event;
237 else
238 rb_ary_push(tmp, event);
239 e = (struct inotify_event *)
240 ((char *)e + event_len(e));
243 } while (NIL_P(rv));
245 return rv;
249 * call-seq:
250 * inotify_event.events => [ :MOVED_TO, ... ]
252 * Returns an array of symbolic event names based on the contents of
253 * the +mask+ field.
255 static VALUE events(VALUE self)
257 long len = RARRAY_LEN(checks);
258 VALUE *ptr = RARRAY_PTR(checks);
259 VALUE sym;
260 VALUE rv = rb_ary_new();
261 uint32_t mask;
262 uint32_t event_mask = NUM2UINT(rb_funcall(self, id_mask, 0));
264 for (; (len -= 2) >= 0;) {
265 sym = *ptr++;
266 mask = NUM2UINT(*ptr++);
267 if ((event_mask & mask) == mask)
268 rb_ary_push(rv, sym);
271 return rv;
275 * call-seq:
276 * ino.each { |event| ... } -> ino
278 * Yields each Inotify::Event received in a blocking fashion.
280 static VALUE each(VALUE self)
282 VALUE argv = Qfalse;
284 while (1)
285 rb_yield(take(0, &argv, self));
287 return self;
290 void sleepy_penguin_init_inotify(void)
292 VALUE mSleepyPenguin, cInotify;
294 mSleepyPenguin = rb_define_module("SleepyPenguin");
297 * Document-class: SleepyPenguin::Inotify
299 * Inotify objects are used for monitoring file system events,
300 * it can monitor individual files or directories. When a directory
301 * is monitored it will return events for the directory itself and
302 * all files inside the directory.
304 * Inotify IO objects can be watched using IO.select or Epoll.
305 * IO#close may be called on the object when it is no longer needed.
307 * Inotify is available on Linux 2.6.13 or later.
309 * require "sleepy_penguin/sp"
310 * ino = SP::Inotify.new
311 * ino.add_watch("/path/to/foo", :OPEN)
312 * ino.each do |event|
313 * p event.events # => [ :OPEN ]
314 * end
316 cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
317 rb_define_method(cInotify, "add_watch", add_watch, 2);
318 rb_define_method(cInotify, "rm_watch", rm_watch, 1);
319 rb_define_method(cInotify, "take", take, -1);
320 rb_define_method(cInotify, "each", each, 0);
323 * Document-class: SleepyPenguin::Inotify::Event
325 * Returned by SleepyPenguin::Inotify#take. It is a Struct with the
326 * following elements:
328 * - wd - watch descriptor (unsigned Integer)
329 * - mask - mask of events (unsigned Integer)
330 * - cookie - unique cookie associated related events (for rename)
331 * - name - optional string name (may be nil)
333 * The mask is a bitmask of the event flags accepted by
334 * Inotify#add_watch and may also include the following flags:
336 * - :IGNORED - watch was removed
337 * - :ISDIR - event occured on a directory
338 * - :Q_OVERFLOW - event queue overflowed (wd is -1)
339 * - :UNMOUNT - filesystem containing watched object was unmounted
341 * Use the Event#events method to get an array of symbols for the
342 * matched events.
344 cEvent = rb_struct_define("Event", "wd", "mask", "cookie", "name", 0);
345 cEvent = rb_define_class_under(cInotify, "Event", cEvent);
346 rb_define_method(cEvent, "events", events, 0);
347 rb_define_singleton_method(cInotify, "new", s_new, -1);
348 id_inotify_tmp = rb_intern("@inotify_tmp");
349 id_mask = rb_intern("mask");
350 checks = rb_ary_new();
351 rb_global_variable(&checks);
352 #define IN(x) rb_define_const(cInotify,#x,UINT2NUM(IN_##x))
353 #define IN2(x) do { \
354 VALUE val = UINT2NUM(IN_##x); \
355 rb_define_const(cInotify,#x,val); \
356 rb_ary_push(checks, ID2SYM(rb_intern(#x))); \
357 rb_ary_push(checks, val); \
358 } while (0)
360 IN(ALL_EVENTS);
362 /* events a user can watch for */
363 IN2(ACCESS);
364 IN2(MODIFY);
365 IN2(ATTRIB);
366 IN2(CLOSE_WRITE);
367 IN2(CLOSE_NOWRITE);
368 IN2(OPEN);
369 IN2(MOVED_FROM);
370 IN2(MOVED_TO);
371 IN2(CREATE);
372 IN2(DELETE);
373 IN2(DELETE_SELF);
374 IN2(MOVE_SELF);
376 /* sent as needed to any watch */
377 IN2(UNMOUNT);
378 IN2(Q_OVERFLOW);
379 IN2(IGNORED);
380 IN2(ISDIR);
382 /* helpers */
383 IN(CLOSE);
384 IN(MOVE);
386 /* special flags */
387 IN(ONLYDIR);
388 IN(DONT_FOLLOW);
389 IN(EXCL_UNLINK);
390 IN(MASK_ADD);
391 IN(ONESHOT);
393 /* for inotify_init1() */
395 NODOC_CONST(cInotify, "NONBLOCK", INT2NUM(IN_NONBLOCK));
396 NODOC_CONST(cInotify, "CLOEXEC", INT2NUM(IN_CLOEXEC));
398 #endif /* HAVE_SYS_INOTIFY_H */