Switch to unified view

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;