1 #ifdef HAVE_SYS_INOTIFY_H
2 #include "sleepy_penguin.h"
3 #include <sys/inotify.h>
5 #include "missing_inotify.h"
12 static ID id_inotify_tmp
, id_mask
;
13 static VALUE cEvent
, checks
;
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
)
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
);
34 if (errno
== EMFILE
|| errno
== ENFILE
|| errno
== ENOMEM
) {
36 fd
= inotify_init1(flags
);
39 rb_sys_fail("inotify_init1");
43 rv
= rb_call_super(1, &rv
);
44 rb_ivar_set(rv
, id_inotify_tmp
, rb_ary_new());
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
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
);
97 rb_sys_fail("inotify_add_watch");
99 return UINT2NUM((uint32_t)rc
);
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
);
116 rb_sys_fail("inotify_rm_watch");
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
);
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
);
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
)
154 if (inbuf
->capa
>= size
)
157 err
= posix_memalign(&inbuf
->ptr
, rb_sp_l1_cache_line_size
, size
);
165 static void resize_internal_buffer(struct inread_args
*args
)
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)");
176 inbuf_grow(args
->inbuf
, (size_t)newlen
);
178 if (newlen
== 0) /* race: some other thread grabbed the data */
181 rb_raise(rb_eRuntimeError
,
182 "ioctl(inotify,FIONREAD) returned negative length: %d",
188 * ino.take([nonblock]) -> Inotify::Event or nil
190 * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
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
;
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
);
214 rb_sp_set_nonblock(args
.fd
);
216 blocking_io_prepare(args
.fd
);
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
);
225 if (errno
== EAGAIN
&& RTEST(nonblock
))
227 if (!rb_sp_wait(rb_io_wait_readable
, self
, &args
.fd
))
228 rb_sys_fail("read(inotify)");
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
);
238 rb_ary_push(tmp
, event
);
239 e
= (struct inotify_event
*)
240 ((char *)e
+ event_len(e
));
250 * inotify_event.events => [ :MOVED_TO, ... ]
252 * Returns an array of symbolic event names based on the contents of
255 static VALUE
events(VALUE self
)
257 long len
= RARRAY_LEN(checks
);
258 VALUE
*ptr
= RARRAY_PTR(checks
);
260 VALUE rv
= rb_ary_new();
262 uint32_t event_mask
= NUM2UINT(rb_funcall(self
, id_mask
, 0));
264 for (; (len
-= 2) >= 0;) {
266 mask
= NUM2UINT(*ptr
++);
267 if ((event_mask
& mask
) == mask
)
268 rb_ary_push(rv
, sym
);
276 * ino.each { |event| ... } -> ino
278 * Yields each Inotify::Event received in a blocking fashion.
280 static VALUE
each(VALUE self
)
285 rb_yield(take(0, &argv
, 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 ]
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
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); \
362 /* events a user can watch for */
376 /* sent as needed to any watch */
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 */