--- a/src/index/rclmonrcv.cpp
+++ b/src/index/rclmonrcv.cpp
@@ -41,9 +41,9 @@
virtual ~RclMonitor() {}
virtual bool addWatch(const string& path, bool isDir) = 0;
virtual bool getEvent(RclMonEvent& ev, int secs = -1) = 0;
- virtual bool ok() = 0;
+ virtual bool ok() const = 0;
// Does this monitor generate 'exist' events at startup?
- virtual bool generatesExist() = 0;
+ virtual bool generatesExist() const = 0;
};
// Monitor factory. We only have one compiled-in kind at a time, no
@@ -96,6 +96,16 @@
flg == FsTreeWalker::FtwRegular) {
// Have to synthetize events for regular files existence
// at startup because the monitor does not do it
+ // Note 2011-09-29: no sure this is actually needed. We just ran
+ // an incremental indexing pass (before starting the
+ // monitor). Why go over the files once more ? The only
+ // reason I can see would be to catch modifications that
+ // happen between the incremental and the start of
+ // monitoring ? There should be another way: maybe start
+ // monitoring without actually handling events (just
+ // queue), then run incremental then start handling
+ // events ? But we also have to do it on a directory
+ // move! So keep it
RclMonEvent ev;
ev.m_path = fn;
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
@@ -180,13 +190,21 @@
// timeout so that an intr will be detected
if (mon->getEvent(ev, 2)) {
if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) {
- // Add watch after checking that this doesn't match
- // ignored files or paths
- string name = path_getsimple(ev.m_path);
- if (!walker.inSkippedNames(name) &&
- !walker.inSkippedPaths(ev.m_path))
- mon->addWatch(ev.m_path, true);
+ // Recursive addwatch: there may already be stuff
+ // inside this directory. Ie: files were quickly
+ // created, or this is actually the target of a
+ // directory move. This is necessary for inotify, but
+ // it seems that fam/gamin is doing the job for us so
+ // that we are generating double events here (no big
+ // deal as prc will sort/merge).
+ if (!walker.inSkippedNames(path_getsimple(ev.m_path)) &&
+ !walker.inSkippedPaths(ev.m_path)) {
+ LOGDEB(("rclMonRcvRun: walking new dir %s\n",
+ ev.m_path.c_str()));
+ walker.walk(ev.m_path, walkcb);
+ }
}
+
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
queue->pushEvent(ev);
}
@@ -195,6 +213,24 @@
queue->setTerminate();
LOGINFO(("rclMonRcvRun: monrcv thread routine returning\n"));
return 0;
+}
+
+// Utility routine used by both the fam/gamin and inotify versions to get
+// rid of the id-path translation for a moved dir
+bool eraseWatchSubTree(map<int, string>& idtopath, const string& top)
+{
+ bool found = false;
+ LOGDEB0(("Clearing map for [%s]\n", top.c_str()));
+ map<int,string>::iterator it = idtopath.begin();
+ while (it != idtopath.end()) {
+ if (it->second.find(top) == 0) {
+ found = true;
+ idtopath.erase(it++);
+ } else {
+ it++;
+ }
+ }
+ return found;
}
// We dont compile both the inotify and the fam interface and inotify
@@ -215,8 +251,8 @@
virtual ~RclFAM();
virtual bool addWatch(const string& path, bool isdir);
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
- bool ok() {return m_ok;}
- virtual bool generatesExist() {return true;}
+ bool ok() const {return m_ok;}
+ virtual bool generatesExist() const {return true;}
private:
bool m_ok;
@@ -225,7 +261,7 @@
FAMClose(&m_conn);
m_ok = false;
}
- map<int,string> m_reqtopath;
+ map<int,string> m_idtopath;
const char *event_name(int code);
};
@@ -286,7 +322,7 @@
return false;
}
}
- m_reqtopath[req.reqnum] = path;
+ m_idtopath[req.reqnum] = path;
return true;
}
@@ -346,12 +382,13 @@
map<int,string>::const_iterator it;
if ((fe.filename[0] != '/') &&
- (it = m_reqtopath.find(fe.fr.reqnum)) != m_reqtopath.end()) {
+ (it = m_idtopath.find(fe.fr.reqnum)) != m_idtopath.end()) {
ev.m_path = path_cat(it->second, fe.filename);
} else {
ev.m_path = fe.filename;
}
- MONDEB(("RclFAM::getEvent: %-12s %s\n",
+
+ LOGDEB(("RclFAM::getEvent: %-12s %s\n",
event_name(fe.code), ev.m_path.c_str()));
switch (fe.code) {
@@ -367,13 +404,15 @@
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
break;
+ case FAMMoved:
case FAMDeleted:
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
- break;
-
- case FAMMoved: /* Never generated it seems */
- LOGINFO(("RclFAM::getEvent: got move event !\n"));
- ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
+ // We would like to signal a directory here to enable cleaning
+ // the subtree (on a dir move), but can't test the actual file
+ // which is gone. Let's rely on the fact that a directory
+ // should be watched
+ if (eraseWatchSubTree(m_idtopath, ev.m_path))
+ ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
break;
case FAMStartExecuting:
@@ -401,17 +440,29 @@
class RclIntf : public RclMonitor {
public:
- RclIntf();
- virtual ~RclIntf();
+ RclIntf()
+ : m_ok(false), m_fd(-1), m_evp(0), m_ep(0)
+ {
+ if ((m_fd = inotify_init()) < 0) {
+ LOGERR(("RclIntf:: inotify_init failed, errno %d\n", errno));
+ return;
+ }
+ m_ok = true;
+ }
+ virtual ~RclIntf()
+ {
+ close();
+ }
+
virtual bool addWatch(const string& path, bool isdir);
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
- bool ok() {return m_ok;}
- virtual bool generatesExist() {return false;}
+ bool ok() const {return m_ok;}
+ virtual bool generatesExist() const {return false;}
private:
bool m_ok;
int m_fd;
- map<int,string> m_wdtopath; // Watch descriptor to name
+ map<int,string> m_idtopath; // Watch descriptor to name
#define EVBUFSIZE (32*1024)
char m_evbuf[EVBUFSIZE]; // Event buffer
char *m_evp; // Pointer to next event or 0
@@ -455,40 +506,27 @@
};
}
-RclIntf::RclIntf()
- : m_ok(false), m_fd(-1), m_evp(0), m_ep(0)
-{
- if ((m_fd = inotify_init()) < 0) {
- LOGERR(("RclIntf::RclIntf: inotify_init failed, errno %d\n", errno));
- return;
- }
- m_ok = true;
-}
-
-RclIntf::~RclIntf()
-{
- close();
-}
-
bool RclIntf::addWatch(const string& path, bool)
{
if (!ok())
- return false;
+ return false;
MONDEB(("RclIntf::addWatch: adding %s\n", path.c_str()));
// CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs
uint32_t mask = IN_MODIFY | IN_CREATE
- | IN_MOVED_FROM | IN_MOVED_TO
- | IN_DELETE
+ | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE
#ifdef IN_DONT_FOLLOW
| IN_DONT_FOLLOW
#endif
-;
+#ifdef IN_EXCL_UNLINK
+ | IN_EXCL_UNLINK
+#endif
+ ;
int wd;
if ((wd = inotify_add_watch(m_fd, path.c_str(), mask)) < 0) {
LOGERR(("RclIntf::addWatch: inotify_add_watch failed\n"));
return false;
}
- m_wdtopath[wd] = path;
+ m_idtopath[wd] = path;
return true;
}
@@ -544,8 +582,8 @@
m_evp = m_ep = 0;
map<int,string>::const_iterator it;
- if ((it = m_wdtopath.find(evp->wd)) == m_wdtopath.end()) {
- LOGERR(("RclIntf::getEvent: unknown wd\n"));
+ if ((it = m_idtopath.find(evp->wd)) == m_idtopath.end()) {
+ LOGERR(("RclIntf::getEvent: unknown wd %d\n", evp->wd));
return true;
}
ev.m_path = it->second;
@@ -557,16 +595,35 @@
MONDEB(("RclIntf::getEvent: %-12s %s\n",
event_name(evp->mask), ev.m_path.c_str()));
- if (evp->mask & (IN_MODIFY | IN_MOVED_TO)) {
+ if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) {
+ // We get this when a directory is renamed. Erase the subtree
+ // entries in the map. The subsequent MOVED_TO will recreate
+ // them. This is probably not needed because the watches
+ // actually still exist in the kernel, so that the wds
+ // returned by future addwatches will be the old ones, and the
+ // map will be updated in place. But still, this feels safer
+ eraseWatchSubTree(m_idtopath, ev.m_path);
+ }
+
+
+ if (evp->mask & (IN_MODIFY)) {
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
} else if (evp->mask & (IN_DELETE | IN_MOVED_FROM)) {
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
- } else if (evp->mask & (IN_CREATE)) {
- if (path_isdir(ev.m_path)) {
+ if (evp->mask & IN_ISDIR)
+ ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
+ } else if (evp->mask & (IN_CREATE | IN_MOVED_TO)) {
+ if (evp->mask & IN_ISDIR) {
ev.m_etyp = RclMonEvent::RCLEVT_DIRCREATE;
} else {
// Return null event. Will get modify event later
return true;
+ }
+ } else if (evp->mask & (IN_IGNORED)) {
+ if (!m_idtopath.erase(evp->wd)) {
+ LOGDEB0(("Got IGNORE event for unknown watch\n"));
+ } else {
+ eraseWatchSubTree(m_idtopath, ev.m_path);
}
} else {
LOGDEB(("RclIntf::getEvent: unhandled event %s 0x%x %s\n",