~ubuntu-branches/ubuntu/oneiric/strigi/oneiric

« back to all changes in this revision

Viewing changes to src/daemon/eventlistener/fslistener.cpp

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2011-09-24 17:12:15 UTC
  • mfrom: (1.2.6 upstream)
  • mto: This revision was merged to the branch mainline in revision 44.
  • Revision ID: package-import@ubuntu.com-20110924171215-zmbi1f77jntvz65h
Tags: upstream-0.7.6
ImportĀ upstreamĀ versionĀ 0.7.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* This file is part of Strigi Desktop Search
2
 
 *
3
 
 * Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Library General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Library General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Library General Public License
16
 
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 
 * Boston, MA 02110-1301, USA.
19
 
 */
20
 
#include "fslistener.h"
21
 
 
22
 
#include "combinedindexmanager.h"
23
 
#include "event.h"
24
 
#include "eventlistenerqueue.h"
25
 
#include "filelister.h"
26
 
#include "indexreader.h"
27
 
#include "pollinglistener.h"
28
 
#include "../strigilogging.h"
29
 
 
30
 
#include <cerrno>
31
 
#include <sys/resource.h>
32
 
#include <sys/select.h>
33
 
#include <sys/types.h>
34
 
#include <sstream>
35
 
#include <vector>
36
 
#include <algorithm>
37
 
 
38
 
using namespace std;
39
 
using namespace Strigi;
40
 
 
41
 
namespace {
42
 
    /*!
43
 
    * @param path string containing path to check
44
 
    * Removes the terminating char to path.
45
 
    * Under Windows that char is '\', '/' under *nix
46
 
    */
47
 
    string fixPath (string path)
48
 
    {
49
 
        if ( path.c_str() == NULL || path.length() == 0 )
50
 
            return "";
51
 
 
52
 
        string temp(path);
53
 
 
54
 
    #ifdef HAVE_WINDOWS_H
55
 
        size_t l= temp.length();
56
 
        char* t = (char*)temp.c_str();
57
 
        for (size_t i=0;i<l;i++){
58
 
            if ( t[i] == '\\' )
59
 
                t[i] = '/';
60
 
        }
61
 
        temp[0] = tolower(temp.at(0));
62
 
    #endif
63
 
 
64
 
        char separator = '/';
65
 
 
66
 
        if (temp[temp.length() - 1 ] == separator)
67
 
            return temp.substr(0, temp.size() - 1);
68
 
 
69
 
        return temp;
70
 
    }
71
 
}
72
 
 
73
 
void calculateDiff(set<string> actualDirs, set<string> reindexDirs,
74
 
                   set<string>& dirsDeleted,set<string>& dirsCreated)
75
 
{
76
 
    set<string>::iterator iter;
77
 
    stringstream strDirsDeleted;
78
 
    stringstream strDirsCreated;
79
 
    stringstream strDirsActual;
80
 
    stringstream strDirsReindex;
81
 
 
82
 
    strDirsDeleted << "Dirs deleted:";
83
 
    strDirsCreated << "Dirs created:";
84
 
    strDirsActual << "Actual dirs:";
85
 
    strDirsReindex << "Reindex dirs:";
86
 
 
87
 
    dirsCreated.clear();
88
 
    dirsDeleted.clear();
89
 
 
90
 
    for (iter = actualDirs.begin(); iter != actualDirs.end(); ++iter)
91
 
        strDirsActual << "\n\t-" << *iter;
92
 
    
93
 
    for (iter = reindexDirs.begin(); iter != reindexDirs.end(); ++iter)
94
 
        strDirsReindex << "\n\t-" << *iter;
95
 
 
96
 
    STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsActual.str())
97
 
    STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsReindex.str())
98
 
 
99
 
    for (iter = actualDirs.begin(); iter != actualDirs.end(); ++iter) {
100
 
        set<string>::iterator match = reindexDirs.find (*iter);
101
 
        if (match == reindexDirs.end()) {
102
 
            dirsDeleted.insert (*iter);
103
 
            strDirsDeleted << "\n\t-" << *iter;
104
 
        }
105
 
        else
106
 
            reindexDirs.erase (match);
107
 
    }
108
 
 
109
 
    for (iter = reindexDirs.begin(); iter != reindexDirs.end(); ++iter) {
110
 
        dirsCreated.insert (*iter);
111
 
        strDirsCreated << "\n\t-" << *iter;
112
 
    }
113
 
 
114
 
    STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsCreated.str())
115
 
    STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsDeleted.str())
116
 
}
117
 
 
118
 
class File
119
 
{
120
 
    public:
121
 
        File(string name, time_t mtime)
122
 
            : m_name (name),
123
 
              m_mtime (mtime)
124
 
            {};
125
 
 
126
 
        string m_name;
127
 
        time_t m_mtime;
128
 
};
129
 
 
130
 
bool operator< (const File& f1, const File& f2)
131
 
{
132
 
    if (f1.m_name.compare(f2.m_name) < 0)
133
 
        return true;
134
 
    else
135
 
        return false;
136
 
}
137
 
 
138
 
class MatchFile
139
 
{
140
 
    string m_name;
141
 
    public:
142
 
        MatchFile( string name)
143
 
            : m_name (name) {};
144
 
 
145
 
        bool operator ()(const File& f)
146
 
            { return (m_name == f.m_name); }
147
 
};
148
 
 
149
 
FsEvent::FsEvent (const string path, const string name)
150
 
{
151
 
    m_file = fixPath(path);
152
 
    if (!name.empty()) {
153
 
        m_file += '/';
154
 
        m_file += name;
155
 
    }
156
 
}
157
 
 
158
 
// improved multimap, stl multimap class doesn't provide a method that returns
159
 
// all the available keys
160
 
typedef map< string, set<File> > iMultimap;
161
 
 
162
 
 
163
 
FsListener::FsListener(const char* name, set<string>& indexedDirs)
164
 
    : EventListener(name)
165
 
{
166
 
    m_bMonitor = true;
167
 
    setState(Idling);
168
 
    m_bInitialized = false;
169
 
    m_bBootstrapped = false;
170
 
    m_bReindexReq = false;
171
 
    m_counter = 0;
172
 
    m_pollingListener = 0;
173
 
 
174
 
    for (set<string>::iterator iter = indexedDirs.begin();
175
 
         iter != indexedDirs.end(); ++iter)
176
 
        m_indexedDirs.insert (fixPath (*iter));
177
 
    
178
 
    STRIGI_MUTEX_INIT (&m_reindexLock);
179
 
}
180
 
 
181
 
FsListener::~FsListener()
182
 
{
183
 
    if (m_pollingListener) {
184
 
        m_pollingListener->stop();
185
 
        delete m_pollingListener;
186
 
        m_pollingListener = 0;
187
 
    }
188
 
    STRIGI_MUTEX_DESTROY (&m_reindexLock);
189
 
}
190
 
 
191
 
void* FsListener::run(void*)
192
 
{
193
 
    while (getState() != Stopping) {
194
 
        if (!m_bBootstrapped)
195
 
            bootstrap();
196
 
        else if (reindexReq())
197
 
            reindex();
198
 
        else
199
 
            watch();
200
 
 
201
 
        if (getState() == Working)
202
 
            setState(Idling);
203
 
    }
204
 
 
205
 
    STRIGI_LOG_DEBUG ("strigi.FsListener.run",
206
 
                      string("exit state: ") + getStringState());
207
 
    return 0;
208
 
}
209
 
 
210
 
bool FsListener::reindexReq()
211
 
{
212
 
    bool ret;
213
 
    
214
 
    STRIGI_MUTEX_LOCK (&m_reindexLock);
215
 
    ret = m_bReindexReq;
216
 
    STRIGI_MUTEX_UNLOCK (&m_reindexLock);
217
 
 
218
 
    return ret;
219
 
}
220
 
 
221
 
void FsListener::getReindexDirs(set<string>& reindexDirs)
222
 
{
223
 
    STRIGI_MUTEX_LOCK (&m_reindexLock);
224
 
    m_bReindexReq = false;
225
 
    reindexDirs = m_reindexDirs;
226
 
    m_reindexDirs.clear();
227
 
    STRIGI_MUTEX_UNLOCK (&m_reindexLock);
228
 
}
229
 
 
230
 
void FsListener::bootstrap()
231
 
{
232
 
    STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap","BOOTSTRAP INIT");
233
 
    
234
 
    set<string> toWatch;
235
 
    vector<Event*> events;
236
 
    iMultimap files;
237
 
    set<string> bootstrapDirs;
238
 
 
239
 
    if (reindexReq())
240
 
        getReindexDirs ( bootstrapDirs);
241
 
    else
242
 
        bootstrapDirs = m_indexedDirs;
243
 
    
244
 
    for (set<string>::iterator iter = bootstrapDirs.begin();
245
 
         iter != bootstrapDirs.end(); ++iter)
246
 
    {
247
 
        toWatch.insert (*iter);
248
 
        
249
 
        DirLister lister(m_pAnalyzerConfiguration);
250
 
        string path;
251
 
        vector<pair<string, struct stat> > dirs;
252
 
 
253
 
        lister.startListing (*iter);
254
 
        int ret = lister.nextDir(path, dirs);
255
 
 
256
 
        while (ret != -1) {
257
 
            if (reindexReq())
258
 
                break;
259
 
            
260
 
            vector<pair<string, struct stat> >::iterator iter;
261
 
            
262
 
            for (iter = dirs.begin(); iter != dirs.end(); ++iter) {
263
 
                struct stat stats = iter->second;
264
 
 
265
 
                if (S_ISDIR(stats.st_mode)) { //dir
266
 
                    toWatch.insert (iter->first);
267
 
                }
268
 
                else if (S_ISREG(stats.st_mode)) { //file
269
 
                    iMultimap::iterator mIter = files.find (path);
270
 
                    File file (iter->first, stats.st_mtime);
271
 
                    if (mIter != files.end())
272
 
                        (mIter->second).insert (file);
273
 
                    else {
274
 
                        set<File> temp;
275
 
                        temp.insert (file);
276
 
                        files.insert(make_pair(path, temp));
277
 
                    }
278
 
                }
279
 
            }
280
 
            
281
 
            ret = lister.nextDir(path, dirs);
282
 
        }
283
 
    }
284
 
 
285
 
    stringstream msg;
286
 
    msg << "there're " << files.size();
287
 
    msg << " keys inside iMultimap";
288
 
    STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
289
 
    
290
 
    for (iMultimap::iterator iter = files.begin(); iter != files.end(); ++iter)
291
 
    {
292
 
        map <string, time_t> indexedFiles;
293
 
        stringstream msg;
294
 
        string path = iter->first;
295
 
 
296
 
        // retrieve all indexed files contained by path
297
 
        m_pManager->indexReader()->getChildren (path, indexedFiles);
298
 
        msg << "there're " << indexedFiles.size();
299
 
        msg << " indexed files associated to dir " << path;
300
 
 
301
 
        STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
302
 
 
303
 
        if (indexedFiles.empty()) {
304
 
            string temp = path + '/';
305
 
            msg.str("");
306
 
            m_pManager->indexReader()->getChildren (temp, indexedFiles);
307
 
            msg << "there're " << indexedFiles.size();
308
 
            msg << " indexed files associated to dir " << temp;
309
 
 
310
 
            STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
311
 
        }
312
 
 
313
 
        if (!indexedFiles.empty()) {
314
 
            // find differences between fs files and indexed ones
315
 
            map<string, time_t>::iterator it = indexedFiles.begin();
316
 
            for ( ; it != indexedFiles.end(); it++) {
317
 
                MatchFile finder (it->first);
318
 
                set<File>::iterator match;
319
 
                match = find_if( (iter->second).begin(),
320
 
                                 (iter->second).end(), finder);
321
 
 
322
 
                if (match == (iter->second).end()) {
323
 
                    // indexed file has been deleted from filesystem
324
 
                    events.push_back (new Event (Event::DELETED, iter->first));
325
 
                }
326
 
                else if ((*match).m_mtime > it->second) {
327
 
                    // file has been updated
328
 
                    events.push_back (new Event (Event::UPDATED, iter->first));
329
 
                }
330
 
                else {
331
 
                    // file has not been modified since index time
332
 
                    (iter->second).erase( match);
333
 
                }
334
 
 
335
 
                if (reindexReq())
336
 
                    break;
337
 
            }
338
 
        }
339
 
 
340
 
        for (set<File>::iterator it = (iter->second).begin();
341
 
             it != (iter->second).end(); ++it)
342
 
        {
343
 
            File file = *it;
344
 
            events.push_back (new Event (Event::CREATED, file.m_name));
345
 
        }
346
 
    }
347
 
 
348
 
    //TODO: check
349
 
    if (reindexReq()) {
350
 
        for (vector<Event*>::iterator iter = events.begin();
351
 
             iter != events.end(); ++iter) {
352
 
            delete *iter;
353
 
        }
354
 
 
355
 
        events.clear();
356
 
    }
357
 
    else {
358
 
        if (events.size() > 0)
359
 
            m_pEventQueue->addEvents (events);
360
 
 
361
 
        addWatches (toWatch);
362
 
 
363
 
        m_bBootstrapped = true;
364
 
        m_indexedDirs = bootstrapDirs;
365
 
    }
366
 
}
367
 
 
368
 
void FsListener::reindex()
369
 
{
370
 
    STRIGI_LOG_DEBUG ("strigi.FsListener.reindex","REINDEX INIT");
371
 
    
372
 
    if (m_pEventQueue == NULL) {
373
 
        STRIGI_LOG_ERROR ("strigi.FsListener.reindex",
374
 
                          "m_pEventQueue == NULL!");
375
 
        return;
376
 
    }
377
 
    
378
 
    set<string> reindexDirs;
379
 
    set<string> dirsDeleted;
380
 
    set<string> dirsCreated;
381
 
    set<string> dirsMonitored;
382
 
    vector<Event*> events;
383
 
    
384
 
    if (!reindexReq ())
385
 
        return;
386
 
 
387
 
    getReindexDirs(reindexDirs);
388
 
    
389
 
    calculateDiff(m_indexedDirs, reindexDirs, dirsDeleted, dirsCreated);
390
 
 
391
 
    for (set<string>::iterator iter = dirsCreated.begin();
392
 
         iter != dirsCreated.end() && !reindexReq(); ++iter)
393
 
    {
394
 
        DirLister lister(m_pAnalyzerConfiguration);
395
 
        string path;
396
 
        vector<pair<string, struct stat> > dirs;
397
 
 
398
 
        lister.startListing (*iter);
399
 
        int ret = lister.nextDir(path, dirs);
400
 
 
401
 
        while (ret != -1) {
402
 
            vector<pair<string, struct stat> >::iterator iter;
403
 
            for (iter = dirs.begin(); iter != dirs.end(); iter++) {
404
 
                struct stat stats = iter->second;
405
 
                if (S_ISDIR(stats.st_mode)) {//dir
406
 
                    set<string> toWatch;
407
 
                    recursivelyMonitor (iter->first, toWatch, events);
408
 
                    // add new watches
409
 
                    addWatches (toWatch);
410
 
                    dirsMonitored.insert (iter->first);
411
 
                }
412
 
                else if (S_ISREG(stats.st_mode)) {
413
 
                    //file
414
 
                    events.push_back (new Event (Event::CREATED, iter->first));
415
 
                }
416
 
            }
417
 
            ret = lister.nextDir(path, dirs);
418
 
        }
419
 
    }
420
 
 
421
 
    if (reindexReq()) {
422
 
        // another reindex request arrived, undo last actions
423
 
        for (vector<Event*>::iterator iter = events.begin();
424
 
             iter != events.end(); ++iter)
425
 
            delete *iter;
426
 
        events.clear();
427
 
        //TODO check!!!
428
 
        dirsRemoved (dirsMonitored, events);
429
 
    }
430
 
    else {
431
 
        // finish reindex operation
432
 
        dirsRemoved (dirsDeleted, events);
433
 
 
434
 
        if (events.size() > 0)
435
 
            m_pEventQueue->addEvents (events);
436
 
        
437
 
        //update indexedDirs
438
 
        m_indexedDirs = reindexDirs;
439
 
    }
440
 
}
441
 
 
442
 
void FsListener::watch ()
443
 
{
444
 
    if (m_pEventQueue == NULL) {
445
 
        STRIGI_LOG_ERROR ("strigi.FsListener.watch",
446
 
                          "m_pEventQueue == NULL!");
447
 
        return;
448
 
    }
449
 
 
450
 
    vector <Event*> events;
451
 
 
452
 
    while (pendingEvent()) {
453
 
        FsEvent* fsevent  = retrieveEvent();
454
 
 
455
 
        if (!isEventValid(fsevent)) {
456
 
            STRIGI_LOG_WARNING ("strigi.FsListener.watch",
457
 
                                "discarded invalid event");
458
 
            continue;
459
 
        }
460
 
 
461
 
        if (isEventInteresting(fsevent)) {
462
 
            STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
463
 
                              fsevent->description());
464
 
 
465
 
            switch (fsevent->type()) {
466
 
                case FsEvent::UPDATE:
467
 
                {
468
 
                    Event* event = new Event (Event::UPDATED, fsevent->file());
469
 
                    events.push_back (event);
470
 
                    break;
471
 
                }
472
 
                case FsEvent::DELETE:
473
 
                {
474
 
                    if (fsevent->regardsDir())
475
 
                        dirRemoved (fsevent->file(), events);
476
 
                    else {
477
 
                        Event* event = new Event (Event::DELETED,
478
 
                                                  fsevent->file());
479
 
                        events.push_back (event);
480
 
                    }
481
 
                    break;
482
 
                }
483
 
                case FsEvent::CREATE:
484
 
                {
485
 
                    if (fsevent->regardsDir()) {
486
 
                        set<string> toWatch;
487
 
                        recursivelyMonitor (fsevent->file(), toWatch, events);
488
 
                        addWatches( toWatch);
489
 
                    }
490
 
                    else {
491
 
                        Event* event = new Event (Event::CREATED,
492
 
                                                  fsevent->file());
493
 
                        events.push_back (event);
494
 
                    }
495
 
                    break;
496
 
                }
497
 
                default:
498
 
                    STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
499
 
                                      "unknown event type")
500
 
                    break;
501
 
            }
502
 
 
503
 
            delete fsevent;
504
 
        }
505
 
    }
506
 
 
507
 
    if (events.size() > 0) {
508
 
        STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
509
 
                          "adding events to processing queue")
510
 
        m_pEventQueue->addEvents (events);
511
 
    }
512
 
}
513
 
 
514
 
void FsListener::recursivelyMonitor (string dir, set<string>& toWatch,
515
 
                                     vector<Event*>& events)
516
 
{
517
 
    STRIGI_LOG_DEBUG ("FsListener.recursivelyMonitor","going to monitor " + dir)
518
 
    DirLister lister(m_pAnalyzerConfiguration);
519
 
    string path;
520
 
    vector<pair<string, struct stat> > fsitems;
521
 
 
522
 
    toWatch.insert (dir);
523
 
    
524
 
    lister.startListing (dir);
525
 
    int ret = lister.nextDir(path, fsitems);
526
 
 
527
 
    while (ret != -1) {
528
 
        for (vector<pair<string,struct stat> >::iterator iter = fsitems.begin();
529
 
             iter != fsitems.end(); ++iter)
530
 
        {
531
 
            struct stat stats = iter->second;
532
 
            
533
 
            if (S_ISDIR(stats.st_mode)) //dir
534
 
                recursivelyMonitor(iter->first, toWatch, events);
535
 
            else if (S_ISREG(stats.st_mode)) {
536
 
                //file
537
 
                Event* event = new Event (Event::CREATED, iter->first);
538
 
                events.push_back (event);
539
 
            }
540
 
        }
541
 
        ret = lister.nextDir(path, fsitems);
542
 
    }
543
 
}
544
 
 
545
 
void FsListener::addWatches(const set<string> &watches)
546
 
{
547
 
        set<string>::iterator iter;
548
 
    set<string> toPool;
549
 
    set<string> watched;
550
 
        
551
 
    if (!m_bInitialized) {
552
 
        STRIGI_LOG_ERROR ("strigi.FsListener.addWatches",
553
 
                          "Listener has not been initialized, unable"
554
 
                          "to add watches!")
555
 
        toPool = watches;
556
 
    }
557
 
    else {
558
 
            for (iter = watches.begin(); iter != watches.end(); ++iter) {
559
 
                if (!addWatch (*iter)) {
560
 
                    // adding watch failed, we've to use polling in order to watch it
561
 
                    toPool.insert(*iter);
562
 
                }
563
 
                else
564
 
                    watched.insert (*iter);
565
 
            }
566
 
    }
567
 
 
568
 
    if (!toPool.empty())
569
 
    {
570
 
        if (m_pollingListener == NULL)
571
 
        {
572
 
            m_pollingListener = new PollingListener();
573
 
            m_pollingListener->setEventListenerQueue( m_pEventQueue);
574
 
            m_pollingListener->setCombinedIndexManager( m_pManager);
575
 
            m_pollingListener->setIndexerConfiguration(m_pAnalyzerConfiguration);
576
 
            //TODO: start with a low priority?
577
 
            m_pollingListener->start( );
578
 
        }
579
 
        
580
 
        m_pollingListener->addWatches( toPool);
581
 
        m_pollingDirs.insert (toPool.begin(), toPool.end());
582
 
    }
583
 
}
584
 
 
585
 
void FsListener::dirsRemoved (set<string> dirs, vector<Event*>& events)
586
 
{
587
 
    for (set<string>::iterator iter = dirs.begin();
588
 
         iter != dirs.end(); ++iter)
589
 
    {
590
 
        string path = fixPath(*iter);
591
 
        set<string>::iterator match = m_pollingDirs.find (path);
592
 
        if (match == m_pollingDirs.end())
593
 
            dirRemoved (path, events);
594
 
        else
595
 
            m_pollingListener->rmWatch (*match);
596
 
    }
597
 
}
598
 
 
599
 
void FsListener::setIndexedDirectories (const set<string> &dirs)
600
 
{
601
 
    stringstream msg;
602
 
    set<string> fixedDirs;
603
 
 
604
 
    // fix path, all dir must end with a '/'
605
 
    for (set<string>::iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
606
 
        fixedDirs.insert (fixPath (*iter));
607
 
 
608
 
    STRIGI_MUTEX_LOCK (&m_reindexLock);
609
 
    m_reindexDirs = fixedDirs;
610
 
    m_bReindexReq = true;
611
 
    STRIGI_MUTEX_UNLOCK (&m_reindexLock);
612
 
 
613
 
 
614
 
    msg << fixedDirs.size() << " dirs specified";
615
 
    STRIGI_LOG_DEBUG ("FsListener.setIndexedDirectories", msg.str())
616
 
}
617
 
 
618
 
void FsListener::dumpEvents()
619
 
{
620
 
    unsigned int counter = 1;
621
 
    for (vector<FsEvent*>::iterator iter = m_events.begin();
622
 
         iter != m_events.end(); ++iter)
623
 
    {
624
 
        FsEvent* event = *iter;
625
 
        stringstream msg;
626
 
        
627
 
        msg << "Event " << counter << "/" << m_events.size() << ": ";
628
 
        msg << event->description();
629
 
        STRIGI_LOG_DEBUG("strigi.FsListener.dumpEvents", msg.str())
630
 
        counter++;
631
 
    }
632
 
}