diff options
| -rw-r--r-- | server/src/Makefile.am | 1 | ||||
| -rw-r--r-- | server/src/inotify.cc | 372 | ||||
| -rw-r--r-- | server/src/inotify.h | 40 | ||||
| -rw-r--r-- | server/src/macrolist.cc | 116 | ||||
| -rw-r--r-- | server/src/macrolist.h | 8 | 
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;  }; | 
