summaryrefslogtreecommitdiff
path: root/server/src/inotify.cc
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/inotify.cc')
-rw-r--r--server/src/inotify.cc372
1 files changed, 322 insertions, 50 deletions
diff --git a/server/src/inotify.cc b/server/src/inotify.cc
index e1d47d1..7834327 100644
--- a/server/src/inotify.cc
+++ b/server/src/inotify.cc
@@ -27,15 +27,21 @@
*/
#include "inotify.h"
+#include "debug.h"
+
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
#define TEST(x, m) ((x & m) == m)
+#ifdef WITH_DEBUG
#define STEST(x, m) (TEST(x, m)?#m" ":"")
static std::string mask2asc(uint32_t mask)
@@ -54,13 +60,16 @@ static std::string mask2asc(uint32_t mask)
str += STEST(mask, IN_MOVED_FROM);
str += STEST(mask, IN_MOVED_TO);
str += STEST(mask, IN_OPEN);
+
str += STEST(mask, IN_ALL_EVENTS);
str += STEST(mask, IN_CLOSE);
str += STEST(mask, IN_MOVE);
+
str += STEST(mask, IN_DONT_FOLLOW);
str += STEST(mask, IN_MASK_ADD);
str += STEST(mask, IN_ONESHOT);
str += STEST(mask, IN_ONLYDIR);
+
str += STEST(mask, IN_IGNORED);
str += STEST(mask, IN_ISDIR);
str += STEST(mask, IN_Q_OVERFLOW);
@@ -68,6 +77,7 @@ static std::string mask2asc(uint32_t mask)
return str;
}
+#endif
static inline bool isdir(const char *name)
{
@@ -76,13 +86,23 @@ static inline bool isdir(const char *name)
return S_ISDIR(s.st_mode);
}
-INotify::Event::Event(std::string name, uint32_t mask)
+INotify::Event::Event(struct inotify_event *event, std::string name)
{
this->_name = name;
- this->_mask = mask;
+ if(event) {
+ this->_mask = event->mask;
+ this->_file = event->name;
+ } else {
+ this->_mask = 0;
+ this->_file = "";
+ }
- printf("[%s]\n", mask2asc(_mask).c_str());
+ PRACRO_DEBUG(inotify, "Event [%s] %s (%s)\n",
+ mask2asc(_mask).c_str(),
+ _name.c_str(),
+ _file.c_str());
}
+
bool INotify::Event::isAttributeChangeEvent()
{
return TEST(_mask, IN_ATTRIB);
@@ -90,12 +110,27 @@ bool INotify::Event::isAttributeChangeEvent()
bool INotify::Event::isCloseEvent()
{
- return TEST(_mask, IN_CLOSE_WRITE) || TEST(_mask, IN_CLOSE_NOWRITE);
+ return isCloseWriteEvent() || isCloseNoWriteEvent();
+}
+
+bool INotify::Event::isCloseWriteEvent()
+{
+ return TEST(_mask, IN_CLOSE_WRITE);
+}
+
+bool INotify::Event::isCloseNoWriteEvent()
+{
+ return TEST(_mask, IN_CLOSE_NOWRITE);
+}
+
+bool INotify::Event::isCreateEvent()
+{
+ return TEST(_mask, IN_CREATE);
}
bool INotify::Event::isOpenEvent()
{
- return TEST(_mask, IN_OPEN) || TEST(_mask, IN_CREATE);
+ return TEST(_mask, IN_OPEN);
}
bool INotify::Event::isModifyEvent()
@@ -103,9 +138,39 @@ bool INotify::Event::isModifyEvent()
return TEST(_mask, IN_MODIFY);
}
+bool INotify::Event::isAccessEvent()
+{
+ return TEST(_mask, IN_ACCESS);
+}
+
bool INotify::Event::isDeleteEvent()
{
- return TEST(_mask, IN_DELETE) || TEST(_mask, IN_DELETE_SELF);
+ return TEST(_mask, IN_DELETE);
+}
+
+bool INotify::Event::isDeleteSelfEvent()
+{
+ return TEST(_mask, IN_DELETE_SELF);
+}
+
+bool INotify::Event::isIgnoredEvent()
+{
+ return TEST(_mask, IN_IGNORED);
+}
+
+bool INotify::Event::isMoveSelfEvent()
+{
+ return TEST(_mask, IN_MOVE_SELF);
+}
+
+bool INotify::Event::isMovedFromEvent()
+{
+ return TEST(_mask, IN_MOVED_FROM);
+}
+
+bool INotify::Event::isMovedToEvent()
+{
+ return TEST(_mask, IN_MOVED_TO);
}
std::string INotify::Event::name()
@@ -113,6 +178,11 @@ std::string INotify::Event::name()
return _name;
}
+std::string INotify::Event::file()
+{
+ return _file;
+}
+
uint32_t INotify::Event::mask()
{
return _mask;
@@ -120,8 +190,8 @@ uint32_t INotify::Event::mask()
INotify::INotify()
{
- fd = inotify_init1(O_NONBLOCK);
- if(fd == -1) {
+ ifd = inotify_init1(O_NONBLOCK);
+ if(ifd == -1) {
perror("Inotify init failed.\n");
return;
}
@@ -129,10 +199,10 @@ INotify::INotify()
INotify::~INotify()
{
- if(fd != -1) close(fd);
+ if(ifd != -1) close(ifd);
}
-void INotify::add(std::string name, uint32_t mask)
+void INotify::addFile(std::string name, uint32_t mask)
{
// Extra mask bitflags:
//IN_DONT_FOLLOW (since Linux 2.6.15)
@@ -145,24 +215,132 @@ void INotify::add(std::string name, uint32_t mask)
//IN_ONLYDIR (since Linux 2.6.15)
// Only watch pathname if it is a directory.
- int _fd = inotify_add_watch(fd, name.c_str(), mask);
- if(_fd == -1) {
+ int wd = inotify_add_watch(ifd, name.c_str(), mask);
+ if(wd == -1) {
perror("INotify: Add watch failed!");
return;
}
- // dirmap[fd] = name;
+
+ Watch w;
+ w.mask = mask;
+ w.name = name;
+ w.depth = WATCH_SINGLE;
+ dirmap[wd] = w;
}
-void INotify::addRecursive(std::string name, uint32_t mask)
+static inline bool isdir(std::string name)
{
- /*
- int fd = this->add(name, mask);
- return fd;
- */
+ struct stat s;
+ stat(name.c_str(), &s);
+ return S_ISDIR(s.st_mode);
+}
+
+void INotify::addDirectory(std::string name, depth_t depth, uint32_t mask)
+{
+ PRACRO_DEBUG(inotify, "Adding dir: %s\n", name.c_str());
+
+ int depth_mask = 0;
+ if(depth == WATCH_DEEP || depth == WATCH_DEEP_FOLLOW) {
+ depth_mask = IN_CREATE; // We need to watch for create in order to catch
+ // creation of new subdirs.
+
+ DIR *dir = opendir(name.c_str());
+ if(!dir) {
+ PRACRO_ERR(inotify, "Could not open directory: %s - %s\n",
+ name.c_str(), strerror(errno));
+ return;
+ }
+
+ struct dirent *dirent;
+ while( (dirent = readdir(dir)) != NULL ) {
+
+ if(std::string(dirent->d_name) == "." || std::string(dirent->d_name) == "..")
+ continue;
+
+ PRACRO_DEBUG(inotify, "Add entry?: %s\n", dirent->d_name);
+
+ if(isdir(name+"/"+dirent->d_name)) addDirectory(name+"/"+dirent->d_name, depth, mask);
+ }
+
+ closedir(dir);
+ }
+
+ int wd = inotify_add_watch(ifd, name.c_str(), mask | IN_ONLYDIR | depth_mask);
+ if(wd == -1) {
+ perror("INotify: Add watch failed!");
+ return;
+ }
+
+ Watch w;
+ w.mask = mask;
+ w.name = name;
+ w.depth = depth;
+ dirmap[wd] = w;
+}
+
+void INotify::remove(int wd)
+{
+ if(inotify_rm_watch(ifd, wd) == -1) {
+ perror("inotify_rm_watch failed");
+ return;
+ }
+ dirmap.erase(wd);
+}
+
+void INotify::remove(std::string name)
+{
+ std::map<int, Watch>::iterator i = dirmap.begin();
+ while(i != dirmap.end()) {
+ Watch w = i->second;
+ if(w.name == name) this->remove(i->first);
+ i++;
+ }
+}
+
+void INotify::readEvents()
+{
+ size_t size = 64;
+ char *buf = (char*)malloc(size);
+
+ ssize_t r;
+ while( ((r = read(ifd, buf, size)) == -1 && errno == EINVAL) || r == 0 ) {
+ fprintf(stderr, "Doubling buffer size: %d\n", size);
+ size *= 2;
+ buf = (char*)realloc(buf, size);
+ }
+
+ int p = 0;
+ while(p < r) {
+ struct inotify_event *event = (struct inotify_event*)(buf + p);
+ p += sizeof(struct inotify_event) + event->len;
+
+ // TODO: We need to figure out what the new filename/destination is...
+ if(TEST(event->mask, IN_MOVE_SELF)) dirmap[event->wd].name = "????????";
+
+ if(dirmap[event->wd].depth == WATCH_DEEP_FOLLOW &&
+ TEST(event->mask, IN_CREATE) &&
+ TEST(event->mask, IN_ISDIR))
+ addDirectory(dirmap[event->wd].name + "/" + event->name,
+ dirmap[event->wd].depth, dirmap[event->wd].mask);
+
+ if(TEST(event->mask, IN_CREATE) && !TEST(dirmap[event->wd].mask, IN_CREATE)) {
+ // Ignore this event, it was not requested by the user.
+ } else {
+ eventlist.push_back(INotify::Event(event, dirmap[event->wd].name));
+ }
+
+ if(TEST(event->mask, IN_IGNORED)) dirmap.erase(event->wd);
+ }
+
+ free(buf);
}
bool INotify::hasEvents()
{
+ readEvents();
+ return eventlist.size() > 0;
+
+#if 0
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
@@ -170,8 +348,8 @@ bool INotify::hasEvents()
fd_set fset;
int ret;
FD_ZERO(&fset);
- FD_SET(fd, &fset);
- ret = select (fd+1, &fset, NULL, NULL, &tv);
+ FD_SET(ifd, &fset);
+ ret = select (ifd+1, &fset, NULL, NULL, &tv);
switch(ret) {
case -1:
if(errno == EINTR) {
@@ -184,23 +362,36 @@ bool INotify::hasEvents()
break;
default:
- if(FD_ISSET(fd, &fset)) {
+ if(FD_ISSET(ifd, &fset)) {
// Ready read
return true;
}
}
return false;
+#endif
}
INotify::Event INotify::getNextEvent()
{
- char buf[1024];
- ssize_t r = read(fd, buf, sizeof(struct inotify_event));
+ readEvents();
+ if(eventlist.size() == 0) return Event(NULL, "");
+ Event e = eventlist.front();
+ eventlist.pop_front();
+ return e;
+
+#if 0
+ struct inotify_event event;
+ ssize_t r = read(ifd, &event, sizeof(struct inotify_event));
+ if(r != sizeof(struct inotify_event) && r != -1)
if(r == sizeof(struct inotify_event)) {
- struct inotify_event *event = (struct inotify_event *)buf;
- if(event->len) read(fd, buf + sizeof(struct inotify_event), event->len);
- switch(event->mask) {
+ char *name = NULL;
+ if(event.len) {
+ name = (char*)malloc(event.len);
+ if(read(ifd, name, event.len) != (int)event.len) printf("\n\nFUXK!\n\n");
+ }
+
+ switch(event.mask) {
case IN_IGNORED:
//Watch was removed explicitly (inotify_rm_watch(2)) or automatically
// (file was deleted, or file system was unmounted).
@@ -215,19 +406,13 @@ INotify::Event INotify::getNextEvent()
break;
}
- return Event(event->name, event->mask);
+ if(TEST(event.mask, IN_IGNORED)) dirmap.erase(event.wd);
+ Event e((name!=NULL)?name:"", event.mask);
+ if(name) free(name);
+ return e;
}
return Event("", 0);
-}
-
-void INotify::remove(int fd)
-{
- inotify_rm_watch(this->fd, fd);
-}
-
-void INotify::remove(std::string name)
-{
- // this->remove(rlookup(map, name))
+#endif
}
#ifdef TEST_INOTIFY
@@ -238,25 +423,45 @@ void INotify::remove(std::string name)
#include <stdio.h>
-#define _DIR "/tmp"
-#define _FILE _DIR"/inotify_test"
+#define _BASEDIR "/tmp"
+#define _DIR _BASEDIR"/inotify_test_dir"
+#define _FILE _BASEDIR"/inotify_test"
TEST_BEGIN;
+pracro_debug_parse("+all");
+
INotify inotify;
// Create file
FILE *fp = fopen(_FILE, "w");
-TEST_NOTEQUAL(fp, NULL, "Testing if able to write file");
+if(!fp) TEST_FATAL("Unable to write to the file");
fprintf(fp, "something");
fclose(fp);
-inotify.add(_FILE);
+inotify.addFile(_FILE);
+
+TEST_MSG("Positive tests on file watch.");
+
+// Append to file
+fp = fopen(_FILE, "r");
+if(!fp) TEST_FATAL("Unable to read from the file");
+TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event.");
+
+char buf[32];
+fread(buf, sizeof(buf), 1, fp);
+TEST_TRUE(inotify.hasEvents(), "Test if the read event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isAccessEvent(), "Test if the event was a access event.");
+
+fclose(fp);
+TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isCloseNoWriteEvent(), "Test if the event was a close-nowrite event.");
// Append to file
fp = fopen(_FILE, "a");
-TEST_NOTEQUAL(fp, NULL, "Testing if able to write file");
+if(!fp) TEST_FATAL("Unable to write to the file");
TEST_TRUE(inotify.hasEvents(), "Test if the open event was triggered.");
TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "Test if the event was an open event.");
@@ -266,12 +471,12 @@ TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modif
fclose(fp);
TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered.");
-TEST_TRUE(inotify.getNextEvent().isCloseEvent(), "Test if the event was a close event.");
+TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "Test if the event was a close event.");
// Overwrite file
fp = fopen(_FILE, "w");
-TEST_NOTEQUAL(fp, NULL, "Testing if able to write file");
+if(!fp) TEST_FATAL("Unable to write to the file");
// Open for write initially empties the file content, thus provoking a changed event.
TEST_TRUE(inotify.hasEvents(), "Test if the modified event was triggered.");
@@ -286,19 +491,86 @@ TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "Test if the event was a modif
fclose(fp);
TEST_TRUE(inotify.hasEvents(), "Test if the close event was triggered.");
-TEST_TRUE(inotify.getNextEvent().isCloseEvent(), "Test if the event was a close event.");
+TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "Test if the event was a close event.");
+// Rename file
+rename(_FILE, _FILE"2");
+TEST_TRUE(inotify.hasEvents(), "Test if the rename event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isMoveSelfEvent(), "Test if the event was a move self event.");
// Delete file
-unlink(_FILE);
+unlink(_FILE"2");
// Unlink initially counts down the link counter, which provokes an attributes changed event.
-TEST_EQUAL(inotify.hasEvents(), true, "Test if the delete event was triggered.");
-TEST_EQUAL(inotify.getNextEvent().isAttributeChangeEvent(), true, "Test if the event was an attribute change event.");
+TEST_TRUE(inotify.hasEvents(), "Test if the delete event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isAttributeChangeEvent(), "Test if the event was an attribute change event.");
// Since the linkcount should now be zero, the file should be deleted.
-TEST_EQUAL(inotify.hasEvents(), true, "Test if the delete event was triggered.");
-TEST_EQUAL(inotify.getNextEvent().isDeleteEvent(), true, "Test if the event was a delete event.");
+TEST_TRUE(inotify.hasEvents(), "Test if the delete event was triggered.");
+TEST_TRUE(inotify.getNextEvent().isDeleteSelfEvent(), "Test if the event was a delete self event.");
+
+// Watch is removed upon delete.
+//inotify.remove(_FILE);
+
+TEST_TRUE(inotify.hasEvents(), "Test if the delete event triggered an ignored event.");
+TEST_TRUE(inotify.getNextEvent().isIgnoredEvent(), "Test if the event was an ignored event.");
+
+// Create file again
+fp = fopen(_FILE, "w");
+if(!fp) TEST_FATAL("Unable to write to the file");
+fprintf(fp, "something");
+fclose(fp);
+
+inotify.addFile(_FILE);
+inotify.remove(_FILE);
+
+TEST_TRUE(inotify.hasEvents(), "Test if the call to remove triggered an ignored event.");
+TEST_TRUE(inotify.getNextEvent().isIgnoredEvent(), "Test if the event was an ignored event.");
+
+// Delete file
+unlink(_FILE);
+inotify.getNextEvent();
+TEST_FALSE(inotify.hasEvents(), "Test if the delete event was ignored.");
+
+TEST_FALSE(inotify.hasEvents(), "Test if the event queue is now empty.");
+
+TEST_MSG("Positive tests on directory watch.");
+
+if(mkdir(_DIR, 0777) == -1) TEST_FATAL("Could not create test dir.");
+inotify.addDirectory(_DIR, WATCH_DEEP_FOLLOW);
+
+// Create file again
+fp = fopen(_DIR"/file1", "w");
+if(!fp) TEST_FATAL("Unable to write to the file");
+fprintf(fp, "something");
+fclose(fp);
+
+TEST_TRUE(inotify.hasEvents(), "Test if the file creation triggered events.");
+TEST_TRUE(inotify.getNextEvent().isCreateEvent(), "...");
+TEST_TRUE(inotify.getNextEvent().isOpenEvent(), "...");
+TEST_TRUE(inotify.getNextEvent().isModifyEvent(), "...");
+TEST_TRUE(inotify.getNextEvent().isCloseWriteEvent(), "...");
+
+rename(_DIR"/file1", _DIR"/file2");
+TEST_TRUE(inotify.hasEvents(), "Test if the file renaming triggered events.");
+TEST_TRUE(inotify.getNextEvent().isMovedFromEvent(), "...");
+TEST_TRUE(inotify.getNextEvent().isMovedToEvent(), "...");
+
+unlink(_DIR"/file2");
+
+if(mkdir(_DIR"/dir", 0777) == -1) TEST_FATAL("Could not create test dir.");
+
+if(mkdir(_DIR"/dir/anotherdir", 0777) == -1) TEST_FATAL("Could not create test dir.");
+
+while(inotify.hasEvents()) inotify.getNextEvent();
+
+rmdir(_DIR"/dir/anotherdir");
+
+rmdir(_DIR"/dir");
+
+rmdir(_DIR);
+
+while(inotify.hasEvents()) inotify.getNextEvent();
TEST_END;