From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-2.9 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, T_RP_MATCHES_RCVD shortcircuit=no autolearn=unavailable version=3.3.2 X-source-folder: /home/ew/Mail/a.yhbt/2011-10.mbox.gz Return-Path: X-Original-To: normalperson@yhbt.net Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 1861629682E; Wed, 27 Jul 2011 00:32:39 +0000 (UTC) Date: Tue, 26 Jul 2011 17:32:29 -0700 From: Eric Wong To: sleepy.penguin@librelist.org Subject: [PATCH (WIP)] FANotify support Message-ID: <20110727003229.GA28495@dcvr.yhbt.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) List-Id: I started working on this a few weeks ago but don't know when I'll have a chance to finish it, so I'm throwing it up here in case somebody wants to play with it and get it usable. fanotify lacks manpages, even, and the LWN articles are for an older API so it'll require some reading of kernel/glibc sources to get it working... --- ext/sleepy_penguin/extconf.rb | 1 + ext/sleepy_penguin/fanotify.c | 168 +++++++++++++++++++++++++++++++++++++++++ ext/sleepy_penguin/init.c | 7 ++ test/test_fanotify.rb | 20 +++++ 4 files changed, 196 insertions(+), 0 deletions(-) create mode 100644 ext/sleepy_penguin/fanotify.c create mode 100644 test/test_fanotify.rb diff --git a/ext/sleepy_penguin/extconf.rb b/ext/sleepy_penguin/extconf.rb index ba1db75..d8b6523 100644 --- a/ext/sleepy_penguin/extconf.rb +++ b/ext/sleepy_penguin/extconf.rb @@ -6,6 +6,7 @@ have_header('sys/signalfd.h') have_header('sys/timerfd.h') have_header('sys/inotify.h') have_header('sys/signalfd.h') +have_header('sys/fanotify.h') have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h') have_func('epoll_create1', %w(sys/epoll.h)) have_func('rb_thread_blocking_region') diff --git a/ext/sleepy_penguin/fanotify.c b/ext/sleepy_penguin/fanotify.c new file mode 100644 index 0000000..ccd46d5 --- /dev/null +++ b/ext/sleepy_penguin/fanotify.c @@ -0,0 +1,168 @@ +#ifdef HAVE_SYS_FANOTIFY_H +#include "sleepy_penguin.h" +#include +static VALUE cEventMetadata, cResponse; +static VALUE sym_AT_FDCWD; + +static VALUE response_fd_m(VALUE self) +{ + struct fanotify_response *response = DATA_PTR(self); + + return INT2NUM(response->fd); +} + +static VALUE response_response_m(VALUE self) +{ + struct fanotify_response *response = DATA_PTR(self); + + return UINT2NUM(response->response); +} + +/* +struct fanotify_event_metadata { + __u32 event_len; + __u8 vers; + __u8 reserved; + __u16 metadata_len; + __aligned_u64 mask; + __s32 fd; + __s32 pid; +}; +*/ + +static VALUE s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE flags, event_f_flags, rv; + struct { + unsigned int flags; + unsigned int event_f_flags; + } a; + int fd; + + rb_scan_args(argc, argv, "02", &flags, &event_f_flags); + a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags); + a.event_f_flags = NIL_P(event_f_flags) ? 0 : NUM2UINT(event_f_flags); + + fd = fanotify_init(a.flags, a.event_f_flags); + if (fd == -1) { + if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) { + rb_gc(); + fd = fanotify_init(a.flags, a.event_f_flags); + } + if (fd == -1) + rb_sys_fail("fanotify_init"); + } + + rv = INT2FIX(fd); + return rb_call_super(1, &rv); +} + +struct mark_args { + int fanotify_fd; + unsigned flags; + uint64_t mask; + int dfd; + const char *pathname; +}; + +/* Add, remove, or modify an fanotify mark on a filesystem object. */ +/* extern int fanotify_mark (int fanotify_fd, unsigned int flags, */ + /* uint64_t mask, int dfd, const char *pathname) */ +static VALUE mark(VALUE self) +{ + struct mark_args args; + + /* StringValueCStr() */ + + return self; +} + +static VALUE event_metadata_alloc(VALUE klass) +{ + struct fanotify_event_metadata *em; + + em = ALLOC(struct fanotify_event_metadata); + + return Data_Wrap_Struct(klass, NULL, -1, em); +} + +static VALUE response_alloc(VALUE klass) +{ + struct fanotify_response *response = ALLOC(struct fanotify_response); + + return Data_Wrap_Struct(klass, NULL, -1, response); +} + +void sleepy_penguin_init_fanotify(void) +{ + VALUE mSleepyPenguin, cFanotify; + + mSleepyPenguin = rb_define_module("SleepyPenguin"); + cFanotify = rb_define_class_under(mSleepyPenguin, "Fanotify", rb_cIO); + rb_define_singleton_method(cFanotify, "new", s_new, -1); + return; + +#define FAN(x) rb_define_const(cFanotify,#x,UINT2NUM(FAN_##x)) + FAN(ACCESS); + FAN(MODIFY); + FAN(CLOSE_WRITE); + FAN(CLOSE_NOWRITE); + FAN(OPEN); + FAN(Q_OVERFLOW); + FAN(OPEN_PERM); + FAN(ACCESS_PERM); + FAN(ONDIR); + FAN(EVENT_ON_CHILD); + FAN(CLOSE); + FAN(CLOEXEC); + FAN(CLASS_NOTIF); + FAN(CLASS_CONTENT); + FAN(CLASS_PRE_CONTENT); + FAN(ALL_CLASS_BITS); + FAN(UNLIMITED_QUEUE); + FAN(UNLIMITED_MARKS); + FAN(ALL_INIT_FLAGS); + FAN(MARK_ADD); + FAN(MARK_REMOVE); + FAN(MARK_DONT_FOLLOW); + FAN(MARK_ONLYDIR); + FAN(MARK_MOUNT); + FAN(MARK_IGNORED_MASK); + FAN(MARK_IGNORED_SURV_MODIFY); + FAN(MARK_FLUSH); + FAN(ALL_MARK_FLAGS); + FAN(ALL_EVENTS); + FAN(ALL_PERM_EVENTS); + FAN(ALL_OUTGOING_EVENTS); + + rb_define_const(cFanotify, "METADATA_VERSION", + INT2NUM(FANOTIFY_METADATA_VERSION)); + + /* Legit userspace responses to a _PERM event */ + FAN(ALLOW); + FAN(DENY); + + rb_define_const(cFanotify, "NOFD", INT2FIX(-1)); + + rb_define_alloc_func(cResponse, response_alloc); + rb_define_method(cResponse, "fd", response_fd_m, 0); + rb_define_method(cResponse, "response", response_response_m, 0); + + rb_define_alloc_func(cEventMetadata, event_metadata_alloc); + + sym_AT_FDCWD = ID2SYM(rb_intern("AT_FDCWD")); +} + +/* Helper functions to deal with fanotify_event_metadata buffers */ +#if 0 +#define FAN_EVENT_METADATA_LEN + +#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \ + (struct fanotify_event_metadata*)(((char *)(meta)) + \ + (meta)->event_len)) + +#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len <= (long)(len)) +#endif +#endif /* HAVE_SYS_FANOTIFY_H */ diff --git a/ext/sleepy_penguin/init.c b/ext/sleepy_penguin/init.c index eea0025..24a23b0 100644 --- a/ext/sleepy_penguin/init.c +++ b/ext/sleepy_penguin/init.c @@ -24,6 +24,12 @@ void sleepy_penguin_init_signalfd(void); # define sleepy_penguin_init_signalfd() for(;0;) #endif +#ifdef HAVE_SYS_FANOTIFY_H +void sleepy_penguin_init_fanotify(void); +#else +# define sleepy_penguin_init_fanotify() for(;0;) +#endif + void Init_sleepy_penguin_ext(void) { sleepy_penguin_init_epoll(); @@ -31,4 +37,5 @@ void Init_sleepy_penguin_ext(void) sleepy_penguin_init_eventfd(); sleepy_penguin_init_inotify(); sleepy_penguin_init_signalfd(); + sleepy_penguin_init_fanotify(); } diff --git a/test/test_fanotify.rb b/test/test_fanotify.rb new file mode 100644 index 0000000..80ae6c6 --- /dev/null +++ b/test/test_fanotify.rb @@ -0,0 +1,20 @@ +require 'test/unit' +require 'fcntl' +require 'tempfile' +require 'set' +$-w = true +require 'sleepy_penguin' + +class TestFanotify < Test::Unit::TestCase + include SleepyPenguin + + def teardown + ObjectSpace.each_object(Fanotify) { |io| io.close unless io.closed? } + ObjectSpace.each_object(Tempfile) { |io| io.close unless io.closed? } + end + + def test_new + @fan = Fanotify.new + assert_kind_of(IO, @fan) + end +end -- Eric Wong