summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/Makefile.am1
-rw-r--r--server/src/inotify.cc372
-rw-r--r--server/src/inotify.h40
-rw-r--r--server/src/macrolist.cc116
-rw-r--r--server/src/macrolist.h8
5 files changed, 465 insertions, 72 deletions
diff --git a/server/src/Makefile.am b/server/src/Makefile.am
index 8d6e347..efe1413 100644
--- a/server/src/Makefile.am
+++ b/server/src/Makefile.am
@@ -59,6 +59,7 @@ macrotool_SOURCES = \
configurationparser.cc \
database.cc \
exception.cc \
+ inotify.cc \
log.cc \
macroheaderparser.cc \
macrolist.cc \
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;
diff --git a/server/src/inotify.h b/server/src/inotify.h
index a2503b3..5199055 100644
--- a/server/src/inotify.h
+++ b/server/src/inotify.h
@@ -35,42 +35,70 @@
#include <map>
#include <list>
+typedef enum {
+ WATCH_SINGLE, // Watch only the specified directory.
+ WATCH_DEEP, // Watch all current subdirs as well
+ WATCH_DEEP_FOLLOW // Watch all current subdir as well as subdirs
+ // being created after the watch is initiated
+} depth_t;
+
+
class INotify {
public:
class Event {
public:
- Event(std::string name, uint32_t mask);
+ Event(struct inotify_event *event, std::string name);
bool isAttributeChangeEvent();
+ bool isCreateEvent();
bool isOpenEvent();
bool isCloseEvent();
+ bool isCloseWriteEvent();
+ bool isCloseNoWriteEvent();
bool isModifyEvent();
+ bool isAccessEvent();
bool isDeleteEvent();
+ bool isDeleteSelfEvent();
+ bool isIgnoredEvent();
+ bool isMoveSelfEvent();
+ bool isMovedFromEvent();
+ bool isMovedToEvent();
std::string name();
+ std::string file();
uint32_t mask();
private:
std::string _name;
+ std::string _file;
uint32_t _mask;
};
INotify();
~INotify();
- void add(std::string name, uint32_t mask = IN_ALL_EVENTS);
+ void addFile(std::string name, uint32_t mask = IN_ALL_EVENTS);
+ void addDirectory(std::string name,
+ depth_t depth = WATCH_SINGLE,
+ uint32_t mask = IN_ALL_EVENTS);
void remove(std::string name);
void remove(int fd);
- void addRecursive(std::string name, uint32_t mask = IN_ALL_EVENTS);
-
bool hasEvents();
Event getNextEvent();
private:
+ class Watch {
+ public:
+ std::string name;
+ uint32_t mask;
+ depth_t depth;
+ };
+
void readEvents();
- int fd;
- std::map<int, std::string> dirmap;
+
+ int ifd;
+ std::map<int, Watch> dirmap;
std::list<Event> eventlist;
};
diff --git a/server/src/macrolist.cc b/server/src/macrolist.cc
index 0d0916d..89abfe8 100644
--- a/server/src/macrolist.cc
+++ b/server/src/macrolist.cc
@@ -33,6 +33,24 @@
#include "debug.h"
#include "macroheaderparser.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static inline bool isdir(std::string name)
+{
+ struct stat s;
+ stat(name.c_str(), &s);
+ return S_ISDIR(s.st_mode);
+}
+
+static inline bool isfile(std::string name)
+{
+ struct stat s;
+ stat(name.c_str(), &s);
+ return S_ISREG(s.st_mode);
+}
+
static std::vector<std::string> listdir(std::string path)
{
std::vector<std::string> files;
@@ -45,11 +63,21 @@ static std::vector<std::string> listdir(std::string path)
struct dirent *d;
while((d = readdir(dir)) != 0) {
- //if(d->d_type == DT_DIR) {
- std::string name = d->d_name;
- if(name.length() >= 4 && name.substr(name.length() - 4) == ".xml")
- files.push_back(name);
- //}
+ if(std::string(d->d_name) == "." || std::string(d->d_name) == "..") continue;
+
+ PRACRO_DEBUG(macrolist, "d_name: %s - d_type: %d\n", d->d_name, d->d_type);
+
+ if(isdir(path + "/" + d->d_name)) {
+ std::vector<std::string> sub = listdir(path + "/" + d->d_name);
+ files.insert(files.end(), sub.begin(), sub.end());
+ continue;
+ }
+
+ if(isfile(path + "/" + d->d_name)) {
+ std::string name = d->d_name;
+ if(name.length() >= 4 && name.substr(name.length() - 4) == ".xml")
+ files.push_back(path + "/" + name);
+ }
}
closedir(dir);
@@ -60,18 +88,16 @@ MacroList::MacroList(std::string macropath)
{
MutexAutolock lock(mutex);
+ inotify.addDirectory(macropath, WATCH_DEEP_FOLLOW,
+ IN_CLOSE_WRITE |
+ IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF |
+ IN_DELETE | IN_DELETE_SELF);
+
this->macropath = macropath;
std::vector<std::string> macros = listdir(macropath);
std::vector<std::string>::iterator i = macros.begin();
while(i != macros.end()) {
- MacroHeaderParser parser(macropath + "/" + *i);
- try {
- parser.parse();
- Macro *macro = parser.getMacro();
- (*this)[macro->attributes["name"]][VersionStr(macro->attributes["version"])] = *i;
- } catch(Exception &e) {
- PRACRO_WARN(macrolist, "Skipping %s: %s\n", i->c_str(), e.what());
- }
+ addFile(*i);
i++;
}
@@ -91,19 +117,77 @@ MacroList::MacroList(std::string macropath)
}
}
+bool MacroList::removeFile(std::string file)
+{
+ // Check if the file is already in the tree.
+ iterator i = begin();
+ while(i != end()) {
+ MacroListItem::iterator j = i->second.begin();
+ while(j != i->second.end()) {
+ if(file == j->second) {
+ PRACRO_DEBUG(macrolist, "Removing file: %s\n", file.c_str());
+ i->second.erase(j->first);
+ /*
+ if(i->second.size() == 0) erase(i->first);
+ */
+ return true;
+ }
+ j++;
+ }
+ i++;
+ }
+
+ return false;
+}
+
+void MacroList::addFile(std::string file)
+{
+ PRACRO_DEBUG(macrolist, "Adding file: %s\n", file.c_str());
+ MacroHeaderParser parser(file);
+ try {
+ parser.parse();
+ Macro *macro = parser.getMacro();
+ (*this)[macro->attributes["name"]][VersionStr(macro->attributes["version"])] = file;
+ } catch(Exception &e) {
+ PRACRO_WARN(macrolist, "Skipping %s: %s\n", file.c_str(), e.what());
+ }
+}
+
+void MacroList::updateFile(std::string file)
+{
+ removeFile(file);
+ addFile(file);
+}
+
+void MacroList::updateList()
+{
+ while(inotify.hasEvents()) {
+ INotify::Event event = inotify.getNextEvent();
+ if(event.isCloseWriteEvent()) updateFile(event.name()+"/"+event.file());
+ if(event.isMovedFromEvent()) removeFile(event.name()+"/"+event.file());
+ if(event.isMovedToEvent()) updateFile(event.name()+"/"+event.file());
+ if(event.isDeleteEvent()) removeFile(event.name()+"/"+event.file());
+
+ if(event.isMoveSelfEvent()) {/* TODO: what to do here? */}
+ if(event.isDeleteSelfEvent()) {/* TODO: what to do here? */}
+ }
+}
+
std::string MacroList::getLatestVersion(std::string macro) throw(Exception)
{
MutexAutolock lock(mutex);
+ updateList();
+
if(find(macro) == end()) throw Exception("Macro ["+macro+"] does not exist");
MacroListItem mli = (*this)[macro];
- if(mli.size() == 0) return "";
+ if(mli.size() == 0) throw Exception("Macro ["+macro+"] does not exist");
PRACRO_DEBUG(macrolist, "Search for %s - found %s v%s\n",
macro.c_str(),
- (macropath + "/" + mli.begin()->second).c_str(),
+ mli.begin()->second.c_str(),
((std::string)mli.begin()->first).c_str());
- return macropath + "/" + mli.begin()->second;
+ return mli.begin()->second;
}
#ifdef TEST_MACROLIST
diff --git a/server/src/macrolist.h b/server/src/macrolist.h
index 6460f8b..4c73ded 100644
--- a/server/src/macrolist.h
+++ b/server/src/macrolist.h
@@ -33,6 +33,8 @@
#include "versionstr.h"
#include "mutex.h"
+#include "inotify.h"
+
#include "exception.h"
/**
@@ -67,7 +69,13 @@ public:
std::string getLatestVersion(std::string macro) throw(Exception);
private:
+ bool removeFile(std::string file);
+ void addFile(std::string file);
+ void updateFile(std::string file);
+ void updateList();
+
Mutex mutex;
+ INotify inotify;
std::string macropath;
};