pax_global_header00006660000000000000000000000064116376742220014524gustar00rootroot0000000000000052 comment=fe4c2edbed9eb9e16aa5d5947b863f1ffcbdf4e5 ruby-inotify-0.0.2/000077500000000000000000000000001163767422200141635ustar00rootroot00000000000000ruby-inotify-0.0.2/MANIFEST000066400000000000000000000001311163767422200153070ustar00rootroot00000000000000ext/inotify.c ext/inotify.h ext/inotify-syscalls.h tests/test.rb extconf.rb MANIFEST doc ruby-inotify-0.0.2/Rakefile000066400000000000000000000033351163767422200156340ustar00rootroot00000000000000# Rakefile for project management (from chris2) -*-ruby-*- Project = 'project-template' require 'rake/rdoctask' require 'rake/testtask' desc "Build and test" task :default => [:build, :test] desc "Do predistribution stuff" task :predist => [:chmod, :changelog, :doc] desc "Build" task :build do ruby "extconf.rb" system "make" end task :test => :build desc "Run all the tests" Rake::TestTask.new do |t| t.libs << "tests" t.libs << "ext" t.test_files = FileList['tests/test_*.rb'] t.verbose = true end desc "Make an archive as .tar.gz" task :dist => :test do system "export DARCS_REPO=#{File.expand_path "."}; " + "darcs dist -d #{Project}#{get_darcs_tree_version}" end desc "Make binaries executable" task :chmod do Dir["bin/*"].each { |binary| File.chmod(0775, binary) } end desc "Generate a ChangeLog" task :changelog do system "darcs changes --repo=#{ENV["DARCS_REPO"] || "."} >ChangeLog" end desc "Generate RDoc documentation" Rake::RDocTask.new(:doc) do |rdoc| rdoc.options << '--line-numbers --inline-source' rdoc.rdoc_dir = "rdoc" rdoc.rdoc_files.include("lib/**/*.rb", "lib/*.rb") end desc "Clean to distribution pristine" task :distclean do system 'make distclean' end # Helper to retrieve the "revision number" of the darcs tree. def get_darcs_tree_version return "" unless File.directory? "_darcs" changes = `darcs changes` count = 0 tag = "0.0" changes.each("\n\n") { |change| head, title, desc = change.split("\n", 3) if title =~ /^ \*/ # Normal change. count += 1 elsif title =~ /tagged (.*)/ # Tag. We look for these. tag = $1 break else warn "Unparsable change: #{change}" end } "-" + tag + "." + count.to_s end ruby-inotify-0.0.2/examples/000077500000000000000000000000001163767422200160015ustar00rootroot00000000000000ruby-inotify-0.0.2/examples/watcher.rb000066400000000000000000000007221163767422200177640ustar00rootroot00000000000000#!usr/bin/ruby require 'inotify' require 'find' i = Inotify.new t = Thread.new do i.each_event do |ev| p ev.name p ev.mask end end raise("Specify a directory") if !ARGV[0] Find.find(ARGV[0]) do |e| if ['.svn', 'CVS', 'RCS'].include? File.basename(e) or !File.directory? e Find.prune else begin puts "Adding #{e}" i.add_watch(e, Inotify::CREATE | Inotify::DELETE | Inotify::MOVE) rescue puts "Skipping #{e}: #{$!}" end end end t.join ruby-inotify-0.0.2/ext/000077500000000000000000000000001163767422200147635ustar00rootroot00000000000000ruby-inotify-0.0.2/ext/inotify-syscalls.h000066400000000000000000000012311163767422200204450ustar00rootroot00000000000000#ifndef _LINUX_INOTIFY_SYSCALLS_H #define _LINUX_INOTIFY_SYSCALLS_H #if defined(__i386__) # define __NR_inotify_init 291 # define __NR_inotify_add_watch 292 # define __NR_inotify_rm_watch 293 #elif defined(__x86_64__) # define __NR_inotify_init 253 # define __NR_inotify_add_watch 254 # define __NR_inotify_rm_watch 255 #elif defined(__ppc__) # define __NR_inotify_init 275 # define __NR_inotify_add_watch 276 # define __NR_inotify_rm_watch 277 #elif defined (__ia64__) # define __NR_inotify_init 1277 # define __NR_inotify_add_watch 1278 # define __NR_inotify_rm_watch 1279 #else # error "Unsupported architecture!" #endif #endif /* _LINUX_INOTIFY_SYSCALLS_H */ ruby-inotify-0.0.2/ext/inotify.c000066400000000000000000000150201163767422200166060ustar00rootroot00000000000000#include #include #ifdef HAVE_VERSION_H #include #endif #ifdef HAVE_LINUX_INOTIFY_H #include #include #else #include "inotify.h" #include "inotify-syscalls.h" #endif #include #include static inline int inotify_init (void) { return syscall (__NR_inotify_init); } static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); } static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); } VALUE rb_cInotify; VALUE rb_cInotifyEvent; int event_check (int fd) { struct timeval timeout; int r; fd_set rfds; timeout.tv_sec = 0; timeout.tv_usec = 4000; FD_ZERO(&rfds); FD_SET(fd, &rfds); r = rb_thread_select (fd+1, &rfds, NULL, NULL, &timeout); return r; } static VALUE rb_inotify_event_new(struct inotify_event *event) { VALUE retval; retval = Data_Wrap_Struct(rb_cInotifyEvent, NULL, free, event); rb_obj_call_init(retval, 0, NULL); return retval; } /* * call-seq: * Inotify.new => inotify * */ static VALUE rb_inotify_new(VALUE klass) { int *fd; VALUE retval; fd = malloc(sizeof(int)); *fd = inotify_init(); if(*fd < 0) rb_sys_fail("inotify_init()"); retval = Data_Wrap_Struct(klass, NULL, free, fd); rb_obj_call_init(retval, 0, NULL); return retval; } /* * call-seq: * inotify.add_watch(filename, Inotify::ALL_EVENTS) => watch number * */ static VALUE rb_inotify_add_watch(VALUE self, VALUE filename, VALUE mask) { #ifndef HAVE_TYPE_OPENFILE rb_io_t *fptr; #else OpenFile *fptr; #endif int *fd, wd; Data_Get_Struct(self, int, fd); wd = inotify_add_watch(*fd, RSTRING_PTR(filename), NUM2INT(mask)); if(wd < 0) { rb_sys_fail(RSTRING_PTR(filename)); } return INT2NUM(wd); } /* * call-seq: * inotify.rm_watch(filename, wd) => true or raises exception. * */ static VALUE rb_inotify_rm_watch(VALUE self, VALUE wdnum) { int *fd; Data_Get_Struct(self, int, fd); if(inotify_rm_watch(*fd, NUM2INT(wdnum)) < 0) { rb_sys_fail("removing watch"); } return Qtrue; } /* * call-seq: * inotify.each_event { |event| ... } * */ static VALUE rb_inotify_each_event(VALUE self) { #ifndef HAVE_TYPE_OPENFILE rb_io_t *fptr; #else OpenFile *fptr; #endif int *fd, r; struct inotify_event *event, *pevent; char buffer[16384]; size_t buffer_n, event_size; Data_Get_Struct(self, int, fd); while(1) { r = event_check(*fd); if(r == 0) { continue; } if((r = read(*fd, buffer, 16384)) < 0) { rb_sys_fail("reading event"); } buffer_n = 0; while (buffer_n < r) { pevent = (struct inotify_event *)&buffer[buffer_n]; event_size = sizeof(struct inotify_event) + pevent->len; event = malloc(event_size); memmove(event, pevent, event_size); buffer_n += event_size; rb_yield(rb_inotify_event_new(event)); } } return Qnil; } /* * call-seq: * inotify.close => nil * */ static VALUE rb_inotify_close(VALUE self) { int *fd; Data_Get_Struct(self, int, fd); if(close(*fd) != 0) { rb_sys_fail("closing inotify"); } return Qnil; } /* * call-seq: * inotify_event.inspect => "" * */ static VALUE rb_inotify_event_inspect(VALUE self) { struct inotify_event *event; int len, pf; char buf[1024]; Data_Get_Struct(self, struct inotify_event, event); len = event->len; /* TODO: Check for string getting truncated */ pf = snprintf(buf, 1024, "", event->name, event->mask, event->wd); return rb_str_new2(buf); } /* * call-seq: * inotify_event.name => name or nil * */ static VALUE rb_inotify_event_name(VALUE self) { struct inotify_event *event; Data_Get_Struct(self, struct inotify_event, event); if(event->len) { return rb_str_new2(event->name); } else { return Qnil; } } /* * call-seq: * inotify_event.wd => watch descriptor * */ static VALUE rb_inotify_event_wd(VALUE self) { struct inotify_event *event; Data_Get_Struct(self, struct inotify_event, event); return INT2NUM(event->wd); } /* * call-seq: * inotify_event.mask => 0xcafebabe * */ static VALUE rb_inotify_event_mask(VALUE self) { struct inotify_event *event; Data_Get_Struct(self, struct inotify_event, event); return LONG2NUM(event->mask); } void Init_inotify () { rb_cInotify = rb_define_class("Inotify", rb_cObject); rb_cInotifyEvent = rb_define_class_under(rb_cInotify, "Event", rb_cObject); rb_const_set(rb_cInotify, rb_intern("ACCESS"), INT2NUM(IN_ACCESS)); rb_const_set(rb_cInotify, rb_intern("MODIFY"), INT2NUM(IN_MODIFY)); rb_const_set(rb_cInotify, rb_intern("ATTRIB"), INT2NUM(IN_ATTRIB)); rb_const_set(rb_cInotify, rb_intern("CLOSE_WRITE"), INT2NUM(IN_CLOSE_WRITE)); rb_const_set(rb_cInotify, rb_intern("CLOSE_NOWRITE"), INT2NUM(IN_CLOSE_NOWRITE)); rb_const_set(rb_cInotify, rb_intern("OPEN"), INT2NUM(IN_OPEN)); rb_const_set(rb_cInotify, rb_intern("MOVED_FROM"), INT2NUM(IN_MOVED_FROM)); rb_const_set(rb_cInotify, rb_intern("MOVED_TO"), INT2NUM(IN_MOVED_TO)); rb_const_set(rb_cInotify, rb_intern("CREATE"), INT2NUM(IN_CREATE)); rb_const_set(rb_cInotify, rb_intern("DELETE"), INT2NUM(IN_DELETE)); rb_const_set(rb_cInotify, rb_intern("DELETE_SELF"), INT2NUM(IN_DELETE_SELF)); rb_const_set(rb_cInotify, rb_intern("MOVE_SELF"), INT2NUM(IN_MOVE_SELF)); rb_const_set(rb_cInotify, rb_intern("UNMOUNT"), INT2NUM(IN_UNMOUNT)); rb_const_set(rb_cInotify, rb_intern("Q_OVERFLOW"), INT2NUM(IN_Q_OVERFLOW)); rb_const_set(rb_cInotify, rb_intern("IGNORED"), INT2NUM(IN_IGNORED)); rb_const_set(rb_cInotify, rb_intern("CLOSE"), INT2NUM(IN_CLOSE)); rb_const_set(rb_cInotify, rb_intern("MOVE"), INT2NUM(IN_MOVE)); rb_const_set(rb_cInotify, rb_intern("MASK_ADD"), INT2NUM(IN_MASK_ADD)); rb_const_set(rb_cInotify, rb_intern("ISDIR"), INT2NUM(IN_ISDIR)); rb_const_set(rb_cInotify, rb_intern("ONESHOT"), INT2NUM(IN_ONESHOT)); rb_const_set(rb_cInotify, rb_intern("ALL_EVENTS"), INT2NUM(IN_ALL_EVENTS)); rb_define_singleton_method(rb_cInotify, "new", rb_inotify_new, 0); rb_define_method(rb_cInotify, "add_watch", rb_inotify_add_watch, 2); rb_define_method(rb_cInotify, "rm_watch", rb_inotify_rm_watch, 1); rb_define_method(rb_cInotify, "each_event", rb_inotify_each_event, 0); rb_define_method(rb_cInotify, "close", rb_inotify_close, 0); rb_define_method(rb_cInotifyEvent, "inspect", rb_inotify_event_inspect, 0); rb_define_method(rb_cInotifyEvent, "name", rb_inotify_event_name, 0); rb_define_method(rb_cInotifyEvent, "wd", rb_inotify_event_wd, 0); rb_define_method(rb_cInotifyEvent, "mask", rb_inotify_event_mask, 0); } ruby-inotify-0.0.2/ext/inotify.h000066400000000000000000000047511163767422200166240ustar00rootroot00000000000000/* * Inode based directory notification for Linux * * Copyright (C) 2005 John McCutchan */ #ifndef _LINUX_INOTIFY_H #define _LINUX_INOTIFY_H #include /* * struct inotify_event - structure read from the inotify device for each event * * When you are watching a directory, you will receive the filename for events * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd. */ struct inotify_event { __s32 wd; /* watch descriptor */ __u32 mask; /* watch mask */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* length (including nulls) of name */ char name[0]; /* stub for possible name */ }; /* the following are legal, implemented events that user-space can watch for */ #define IN_ACCESS 0x00000001 /* File was accessed */ #define IN_MODIFY 0x00000002 /* File was modified */ #define IN_ATTRIB 0x00000004 /* Metadata changed */ #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */ #define IN_OPEN 0x00000020 /* File was opened */ #define IN_MOVED_FROM 0x00000040 /* File was moved from X */ #define IN_MOVED_TO 0x00000080 /* File was moved to Y */ #define IN_CREATE 0x00000100 /* Subfile was created */ #define IN_DELETE 0x00000200 /* Subfile was deleted */ #define IN_DELETE_SELF 0x00000400 /* Self was deleted */ #define IN_MOVE_SELF 0x00000800 /* Self was moved */ /* the following are legal events. they are sent as needed to any watch */ #define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */ #define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ #define IN_IGNORED 0x00008000 /* File was ignored */ /* helper events */ #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */ #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ /* special flags */ #define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */ #define IN_ISDIR 0x40000000 /* event occurred against dir */ #define IN_ONESHOT 0x80000000 /* only send event once */ /* * All of the events - we build the list by hand so that we can add flags in * the future and not break backward compatibility. Apps will get only the * events that they originally wanted. Be sure to add new events here! */ #define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \ IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \ IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \ IN_MOVE_SELF) #endif /* _LINUX_INOTIFY_H */ ruby-inotify-0.0.2/extconf.rb000066400000000000000000000002301163767422200161510ustar00rootroot00000000000000require 'mkmf' have_header('linux/inotify.h') have_header("version.h") have_type("OpenFile", ["ruby.h", "rubyio.h"]) create_makefile('inotify', 'ext') ruby-inotify-0.0.2/tests/000077500000000000000000000000001163767422200153255ustar00rootroot00000000000000ruby-inotify-0.0.2/tests/test_1.rb000066400000000000000000000015671163767422200170620ustar00rootroot00000000000000require 'test/unit' require 'inotify' class Test1 < Test::Unit::TestCase def setup @inotify = Inotify.new end def test1 assert_equal(Inotify, @inotify.class) end def test2 assert(@inotify.add_watch("/tmp", Inotify::CREATE)) end def test3 wd = @inotify.add_watch("/tmp", Inotify::CREATE) assert_equal(Fixnum, wd.class) assert(@inotify.rm_watch(wd)) end def test4 @inotify.add_watch("/tmp", Inotify::CREATE) begin File.open(File.join("/tmp", "ruby-inotify-test-4"), 'w') @inotify.each_event do |ev| assert_equal(ev.class, Inotify::Event) assert_equal(ev.inspect, "") assert_equal(ev.name, "ruby-inotify-test-4") assert_equal(ev.mask, Inotify::CREATE) break end ensure File.unlink(File.join("/tmp", "ruby-inotify-test-4")) end end def teardown @inotify.close end end