http://github.com/smparkes/eventmachine/commit/0dc5780ce28b1ad7a7107ca1b20faba031ff3ef5
(sorry for the fprintf cruft).
I need to be able to see directory modifications and they're triggerd
via things like IN_MOVE.
Seems like a pretty small change. It shouldn't effect the results for
files, since these flags should only affect directories. It'll affect
anyone putting watches on directories, but since few events fire for
directories w/o these flags, I wonder if there are many of folks like
that.
I don't pull out the path from the results, but I do provide a buffer
for the result, otherwise read returns EINVAL. Not sure how to come up
with a size ... The failure case right now should you hit it is kinda
nasty. Seems like the events never dequeue, so you get stuck just
trying to read the big events over and over.
Overall, works great, though. I've ported/extended the Ruby watchr gem
(git://github.com/smparkes/watchr.git) and it works well on both OS X
and Linux.
Hmmm ... people that know more about the inotify interface may have
noticed (so to speak) that was not a great patch: I neglected to read
far enough in the man page to see that the kernel will stuff more than
one event per read if you give it enough space.
This commit fixes that:
http://github.com/smparkes/eventmachine/commit/8e2628173c002ad67a3f2321d8af7a3e1843af88
I tried to fix the spacing, but on github, it looks tweaked still,
somewhere between spaces and tabs, I suspect. I got rid of the old
fprintf cruft ... and added new, sigh.
Changes are in my fork at git://github.com/smparkes/eventmachine.git
The changes are three-fold:
1) Add some more inotify options so that directory changes can be detected.
2) Handle more than a minimal size inotify packet. Otherwise you can get an infinite loop because non-minimal packets are never delivered. This has the side effect of needing to handle multiple events per packet, since now there can now be enough room for multiple events in a packet.
3) Somewhat unrelated, but remove the 100-file limit on popen (which I need ...)
Happy to fix/change/improve, if some needs to be done.
Patch below (but can just be pulled from my fork (which has other changes for .gitignore/gemspec for my version of the gem):
diff --git a/ext/em.cpp b/ext/em.cpp
index e4ee9be..1b9c910 100644
--- a/ext/em.cpp
+++ b/ext/em.cpp
@@ -1963,9 +1963,9 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
if (!cmd_strings)
return 0;
int j;
- for (j=0; j < 100 && cmd_strings[j]; j++)
+ for (j=0; cmd_strings[j]; j++)
;
- if ((j==0) || (j==100))
+ if (j==0)
return 0;
unsigned long output_binding = 0;
@@ -2131,7 +2131,8 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
Add(inotify);
}
- wd = inotify_add_watch(inotify->GetSocket(), fpath, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
+ wd = inotify_add_watch(inotify->GetSocket(), fpath,
+ IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_MOVE) ;
if (wd == -1) {
char errbuf[300];
sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
@@ -2207,20 +2208,27 @@ EventMachine_t::_ReadInotify_Events
void EventMachine_t::_ReadInotifyEvents()
{
#ifdef HAVE_INOTIFY
- struct inotify_event event;
+ char buffer[1024];
assert(EventCallback);
- while (read(inotify->GetSocket(), &event, INOTIFY_EVENT_SIZE) > 0) {
- assert(event.len == 0);
- if (event.mask & IN_MODIFY)
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
- if (event.mask & IN_MOVE_SELF)
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
- if (event.mask & IN_DELETE_SELF) {
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
- UnwatchFile ((int)event.wd);
- }
+ int returned;
+ while ((returned = read(inotify->GetSocket(), buffer, sizeof(buffer) )) > 0) {
+ int current = 0;
+ while(current< returned){
+ struct inotify_event* event = (struct inotify_event*)(buffer+current);
+ if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){
+ (*EventCallback)(Files [event->wd]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
+ }
+ if (event->mask & IN_MOVE_SELF){
+ (*EventCallback)(Files [event->wd]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
+ }
+ if (event->mask & IN_DELETE_SELF) {
+ (*EventCallback)(Files [event->wd]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
+ UnwatchFile ((int)event->wd);
+ }
+ current += sizeof(struct inotify_event) + event->len;
+ }
}
#endif
}
diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp
index f445de1..4c81789 100644
--- a/ext/rubymain.cpp
+++ b/ext/rubymain.cpp
@@ -754,9 +754,11 @@ static VALUE t_invoke_popen (VALUE self, VALUE cmd)
#else
int len = RARRAY (cmd)->len;
#endif
- if (len > 98)
+ char** strings = new char*[len+2];
+ if(!strings) {
rb_raise (rb_eRuntimeError, "too many arguments to popen");
- char *strings [100];
+ return ULONG2NUM (0);
+ }
for (int i=0; i < len; i++) {
VALUE ix = INT2FIX (i);
VALUE s = rb_ary_aref (1, &ix, cmd);
@@ -772,6 +774,7 @@ static VALUE t_invoke_popen (VALUE self, VALUE cmd)
snprintf (buf, sizeof(buf)-1, "no popen: %s", (err?err:"???"));
rb_raise (rb_eRuntimeError, "%s", buf);
}
+ delete [] strings;
return ULONG2NUM (f);
}