|
a/src/index/rclmonrcv.cpp |
|
b/src/index/rclmonrcv.cpp |
|
... |
|
... |
39 |
public:
|
39 |
public:
|
40 |
RclMonitor(){}
|
40 |
RclMonitor(){}
|
41 |
virtual ~RclMonitor() {}
|
41 |
virtual ~RclMonitor() {}
|
42 |
virtual bool addWatch(const string& path, bool isDir) = 0;
|
42 |
virtual bool addWatch(const string& path, bool isDir) = 0;
|
43 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1) = 0;
|
43 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1) = 0;
|
44 |
virtual bool ok() = 0;
|
44 |
virtual bool ok() const = 0;
|
45 |
// Does this monitor generate 'exist' events at startup?
|
45 |
// Does this monitor generate 'exist' events at startup?
|
46 |
virtual bool generatesExist() = 0;
|
46 |
virtual bool generatesExist() const = 0;
|
47 |
};
|
47 |
};
|
48 |
|
48 |
|
49 |
// Monitor factory. We only have one compiled-in kind at a time, no
|
49 |
// Monitor factory. We only have one compiled-in kind at a time, no
|
50 |
// need for a 'kind' parameter
|
50 |
// need for a 'kind' parameter
|
51 |
static RclMonitor *makeMonitor();
|
51 |
static RclMonitor *makeMonitor();
|
|
... |
|
... |
94 |
return FsTreeWalker::FtwError;
|
94 |
return FsTreeWalker::FtwError;
|
95 |
} else if (!m_mon->generatesExist() &&
|
95 |
} else if (!m_mon->generatesExist() &&
|
96 |
flg == FsTreeWalker::FtwRegular) {
|
96 |
flg == FsTreeWalker::FtwRegular) {
|
97 |
// Have to synthetize events for regular files existence
|
97 |
// Have to synthetize events for regular files existence
|
98 |
// at startup because the monitor does not do it
|
98 |
// at startup because the monitor does not do it
|
|
|
99 |
// Note 2011-09-29: no sure this is actually needed. We just ran
|
|
|
100 |
// an incremental indexing pass (before starting the
|
|
|
101 |
// monitor). Why go over the files once more ? The only
|
|
|
102 |
// reason I can see would be to catch modifications that
|
|
|
103 |
// happen between the incremental and the start of
|
|
|
104 |
// monitoring ? There should be another way: maybe start
|
|
|
105 |
// monitoring without actually handling events (just
|
|
|
106 |
// queue), then run incremental then start handling
|
|
|
107 |
// events ? But we also have to do it on a directory
|
|
|
108 |
// move! So keep it
|
99 |
RclMonEvent ev;
|
109 |
RclMonEvent ev;
|
100 |
ev.m_path = fn;
|
110 |
ev.m_path = fn;
|
101 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
111 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
102 |
m_queue->pushEvent(ev);
|
112 |
m_queue->pushEvent(ev);
|
103 |
}
|
113 |
}
|
|
... |
|
... |
178 |
// (it goes to the main thread, from which I tried to close or
|
188 |
// (it goes to the main thread, from which I tried to close or
|
179 |
// write to the select fd, with no effect). So set a
|
189 |
// write to the select fd, with no effect). So set a
|
180 |
// timeout so that an intr will be detected
|
190 |
// timeout so that an intr will be detected
|
181 |
if (mon->getEvent(ev, 2)) {
|
191 |
if (mon->getEvent(ev, 2)) {
|
182 |
if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) {
|
192 |
if (ev.m_etyp == RclMonEvent::RCLEVT_DIRCREATE) {
|
183 |
// Add watch after checking that this doesn't match
|
193 |
// Recursive addwatch: there may already be stuff
|
184 |
// ignored files or paths
|
194 |
// inside this directory. Ie: files were quickly
|
185 |
string name = path_getsimple(ev.m_path);
|
195 |
// created, or this is actually the target of a
|
|
|
196 |
// directory move. This is necessary for inotify, but
|
|
|
197 |
// it seems that fam/gamin is doing the job for us so
|
|
|
198 |
// that we are generating double events here (no big
|
|
|
199 |
// deal as prc will sort/merge).
|
186 |
if (!walker.inSkippedNames(name) &&
|
200 |
if (!walker.inSkippedNames(path_getsimple(ev.m_path)) &&
|
187 |
!walker.inSkippedPaths(ev.m_path))
|
201 |
!walker.inSkippedPaths(ev.m_path)) {
|
188 |
mon->addWatch(ev.m_path, true);
|
202 |
LOGDEB(("rclMonRcvRun: walking new dir %s\n",
|
|
|
203 |
ev.m_path.c_str()));
|
|
|
204 |
walker.walk(ev.m_path, walkcb);
|
|
|
205 |
}
|
189 |
}
|
206 |
}
|
|
|
207 |
|
190 |
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
|
208 |
if (ev.m_etyp != RclMonEvent::RCLEVT_NONE)
|
191 |
queue->pushEvent(ev);
|
209 |
queue->pushEvent(ev);
|
192 |
}
|
210 |
}
|
193 |
}
|
211 |
}
|
194 |
|
212 |
|
195 |
queue->setTerminate();
|
213 |
queue->setTerminate();
|
196 |
LOGINFO(("rclMonRcvRun: monrcv thread routine returning\n"));
|
214 |
LOGINFO(("rclMonRcvRun: monrcv thread routine returning\n"));
|
197 |
return 0;
|
215 |
return 0;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
// Utility routine used by both the fam/gamin and inotify versions to get
|
|
|
219 |
// rid of the id-path translation for a moved dir
|
|
|
220 |
bool eraseWatchSubTree(map<int, string>& idtopath, const string& top)
|
|
|
221 |
{
|
|
|
222 |
bool found = false;
|
|
|
223 |
LOGDEB0(("Clearing map for [%s]\n", top.c_str()));
|
|
|
224 |
map<int,string>::iterator it = idtopath.begin();
|
|
|
225 |
while (it != idtopath.end()) {
|
|
|
226 |
if (it->second.find(top) == 0) {
|
|
|
227 |
found = true;
|
|
|
228 |
idtopath.erase(it++);
|
|
|
229 |
} else {
|
|
|
230 |
it++;
|
|
|
231 |
}
|
|
|
232 |
}
|
|
|
233 |
return found;
|
198 |
}
|
234 |
}
|
199 |
|
235 |
|
200 |
// We dont compile both the inotify and the fam interface and inotify
|
236 |
// We dont compile both the inotify and the fam interface and inotify
|
201 |
// has preference
|
237 |
// has preference
|
202 |
#ifndef RCL_USE_INOTIFY
|
238 |
#ifndef RCL_USE_INOTIFY
|
|
... |
|
... |
213 |
public:
|
249 |
public:
|
214 |
RclFAM();
|
250 |
RclFAM();
|
215 |
virtual ~RclFAM();
|
251 |
virtual ~RclFAM();
|
216 |
virtual bool addWatch(const string& path, bool isdir);
|
252 |
virtual bool addWatch(const string& path, bool isdir);
|
217 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
|
253 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
|
218 |
bool ok() {return m_ok;}
|
254 |
bool ok() const {return m_ok;}
|
219 |
virtual bool generatesExist() {return true;}
|
255 |
virtual bool generatesExist() const {return true;}
|
220 |
|
256 |
|
221 |
private:
|
257 |
private:
|
222 |
bool m_ok;
|
258 |
bool m_ok;
|
223 |
FAMConnection m_conn;
|
259 |
FAMConnection m_conn;
|
224 |
void close() {
|
260 |
void close() {
|
225 |
FAMClose(&m_conn);
|
261 |
FAMClose(&m_conn);
|
226 |
m_ok = false;
|
262 |
m_ok = false;
|
227 |
}
|
263 |
}
|
228 |
map<int,string> m_reqtopath;
|
264 |
map<int,string> m_idtopath;
|
229 |
const char *event_name(int code);
|
265 |
const char *event_name(int code);
|
230 |
};
|
266 |
};
|
231 |
|
267 |
|
232 |
// Translate event code to string (debug)
|
268 |
// Translate event code to string (debug)
|
233 |
const char *RclFAM::event_name(int code)
|
269 |
const char *RclFAM::event_name(int code)
|
|
... |
|
... |
284 |
if (FAMMonitorFile(&m_conn, path.c_str(), &req, 0) != 0) {
|
320 |
if (FAMMonitorFile(&m_conn, path.c_str(), &req, 0) != 0) {
|
285 |
LOGERR(("RclFAM::addWatch: FAMMonitorFile failed\n"));
|
321 |
LOGERR(("RclFAM::addWatch: FAMMonitorFile failed\n"));
|
286 |
return false;
|
322 |
return false;
|
287 |
}
|
323 |
}
|
288 |
}
|
324 |
}
|
289 |
m_reqtopath[req.reqnum] = path;
|
325 |
m_idtopath[req.reqnum] = path;
|
290 |
return true;
|
326 |
return true;
|
291 |
}
|
327 |
}
|
292 |
|
328 |
|
293 |
// Note: return false only for queue empty or error
|
329 |
// Note: return false only for queue empty or error
|
294 |
// Return EVT_NONE for bad event to keep queue processing going
|
330 |
// Return EVT_NONE for bad event to keep queue processing going
|
|
... |
|
... |
344 |
}
|
380 |
}
|
345 |
MONDEB(("RclFAM::getEvent: FAMNextEvent returned\n"));
|
381 |
MONDEB(("RclFAM::getEvent: FAMNextEvent returned\n"));
|
346 |
|
382 |
|
347 |
map<int,string>::const_iterator it;
|
383 |
map<int,string>::const_iterator it;
|
348 |
if ((fe.filename[0] != '/') &&
|
384 |
if ((fe.filename[0] != '/') &&
|
349 |
(it = m_reqtopath.find(fe.fr.reqnum)) != m_reqtopath.end()) {
|
385 |
(it = m_idtopath.find(fe.fr.reqnum)) != m_idtopath.end()) {
|
350 |
ev.m_path = path_cat(it->second, fe.filename);
|
386 |
ev.m_path = path_cat(it->second, fe.filename);
|
351 |
} else {
|
387 |
} else {
|
352 |
ev.m_path = fe.filename;
|
388 |
ev.m_path = fe.filename;
|
353 |
}
|
389 |
}
|
|
|
390 |
|
354 |
MONDEB(("RclFAM::getEvent: %-12s %s\n",
|
391 |
LOGDEB(("RclFAM::getEvent: %-12s %s\n",
|
355 |
event_name(fe.code), ev.m_path.c_str()));
|
392 |
event_name(fe.code), ev.m_path.c_str()));
|
356 |
|
393 |
|
357 |
switch (fe.code) {
|
394 |
switch (fe.code) {
|
358 |
case FAMCreated:
|
395 |
case FAMCreated:
|
359 |
if (path_isdir(ev.m_path)) {
|
396 |
if (path_isdir(ev.m_path)) {
|
|
... |
|
... |
365 |
case FAMExists:
|
402 |
case FAMExists:
|
366 |
// Let the other side sort out the status of this file vs the db
|
403 |
// Let the other side sort out the status of this file vs the db
|
367 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
404 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
368 |
break;
|
405 |
break;
|
369 |
|
406 |
|
|
|
407 |
case FAMMoved:
|
370 |
case FAMDeleted:
|
408 |
case FAMDeleted:
|
371 |
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
|
409 |
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
|
372 |
break;
|
410 |
// We would like to signal a directory here to enable cleaning
|
373 |
|
411 |
// the subtree (on a dir move), but can't test the actual file
|
374 |
case FAMMoved: /* Never generated it seems */
|
412 |
// which is gone. Let's rely on the fact that a directory
|
375 |
LOGINFO(("RclFAM::getEvent: got move event !\n"));
|
413 |
// should be watched
|
|
|
414 |
if (eraseWatchSubTree(m_idtopath, ev.m_path))
|
376 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
415 |
ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
|
377 |
break;
|
416 |
break;
|
378 |
|
417 |
|
379 |
case FAMStartExecuting:
|
418 |
case FAMStartExecuting:
|
380 |
case FAMStopExecuting:
|
419 |
case FAMStopExecuting:
|
381 |
case FAMAcknowledge:
|
420 |
case FAMAcknowledge:
|
|
... |
|
... |
399 |
#include <sys/inotify.h>
|
438 |
#include <sys/inotify.h>
|
400 |
#include <sys/select.h>
|
439 |
#include <sys/select.h>
|
401 |
|
440 |
|
402 |
class RclIntf : public RclMonitor {
|
441 |
class RclIntf : public RclMonitor {
|
403 |
public:
|
442 |
public:
|
404 |
RclIntf();
|
443 |
RclIntf()
|
|
|
444 |
: m_ok(false), m_fd(-1), m_evp(0), m_ep(0)
|
|
|
445 |
{
|
|
|
446 |
if ((m_fd = inotify_init()) < 0) {
|
|
|
447 |
LOGERR(("RclIntf:: inotify_init failed, errno %d\n", errno));
|
|
|
448 |
return;
|
|
|
449 |
}
|
|
|
450 |
m_ok = true;
|
|
|
451 |
}
|
405 |
virtual ~RclIntf();
|
452 |
virtual ~RclIntf()
|
|
|
453 |
{
|
|
|
454 |
close();
|
|
|
455 |
}
|
|
|
456 |
|
406 |
virtual bool addWatch(const string& path, bool isdir);
|
457 |
virtual bool addWatch(const string& path, bool isdir);
|
407 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
|
458 |
virtual bool getEvent(RclMonEvent& ev, int secs = -1);
|
408 |
bool ok() {return m_ok;}
|
459 |
bool ok() const {return m_ok;}
|
409 |
virtual bool generatesExist() {return false;}
|
460 |
virtual bool generatesExist() const {return false;}
|
410 |
|
461 |
|
411 |
private:
|
462 |
private:
|
412 |
bool m_ok;
|
463 |
bool m_ok;
|
413 |
int m_fd;
|
464 |
int m_fd;
|
414 |
map<int,string> m_wdtopath; // Watch descriptor to name
|
465 |
map<int,string> m_idtopath; // Watch descriptor to name
|
415 |
#define EVBUFSIZE (32*1024)
|
466 |
#define EVBUFSIZE (32*1024)
|
416 |
char m_evbuf[EVBUFSIZE]; // Event buffer
|
467 |
char m_evbuf[EVBUFSIZE]; // Event buffer
|
417 |
char *m_evp; // Pointer to next event or 0
|
468 |
char *m_evp; // Pointer to next event or 0
|
418 |
char *m_ep; // Pointer to end of events
|
469 |
char *m_ep; // Pointer to end of events
|
419 |
const char *event_name(int code);
|
470 |
const char *event_name(int code);
|
|
... |
|
... |
453 |
return msg;
|
504 |
return msg;
|
454 |
}
|
505 |
}
|
455 |
};
|
506 |
};
|
456 |
}
|
507 |
}
|
457 |
|
508 |
|
458 |
RclIntf::RclIntf()
|
|
|
459 |
: m_ok(false), m_fd(-1), m_evp(0), m_ep(0)
|
|
|
460 |
{
|
|
|
461 |
if ((m_fd = inotify_init()) < 0) {
|
|
|
462 |
LOGERR(("RclIntf::RclIntf: inotify_init failed, errno %d\n", errno));
|
|
|
463 |
return;
|
|
|
464 |
}
|
|
|
465 |
m_ok = true;
|
|
|
466 |
}
|
|
|
467 |
|
|
|
468 |
RclIntf::~RclIntf()
|
|
|
469 |
{
|
|
|
470 |
close();
|
|
|
471 |
}
|
|
|
472 |
|
|
|
473 |
bool RclIntf::addWatch(const string& path, bool)
|
509 |
bool RclIntf::addWatch(const string& path, bool)
|
474 |
{
|
510 |
{
|
475 |
if (!ok())
|
511 |
if (!ok())
|
476 |
return false;
|
512 |
return false;
|
477 |
MONDEB(("RclIntf::addWatch: adding %s\n", path.c_str()));
|
513 |
MONDEB(("RclIntf::addWatch: adding %s\n", path.c_str()));
|
478 |
// CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs
|
514 |
// CLOSE_WRITE is covered through MODIFY. CREATE is needed for mkdirs
|
479 |
uint32_t mask = IN_MODIFY | IN_CREATE
|
515 |
uint32_t mask = IN_MODIFY | IN_CREATE
|
480 |
| IN_MOVED_FROM | IN_MOVED_TO
|
516 |
| IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE
|
481 |
| IN_DELETE
|
|
|
482 |
#ifdef IN_DONT_FOLLOW
|
517 |
#ifdef IN_DONT_FOLLOW
|
483 |
| IN_DONT_FOLLOW
|
518 |
| IN_DONT_FOLLOW
|
484 |
#endif
|
519 |
#endif
|
|
|
520 |
#ifdef IN_EXCL_UNLINK
|
|
|
521 |
| IN_EXCL_UNLINK
|
|
|
522 |
#endif
|
485 |
;
|
523 |
;
|
486 |
int wd;
|
524 |
int wd;
|
487 |
if ((wd = inotify_add_watch(m_fd, path.c_str(), mask)) < 0) {
|
525 |
if ((wd = inotify_add_watch(m_fd, path.c_str(), mask)) < 0) {
|
488 |
LOGERR(("RclIntf::addWatch: inotify_add_watch failed\n"));
|
526 |
LOGERR(("RclIntf::addWatch: inotify_add_watch failed\n"));
|
489 |
return false;
|
527 |
return false;
|
490 |
}
|
528 |
}
|
491 |
m_wdtopath[wd] = path;
|
529 |
m_idtopath[wd] = path;
|
492 |
return true;
|
530 |
return true;
|
493 |
}
|
531 |
}
|
494 |
|
532 |
|
495 |
// Note: return false only for queue empty or error
|
533 |
// Note: return false only for queue empty or error
|
496 |
// Return EVT_NONE for bad event to keep queue processing going
|
534 |
// Return EVT_NONE for bad event to keep queue processing going
|
|
... |
|
... |
542 |
m_evp += evp->len;
|
580 |
m_evp += evp->len;
|
543 |
if (m_evp >= m_ep)
|
581 |
if (m_evp >= m_ep)
|
544 |
m_evp = m_ep = 0;
|
582 |
m_evp = m_ep = 0;
|
545 |
|
583 |
|
546 |
map<int,string>::const_iterator it;
|
584 |
map<int,string>::const_iterator it;
|
547 |
if ((it = m_wdtopath.find(evp->wd)) == m_wdtopath.end()) {
|
585 |
if ((it = m_idtopath.find(evp->wd)) == m_idtopath.end()) {
|
548 |
LOGERR(("RclIntf::getEvent: unknown wd\n"));
|
586 |
LOGERR(("RclIntf::getEvent: unknown wd %d\n", evp->wd));
|
549 |
return true;
|
587 |
return true;
|
550 |
}
|
588 |
}
|
551 |
ev.m_path = it->second;
|
589 |
ev.m_path = it->second;
|
552 |
|
590 |
|
553 |
if (evp->len > 0) {
|
591 |
if (evp->len > 0) {
|
|
... |
|
... |
555 |
}
|
593 |
}
|
556 |
|
594 |
|
557 |
MONDEB(("RclIntf::getEvent: %-12s %s\n",
|
595 |
MONDEB(("RclIntf::getEvent: %-12s %s\n",
|
558 |
event_name(evp->mask), ev.m_path.c_str()));
|
596 |
event_name(evp->mask), ev.m_path.c_str()));
|
559 |
|
597 |
|
|
|
598 |
if ((evp->mask & IN_MOVED_FROM) && (evp->mask & IN_ISDIR)) {
|
|
|
599 |
// We get this when a directory is renamed. Erase the subtree
|
|
|
600 |
// entries in the map. The subsequent MOVED_TO will recreate
|
|
|
601 |
// them. This is probably not needed because the watches
|
|
|
602 |
// actually still exist in the kernel, so that the wds
|
|
|
603 |
// returned by future addwatches will be the old ones, and the
|
|
|
604 |
// map will be updated in place. But still, this feels safer
|
|
|
605 |
eraseWatchSubTree(m_idtopath, ev.m_path);
|
|
|
606 |
}
|
|
|
607 |
|
|
|
608 |
|
560 |
if (evp->mask & (IN_MODIFY | IN_MOVED_TO)) {
|
609 |
if (evp->mask & (IN_MODIFY)) {
|
561 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
610 |
ev.m_etyp = RclMonEvent::RCLEVT_MODIFY;
|
562 |
} else if (evp->mask & (IN_DELETE | IN_MOVED_FROM)) {
|
611 |
} else if (evp->mask & (IN_DELETE | IN_MOVED_FROM)) {
|
563 |
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
|
612 |
ev.m_etyp = RclMonEvent::RCLEVT_DELETE;
|
|
|
613 |
if (evp->mask & IN_ISDIR)
|
|
|
614 |
ev.m_etyp |= RclMonEvent::RCLEVT_ISDIR;
|
564 |
} else if (evp->mask & (IN_CREATE)) {
|
615 |
} else if (evp->mask & (IN_CREATE | IN_MOVED_TO)) {
|
565 |
if (path_isdir(ev.m_path)) {
|
616 |
if (evp->mask & IN_ISDIR) {
|
566 |
ev.m_etyp = RclMonEvent::RCLEVT_DIRCREATE;
|
617 |
ev.m_etyp = RclMonEvent::RCLEVT_DIRCREATE;
|
567 |
} else {
|
618 |
} else {
|
568 |
// Return null event. Will get modify event later
|
619 |
// Return null event. Will get modify event later
|
569 |
return true;
|
620 |
return true;
|
|
|
621 |
}
|
|
|
622 |
} else if (evp->mask & (IN_IGNORED)) {
|
|
|
623 |
if (!m_idtopath.erase(evp->wd)) {
|
|
|
624 |
LOGDEB0(("Got IGNORE event for unknown watch\n"));
|
|
|
625 |
} else {
|
|
|
626 |
eraseWatchSubTree(m_idtopath, ev.m_path);
|
570 |
}
|
627 |
}
|
571 |
} else {
|
628 |
} else {
|
572 |
LOGDEB(("RclIntf::getEvent: unhandled event %s 0x%x %s\n",
|
629 |
LOGDEB(("RclIntf::getEvent: unhandled event %s 0x%x %s\n",
|
573 |
event_name(evp->mask), evp->mask, ev.m_path.c_str()));
|
630 |
event_name(evp->mask), evp->mask, ev.m_path.c_str()));
|
574 |
return true;
|
631 |
return true;
|