~vanvugt/ubuntu/oneiric/mediatomb/fix-770964-784431

« back to all changes in this revision

Viewing changes to src/autoscan_inotify.cc

  • Committer: Bazaar Package Importer
  • Author(s): Andres Mejia
  • Date: 2008-02-02 01:42:48 UTC
  • Revision ID: james.westby@ubuntu.com-20080202014248-cjouolddb8gi2zkz
Tags: upstream-0.10.0.dfsg1
ImportĀ upstreamĀ versionĀ 0.10.0.dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*MT*
 
2
    
 
3
    MediaTomb - http://www.mediatomb.cc/
 
4
    
 
5
    autoscan_inotify.cc - this file is part of MediaTomb.
 
6
    
 
7
    Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
 
8
                       Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
 
9
    
 
10
    Copyright (C) 2006-2007 Gena Batyan <bgeradz@mediatomb.cc>,
 
11
                            Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
 
12
                            Leonhard Wimmer <leo@mediatomb.cc>
 
13
    
 
14
    MediaTomb is free software; you can redistribute it and/or modify
 
15
    it under the terms of the GNU General Public License version 2
 
16
    as published by the Free Software Foundation.
 
17
    
 
18
    MediaTomb is distributed in the hope that it will be useful,
 
19
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
    GNU General Public License for more details.
 
22
    
 
23
    You should have received a copy of the GNU General Public License
 
24
    version 2 along with MediaTomb; if not, write to the Free Software
 
25
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
26
    
 
27
    $Id: autoscan_inotify.cc 102 2007-05-25 22:46:50Z leo $
 
28
*/
 
29
 
 
30
/// \file autoscan_inotify.cc
 
31
 
 
32
#ifdef HAVE_CONFIG_H
 
33
    #include "autoconfig.h"
 
34
#endif
 
35
 
 
36
#ifdef HAVE_INOTIFY
 
37
 
 
38
#include "autoscan_inotify.h"
 
39
#include "content_manager.h"
 
40
 
 
41
#include <dirent.h>
 
42
#include <sys/stat.h>
 
43
 
 
44
#define AUTOSCAN_INOTIFY_INITIAL_QUEUE_SIZE 20
 
45
 
 
46
// always use a prime!
 
47
#define AUTOSCAN_INOTIFY_HASH_SIZE 30851
 
48
 
 
49
#define INOTIFY_MAX_USER_WATCHES_FILE "/proc/sys/fs/inotify/max_user_watches"
 
50
using namespace zmm;
 
51
 
 
52
AutoscanInotify::AutoscanInotify()
 
53
{
 
54
    mutex = Ref<Mutex>(new Mutex());
 
55
    cond = Ref<Cond>(new Cond(mutex));
 
56
 
 
57
    int hash_size = AUTOSCAN_INOTIFY_HASH_SIZE;
 
58
 
 
59
    if (check_path(_(INOTIFY_MAX_USER_WATCHES_FILE)))
 
60
    {
 
61
        int max_watches = -1;
 
62
        try
 
63
        {
 
64
            max_watches = trim_string(read_text_file(_(INOTIFY_MAX_USER_WATCHES_FILE))).toInt();
 
65
            log_debug("Max watches on the system: %d\n", max_watches);
 
66
        }
 
67
        catch (Exception ex)
 
68
        {
 
69
            log_error("Could not determine maximum number of inotify user watches: %s\n", ex.getMessage().c_str());
 
70
        }
 
71
 
 
72
        if (max_watches > 0)
 
73
            hash_size = max_watches * 5;
 
74
    }
 
75
 
 
76
    watches = Ref<DBOHash<int, Wd> >(new DBOHash<int, Wd>(hash_size, -1, -2));
 
77
    shutdownFlag = true;
 
78
    monitorQueue = Ref<ObjectQueue<AutoscanDirectory> >(new ObjectQueue<AutoscanDirectory>(AUTOSCAN_INOTIFY_INITIAL_QUEUE_SIZE));
 
79
    unmonitorQueue = Ref<ObjectQueue<AutoscanDirectory> >(new ObjectQueue<AutoscanDirectory>(AUTOSCAN_INOTIFY_INITIAL_QUEUE_SIZE));
 
80
    events = IN_CLOSE_WRITE | IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT;
 
81
}
 
82
 
 
83
void AutoscanInotify::init()
 
84
{
 
85
    AUTOLOCK(mutex);
 
86
    if (shutdownFlag)
 
87
    {
 
88
        shutdownFlag = false;
 
89
        inotify = Ref<Inotify>(new Inotify());
 
90
        log_debug("starting inotify thread...\n");
 
91
        int ret = pthread_create(
 
92
            &thread,
 
93
            NULL,
 
94
            AutoscanInotify::staticThreadProc,
 
95
            this
 
96
        );
 
97
        
 
98
        if (ret)
 
99
            throw _Exception(_("failed to start inotify thread: ") + ret);
 
100
    }
 
101
}
 
102
 
 
103
AutoscanInotify::~AutoscanInotify()
 
104
{
 
105
    shutdown();
 
106
}
 
107
 
 
108
void AutoscanInotify::shutdown()
 
109
{
 
110
    AUTOLOCK(mutex);
 
111
    if (! shutdownFlag)
 
112
    {
 
113
        log_debug("start\n");
 
114
        shutdownFlag = true;
 
115
        inotify->stop();
 
116
        AUTOUNLOCK();
 
117
        if (thread)
 
118
            pthread_join(thread, NULL);
 
119
        thread = 0;
 
120
        log_debug("inotify thread died.\n");
 
121
        inotify = nil;
 
122
        watches->clear();
 
123
    }
 
124
}
 
125
 
 
126
void *AutoscanInotify::staticThreadProc(void *arg)
 
127
{
 
128
    log_debug("started inotify thread.\n");
 
129
    AutoscanInotify *inst = (AutoscanInotify *)arg;
 
130
    inst->threadProc();
 
131
    log_debug("exiting inotify thread...\n");
 
132
    pthread_exit(NULL);
 
133
    return NULL;
 
134
}
 
135
 
 
136
void AutoscanInotify::threadProc()
 
137
{
 
138
    Ref<ContentManager> cm;
 
139
    Ref<Storage> st;
 
140
    
 
141
    inotify_event *event;
 
142
    
 
143
    Ref<StringBuffer> pathBuf (new StringBuffer());
 
144
    
 
145
    try
 
146
    {
 
147
        cm = ContentManager::getInstance();
 
148
        st = Storage::getInstance();
 
149
    }
 
150
    catch (Exception e)
 
151
    {
 
152
        log_error("Inotify thread caught: %s\n", e.getMessage().c_str());
 
153
        e.printStackTrace();
 
154
        shutdownFlag = true;
 
155
        inotify = nil;
 
156
    }
 
157
    while(! shutdownFlag)
 
158
    {
 
159
        try
 
160
        {
 
161
            Ref<AutoscanDirectory> adir;
 
162
            
 
163
            AUTOLOCK(mutex);
 
164
            while ((adir = unmonitorQueue->dequeue()) != nil)
 
165
            {
 
166
                AUTOUNLOCK();
 
167
                
 
168
                String location = normalizePathNoEx(adir->getLocation());
 
169
                if (! string_ok(location))
 
170
                {
 
171
                    AUTORELOCK();
 
172
                    continue;
 
173
                }
 
174
                
 
175
                if (adir->getRecursive())
 
176
                {
 
177
                    log_debug("removing recursive watch: %s\n", location.c_str());
 
178
                    monitorUnmonitorRecursive(location, true, adir, location, true);
 
179
                }
 
180
                else
 
181
                {
 
182
                    log_debug("removing non-recursive watch: %s\n", location.c_str());
 
183
                    unmonitorDirectory(location, adir);
 
184
                }
 
185
                
 
186
                AUTORELOCK();
 
187
            }
 
188
            
 
189
            while ((adir = monitorQueue->dequeue()) != nil)
 
190
            {
 
191
                AUTOUNLOCK();
 
192
                
 
193
                String location = normalizePathNoEx(adir->getLocation());
 
194
                if (! string_ok(location))
 
195
                {
 
196
                    AUTORELOCK();
 
197
                    continue;
 
198
                }
 
199
                
 
200
                if (adir->getRecursive())
 
201
                {
 
202
                    log_debug("adding recursive watch: %s\n", location.c_str());
 
203
                    monitorUnmonitorRecursive(location, false, adir, location, true);
 
204
                }
 
205
                else
 
206
                {
 
207
                    log_debug("adding non-recursive watch: %s\n", location.c_str());
 
208
                    monitorDirectory(location, adir, location, true);
 
209
                }
 
210
                cm->rescanDirectory(adir->getObjectID(), adir->getScanID(), adir->getScanMode(), nil, false);
 
211
                
 
212
                AUTORELOCK();
 
213
            }
 
214
            
 
215
            AUTOUNLOCK();
 
216
            
 
217
            /* --- get event --- (blocking) */
 
218
            event = inotify->nextEvent();
 
219
            /* --- */
 
220
            
 
221
            if (event)
 
222
            {
 
223
                int wd = event->wd;
 
224
                int mask = event->mask;
 
225
                String name(event->name);
 
226
                log_debug("inotify event: %d %d %s\n", wd, mask, name.c_str());
 
227
                
 
228
                Ref<Wd> wdObj = watches->get(wd);
 
229
                if (wdObj == nil)
 
230
                {
 
231
                    inotify->removeWatch(wd);
 
232
                    continue;
 
233
                }
 
234
                
 
235
                
 
236
                pathBuf->clear();
 
237
                *pathBuf << wdObj->getPath();
 
238
                if (! (mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)))
 
239
                    *pathBuf << name;
 
240
                String path = pathBuf->toString();
 
241
                
 
242
                Ref<AutoscanDirectory> adir;
 
243
                Ref<WatchAutoscan> watchAs = getAppropriateAutoscan(wdObj, path);
 
244
                if (watchAs != nil)
 
245
                    adir = watchAs->getAutoscanDirectory();
 
246
                else
 
247
                    adir = nil;
 
248
                
 
249
                if (mask & IN_MOVE_SELF)
 
250
                {
 
251
                    checkMoveWatches(wd, wdObj);
 
252
                }
 
253
                
 
254
                if (mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT))
 
255
                {
 
256
                    recheckNonexistingMonitors(wd, wdObj);
 
257
                }
 
258
                
 
259
                if (mask & IN_ISDIR)
 
260
                {
 
261
                    if (mask & (IN_CREATE | IN_MOVED_TO))
 
262
                    {
 
263
                        recheckNonexistingMonitors(wd, wdObj);
 
264
                    }
 
265
                    
 
266
                    if (adir != nil && adir->getRecursive())
 
267
                    {
 
268
                        if (mask & IN_CREATE)
 
269
                        {
 
270
                            if (adir->getHidden() || name.charAt(0) != '.')
 
271
                            {
 
272
                                log_debug("new dir detected, adding to inotify: %s\n", path.c_str());
 
273
                                monitorUnmonitorRecursive(path, false, adir, watchAs->getNormalizedAutoscanPath(), false);
 
274
                            }
 
275
                            else
 
276
                            {
 
277
                                log_debug("new dir detected, irgnoring because it's hidden: %s\n", path.c_str());
 
278
                            }
 
279
                        }
 
280
                    }
 
281
                }
 
282
                
 
283
                if (adir != nil && mask & (IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_UNMOUNT))
 
284
                {
 
285
                    String fullPath;
 
286
                    if (mask & IN_ISDIR)
 
287
                        fullPath = path + DIR_SEPARATOR;
 
288
                    else
 
289
                        fullPath = path;
 
290
                    
 
291
                    if (! (mask & IN_MOVED_TO))
 
292
                    {
 
293
                        log_debug("deleting %s\n", fullPath.c_str());
 
294
                        
 
295
                        if (mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT))
 
296
                        {
 
297
                            if (IN_MOVE_SELF)
 
298
                                inotify->removeWatch(wd);
 
299
                            Ref<WatchAutoscan> watch = getStartPoint(wdObj);
 
300
                            if (watch != nil)
 
301
                            {
 
302
                                if (adir->persistent())
 
303
                                {
 
304
                                    monitorNonexisting(path, watch->getAutoscanDirectory(), watch->getNormalizedAutoscanPath());
 
305
                                    cm->handlePeristentAutoscanRemove(adir->getScanID(), InotifyScanMode);
 
306
                                }
 
307
                            }
 
308
                        }
 
309
                        
 
310
                        int objectID = st->findObjectIDByPath(fullPath);
 
311
                        if (objectID != INVALID_OBJECT_ID)
 
312
                            cm->removeObject(objectID);
 
313
                    }
 
314
                    if (mask & (IN_CLOSE_WRITE | IN_MOVED_TO))
 
315
                    {
 
316
                        log_debug("adding %s\n", path.c_str());
 
317
                        // path, recursive, async, hidden, low priority, cancellable
 
318
                        cm->addFile(fullPath, adir->getRecursive(), true, adir->getHidden(), true, false);
 
319
                    }
 
320
                }
 
321
                if (mask & IN_IGNORED)
 
322
                {
 
323
                    removeWatchMoves(wd);
 
324
                    removeDescendants(wd);
 
325
                    watches->remove(wd);
 
326
                }
 
327
            }
 
328
        }
 
329
        catch (Exception e)
 
330
        {
 
331
            log_error("Inotify thread caught exception: %s\n", e.getMessage().c_str());
 
332
            e.printStackTrace();
 
333
        }
 
334
    }
 
335
}
 
336
 
 
337
void AutoscanInotify::monitor(zmm::Ref<AutoscanDirectory> dir)
 
338
{
 
339
    if (shutdownFlag)
 
340
        init();
 
341
    assert(dir->getScanMode() == InotifyScanMode);
 
342
    log_debug("---> INCOMING REQUEST TO MONITOR [%s]\n", 
 
343
            dir->getLocation().c_str());
 
344
    AUTOLOCK(mutex);
 
345
    monitorQueue->enqueue(dir);
 
346
    inotify->stop();
 
347
}
 
348
 
 
349
void AutoscanInotify::unmonitor(zmm::Ref<AutoscanDirectory> dir)
 
350
{
 
351
    // must not be persistent
 
352
    assert(! dir->persistent());
 
353
    
 
354
    log_debug("---> INCOMING REQUEST TO UNMONITOR [%s]\n", 
 
355
            dir->getLocation().c_str());
 
356
    AUTOLOCK(mutex);
 
357
    unmonitorQueue->enqueue(dir);
 
358
    inotify->stop();
 
359
}
 
360
 
 
361
int AutoscanInotify::watchPathForMoves(String path, int wd)
 
362
{
 
363
    Ref<Array<StringBase> > pathAr = split_string(path, DIR_SEPARATOR);
 
364
    Ref<StringBuffer> buf(new StringBuffer());
 
365
    int parentWd = INOTIFY_ROOT;
 
366
    for (int i = -1; i < pathAr->size() - 1; i++)
 
367
    {
 
368
        if (i != 0)
 
369
            *buf << DIR_SEPARATOR;
 
370
        if (i >= 0)
 
371
            *buf << pathAr->get(i);
 
372
        log_debug("adding move watch: %s\n", buf->c_str());
 
373
        parentWd = addMoveWatch(buf->toString(), wd, parentWd);
 
374
    }
 
375
    return parentWd;
 
376
}
 
377
 
 
378
int AutoscanInotify::addMoveWatch(String path, int removeWd, int parentWd)
 
379
{
 
380
    int wd = inotify->addWatch(path, events);
 
381
    if (wd >= 0)
 
382
    {
 
383
        bool alreadyThere = false;
 
384
        Ref<Wd> wdObj = watches->get(wd);
 
385
        if (wdObj == nil)
 
386
        {
 
387
            wdObj = Ref<Wd>(new Wd(path, wd, parentWd));
 
388
            watches->put(wd, wdObj);
 
389
        }
 
390
        else
 
391
        {
 
392
            int parentWdSet = wdObj->getParentWd();
 
393
            if (parentWdSet >= 0)
 
394
            {
 
395
                if (parentWd != parentWdSet)
 
396
                {
 
397
                    log_debug("error: parentWd doesn't match wd: %d, parent is: %d, should be: %d\n", wd, parentWdSet, parentWd);
 
398
                    wdObj->setParentWd(parentWd);
 
399
                }
 
400
            }
 
401
            else
 
402
                wdObj->setParentWd(parentWd);
 
403
                
 
404
            //find
 
405
            //alreadyThere =... 
 
406
        }
 
407
        if (! alreadyThere)
 
408
        {
 
409
            Ref<WatchMove> watch(new WatchMove(removeWd));
 
410
            wdObj->getWdWatches()->append(RefCast(watch, Watch));
 
411
        }
 
412
    }
 
413
    return wd;
 
414
}
 
415
 
 
416
void AutoscanInotify::monitorNonexisting(String path, Ref<AutoscanDirectory> adir, String normalizedAutoscanPath)
 
417
{
 
418
    String pathTmp = path;
 
419
    Ref<Array<StringBase> > pathAr = split_string(path, DIR_SEPARATOR);
 
420
    recheckNonexistingMonitor(-1, pathAr, adir, normalizedAutoscanPath);
 
421
}
 
422
 
 
423
void AutoscanInotify::recheckNonexistingMonitor(int curWd, Ref<Array<StringBase> > pathAr, Ref<AutoscanDirectory> adir, String normalizedAutoscanPath)
 
424
{
 
425
    Ref<StringBuffer> buf(new StringBuffer());
 
426
    bool first = true;
 
427
    for (int i = pathAr->size(); i >= 0; i--)
 
428
    {
 
429
        buf->clear();
 
430
        if (i == 0)
 
431
            *buf << DIR_SEPARATOR;
 
432
        else
 
433
        {
 
434
            for (int j = 0; j < i; j++)
 
435
            {
 
436
                *buf << DIR_SEPARATOR << pathAr->get(j);
 
437
//                log_debug("adding: %s\n", pathAr->get(j)->data);
 
438
            }
 
439
        }
 
440
        bool pathExists = check_path(buf->toString(), true);
 
441
//        log_debug("checking %s: %d\n", buf->c_str(), pathExists);
 
442
        if (pathExists)
 
443
        {
 
444
            if (curWd != -1)
 
445
                removeNonexistingMonitor(curWd, watches->get(curWd), pathAr);
 
446
            
 
447
            String path = buf->toString() + DIR_SEPARATOR;
 
448
            if (first)
 
449
            {
 
450
                monitorDirectory(path, adir, normalizedAutoscanPath, true);
 
451
                ContentManager::getInstance()->handlePersistentAutoscanRecreate(adir->getScanID(), adir->getScanMode());
 
452
            }
 
453
            else
 
454
            {
 
455
                monitorDirectory(path, adir, normalizedAutoscanPath, false, pathAr);
 
456
            }
 
457
            break;
 
458
        }
 
459
        if (first)
 
460
            first = false;
 
461
    }
 
462
}
 
463
 
 
464
void AutoscanInotify::checkMoveWatches(int wd, Ref<Wd> wdObj)
 
465
{
 
466
    Ref<Watch> watch;
 
467
    Ref<WatchMove> watchMv;
 
468
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
469
    for (int i = 0; i < wdWatches->size(); i++)
 
470
    {
 
471
        watch = wdWatches->get(i);
 
472
        if (watch->getType() == WatchMoveType)
 
473
        {
 
474
            if (wdWatches->size() == 1)
 
475
            {
 
476
                inotify->removeWatch(wd);
 
477
            }
 
478
            else
 
479
            {
 
480
                wdWatches->removeUnordered(i);
 
481
            }
 
482
            
 
483
            watchMv = RefCast(watch, WatchMove);
 
484
            int removeWd = watchMv->getRemoveWd();
 
485
            Ref<Wd> wdToRemove = watches->get(removeWd);
 
486
            if (wdToRemove != nil)
 
487
            {
 
488
                recheckNonexistingMonitors(removeWd, wdToRemove);
 
489
                
 
490
                String path = wdToRemove->getPath();
 
491
                log_debug("found wd to remove because of move event: %d %s\n", removeWd, path.c_str());
 
492
                
 
493
                inotify->removeWatch(removeWd);
 
494
                Ref<ContentManager> cm = ContentManager::getInstance();
 
495
                Ref<WatchAutoscan> watch = getStartPoint(wdToRemove);
 
496
                if (watch != nil)
 
497
                {
 
498
                    Ref<AutoscanDirectory> adir = watch->getAutoscanDirectory();
 
499
                    if (adir->persistent())
 
500
                    {
 
501
                        monitorNonexisting(path, adir, watch->getNormalizedAutoscanPath());
 
502
                        cm->handlePeristentAutoscanRemove(adir->getScanID(), InotifyScanMode);
 
503
                    }
 
504
                    
 
505
                    int objectID = Storage::getInstance()->findObjectIDByPath(path);
 
506
                    if (objectID != INVALID_OBJECT_ID)
 
507
                        cm->removeObject(objectID);
 
508
                }
 
509
            }
 
510
        }
 
511
    }
 
512
}
 
513
 
 
514
void AutoscanInotify::recheckNonexistingMonitors(int wd, Ref<Wd> wdObj)
 
515
{
 
516
    Ref<Watch> watch;
 
517
    Ref<WatchAutoscan> watchAs;
 
518
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
519
    for (int i = 0; i < wdWatches->size(); i++)
 
520
    {
 
521
        watch = wdWatches->get(i);
 
522
        if (watch->getType() == WatchAutoscanType)
 
523
        {
 
524
            watchAs = RefCast(watch, WatchAutoscan);
 
525
            Ref<Array<StringBase> > pathAr = watchAs->getNonexistingPathArray();
 
526
            if (pathAr != nil)
 
527
            {
 
528
                recheckNonexistingMonitor(wd, pathAr, watchAs->getAutoscanDirectory(), watchAs->getNormalizedAutoscanPath());
 
529
            }
 
530
        }
 
531
    }
 
532
}
 
533
 
 
534
void AutoscanInotify::removeNonexistingMonitor(int wd, Ref<Wd> wdObj, Ref<Array<StringBase> > pathAr)
 
535
{
 
536
    Ref<Watch> watch;
 
537
    Ref<WatchAutoscan> watchAs;
 
538
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
539
    for (int i = 0; i < wdWatches->size(); i++)
 
540
    {
 
541
        watch = wdWatches->get(i);
 
542
        if (watch->getType() == WatchAutoscanType)
 
543
        {
 
544
            watchAs = RefCast(watch, WatchAutoscan);
 
545
            if (watchAs->getNonexistingPathArray() == pathAr)
 
546
            {
 
547
                if (wdWatches->size() == 1)
 
548
                {
 
549
                    // should be done automatically, because removeWatch triggers an IGNORED event
 
550
                    //watches->remove(wd);
 
551
                    
 
552
                    inotify->removeWatch(wd);
 
553
                }
 
554
                else
 
555
                {
 
556
                    wdWatches->removeUnordered(i);
 
557
                }
 
558
                return;
 
559
            }
 
560
        }
 
561
    }
 
562
}
 
563
 
 
564
void AutoscanInotify::monitorUnmonitorRecursive(String startPath, bool unmonitor, Ref<AutoscanDirectory> adir, String normalizedAutoscanPath, bool startPoint)
 
565
{
 
566
    String location;
 
567
    if (unmonitor)
 
568
        unmonitorDirectory(startPath, adir);
 
569
    else
 
570
    {
 
571
        bool ok = (monitorDirectory(startPath, adir, normalizedAutoscanPath, startPoint) > 0);
 
572
        if (! ok)
 
573
            return;
 
574
    }
 
575
    
 
576
    struct dirent *dent;
 
577
    struct stat statbuf;
 
578
    
 
579
    DIR *dir = opendir(startPath.c_str());
 
580
    if (! dir)
 
581
    {
 
582
        log_warning("Could not open %s\n", startPath.c_str());
 
583
        return;
 
584
    }
 
585
    
 
586
    while ((dent = readdir(dir)) != NULL && ! shutdownFlag)
 
587
    {
 
588
        char *name = dent->d_name;
 
589
        if (name[0] == '.')
 
590
        {
 
591
            if (name[1] == 0)
 
592
                continue;
 
593
            else if (name[1] == '.' && name[2] == 0)
 
594
                continue;
 
595
        }
 
596
        
 
597
        String fullPath = startPath + DIR_SEPARATOR + name;
 
598
        
 
599
        if (stat(fullPath.c_str(), &statbuf) != 0)
 
600
            continue;
 
601
        
 
602
        if (S_ISDIR(statbuf.st_mode))
 
603
        {
 
604
            monitorUnmonitorRecursive(fullPath, unmonitor, adir, normalizedAutoscanPath, false);
 
605
        }
 
606
    }
 
607
    
 
608
    closedir(dir);
 
609
}
 
610
 
 
611
int AutoscanInotify::monitorDirectory(String pathOri, Ref<AutoscanDirectory> adir, String normalizedAutoscanPath, bool startPoint, Ref<Array<StringBase> > pathArray)
 
612
{
 
613
    String path = pathOri + DIR_SEPARATOR;
 
614
    
 
615
    int wd = inotify->addWatch(path, events);
 
616
    if (wd < 0)
 
617
    {
 
618
        if (startPoint && adir->persistent())
 
619
        {
 
620
            monitorNonexisting(path, adir, normalizedAutoscanPath);
 
621
        }
 
622
    }
 
623
    else
 
624
    {
 
625
        bool alreadyWatching = false;
 
626
        Ref<Wd> wdObj = watches->get(wd);
 
627
        int parentWd = INOTIFY_UNKNOWN_PARENT_WD;
 
628
        if (startPoint)
 
629
                parentWd = watchPathForMoves(pathOri, wd);
 
630
        if (wdObj == nil)
 
631
        {
 
632
            wdObj = Ref<Wd>(new Wd(path, wd, parentWd));
 
633
            watches->put(wd, wdObj);
 
634
        }
 
635
        else
 
636
        {
 
637
            if (parentWd >= 0 && wdObj->getParentWd() < 0)
 
638
            {
 
639
                wdObj->setParentWd(parentWd);
 
640
            }
 
641
            
 
642
            if (pathArray == nil)
 
643
                alreadyWatching = (getAppropriateAutoscan(wdObj, adir) != nil);
 
644
            
 
645
            // should we check for already existing "nonexisting" watches?
 
646
            // ...
 
647
        }
 
648
        if (! alreadyWatching)
 
649
        {
 
650
            Ref<WatchAutoscan> watch(new WatchAutoscan(startPoint, adir, normalizedAutoscanPath));
 
651
            if (pathArray != nil)
 
652
            {
 
653
               watch->setNonexistingPathArray(pathArray);
 
654
            }
 
655
            wdObj->getWdWatches()->append(RefCast(watch, Watch));
 
656
            
 
657
            if (! startPoint)
 
658
            {
 
659
                int startPointWd = inotify->addWatch(normalizedAutoscanPath, events);
 
660
                log_debug("getting start point for %s -> %s wd=%d\n", pathOri.c_str(), normalizedAutoscanPath.c_str(), startPointWd);
 
661
                if (wd >= 0)
 
662
                    addDescendant(startPointWd, wd, adir);
 
663
            }
 
664
        }
 
665
    }
 
666
    return wd;
 
667
}
 
668
 
 
669
void AutoscanInotify::unmonitorDirectory(String path, Ref<AutoscanDirectory> adir)
 
670
{
 
671
    path = path + DIR_SEPARATOR;
 
672
    
 
673
    // maybe there is a faster method...
 
674
    // we use addWatch, because it returns the wd to the filename
 
675
    // this should not add a new watch, because it should be already watched
 
676
    int wd = inotify->addWatch(path, events);
 
677
    
 
678
    if (wd < 0)
 
679
    {
 
680
        // doesn't seem to be monitored currently
 
681
        log_debug("unmonitorDirectory called, but it isn't monitored? (%s)\n", path.c_str());
 
682
        return;
 
683
    }
 
684
    
 
685
    Ref<Wd> wdObj = watches->get(wd);
 
686
    if (wdObj == nil)
 
687
    {
 
688
        log_error("wd not found in watches!? (%d, %s)\n", wd, path.c_str());
 
689
        return;
 
690
    }
 
691
    
 
692
    Ref<WatchAutoscan> watchAs = getAppropriateAutoscan(wdObj, adir);
 
693
    if (watchAs == nil)
 
694
    {
 
695
        log_debug("autoscan not found in watches? (%d, %s)\n", wd, path.c_str());
 
696
    }
 
697
    else
 
698
    {
 
699
        if (wdObj->getWdWatches()->size() == 1)
 
700
        {
 
701
            // should be done automatically, because removeWatch triggers an IGNORED event
 
702
            //watches->remove(wd);
 
703
            
 
704
            inotify->removeWatch(wd);
 
705
        }
 
706
        else
 
707
        {
 
708
            removeFromWdObj(wdObj, watchAs);
 
709
        }
 
710
    }
 
711
}
 
712
 
 
713
Ref<AutoscanInotify::WatchAutoscan> AutoscanInotify::getAppropriateAutoscan(Ref<Wd> wdObj, Ref<AutoscanDirectory> adir)
 
714
{
 
715
    Ref<Watch> watch;
 
716
    Ref<WatchAutoscan> watchAs;
 
717
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
718
    for (int i = 0; i < wdWatches->size(); i++)
 
719
    {
 
720
        watch = wdWatches->get(i);
 
721
        if (watch->getType() == WatchAutoscanType)
 
722
        {
 
723
            watchAs = RefCast(watch, WatchAutoscan);
 
724
            if (watchAs->getNonexistingPathArray() == nil)
 
725
            {
 
726
                if (watchAs->getAutoscanDirectory()->getLocation() == adir->getLocation())
 
727
                {
 
728
                    return watchAs;
 
729
                }
 
730
            }
 
731
        }
 
732
    }
 
733
    return nil;
 
734
}
 
735
 
 
736
Ref<AutoscanInotify::WatchAutoscan> AutoscanInotify::getAppropriateAutoscan(Ref<Wd> wdObj, String path)
 
737
{
 
738
    String pathBestMatch;
 
739
    Ref<WatchAutoscan> bestMatch = nil;
 
740
    Ref<Watch> watch;
 
741
    Ref<WatchAutoscan> watchAs;
 
742
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
743
    for (int i = 0; i < wdWatches->size(); i++)
 
744
    {
 
745
        watch = wdWatches->get(i);
 
746
        if (watch->getType() == WatchAutoscanType)
 
747
        {
 
748
            watchAs = RefCast(watch, WatchAutoscan);
 
749
            if (watchAs->getNonexistingPathArray() == nil)
 
750
            {
 
751
                String testLocation = watchAs->getNormalizedAutoscanPath();
 
752
                if (path.startsWith(testLocation))
 
753
                {
 
754
                    if (string_ok(pathBestMatch))
 
755
                    {
 
756
                        if (pathBestMatch.length() < testLocation.length())
 
757
                        {
 
758
                            pathBestMatch = testLocation;
 
759
                            bestMatch = watchAs;
 
760
                        }
 
761
                    }
 
762
                    else
 
763
                    {
 
764
                        pathBestMatch = testLocation;
 
765
                        bestMatch = watchAs;
 
766
                    }
 
767
                }
 
768
            }
 
769
        }
 
770
    }
 
771
    return bestMatch;
 
772
}
 
773
 
 
774
void AutoscanInotify::removeWatchMoves(int wd)
 
775
{
 
776
    Ref<Wd> wdObj;
 
777
    Ref<Array<Watch> > wdWatches;
 
778
    Ref<Watch> watch;
 
779
    Ref<WatchMove> watchMv;
 
780
    bool first = true;
 
781
    int checkWd = wd;
 
782
    do
 
783
    {
 
784
        wdObj = watches->get(checkWd);
 
785
        if (wdObj == nil)
 
786
            break;
 
787
        wdWatches = wdObj->getWdWatches();
 
788
        if (wdWatches == nil)
 
789
            break;
 
790
        
 
791
        if (first)
 
792
            first = false;
 
793
        else
 
794
        {
 
795
            for (int i = 0; i < wdWatches->size(); i++)
 
796
            {
 
797
                watch = wdWatches->get(i);
 
798
                if (watch->getType() == WatchMoveType)
 
799
                {
 
800
                    watchMv = RefCast(watch, WatchMove);
 
801
                    if (watchMv->getRemoveWd() == wd)
 
802
                    {
 
803
                        log_debug("removing watch move\n");
 
804
                        if (wdWatches->size() == 1)
 
805
                            inotify->removeWatch(checkWd);
 
806
                        else
 
807
                            wdWatches->removeUnordered(i);
 
808
                    }
 
809
                }
 
810
            }
 
811
        }
 
812
        checkWd = wdObj->getParentWd();
 
813
    }
 
814
    while(checkWd >= 0);
 
815
}
 
816
 
 
817
bool AutoscanInotify::removeFromWdObj(Ref<Wd> wdObj, Ref<Watch> toRemove)
 
818
{
 
819
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
820
    Ref<Watch> watch;
 
821
    for (int i = 0; i < wdWatches->size(); i++)
 
822
    {
 
823
        watch = wdWatches->get(i);
 
824
        if (watch == toRemove)
 
825
        {
 
826
            if (wdWatches->size() == 1)
 
827
                inotify->removeWatch(wdObj->getWd());
 
828
            else
 
829
                wdWatches->removeUnordered(i);
 
830
            return true;
 
831
        }
 
832
    }
 
833
    return false;
 
834
}
 
835
 
 
836
bool AutoscanInotify::removeFromWdObj(Ref<Wd> wdObj, Ref<WatchAutoscan> toRemove)
 
837
{
 
838
    return removeFromWdObj(wdObj, RefCast(toRemove, Watch));
 
839
}
 
840
 
 
841
bool AutoscanInotify::removeFromWdObj(Ref<Wd> wdObj, Ref<WatchMove> toRemove)
 
842
{
 
843
    return removeFromWdObj(wdObj, RefCast(toRemove, Watch));
 
844
}
 
845
 
 
846
Ref<AutoscanInotify::WatchAutoscan> AutoscanInotify::getStartPoint(Ref<Wd> wdObj)
 
847
{
 
848
    Ref<Watch> watch;
 
849
    Ref<WatchAutoscan> watchAs;
 
850
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
851
    for (int i = 0; i < wdWatches->size(); i++)
 
852
    {
 
853
        watch = wdWatches->get(i);
 
854
        if (watch->getType() == WatchAutoscanType)
 
855
        {
 
856
            watchAs = RefCast(watch, WatchAutoscan);
 
857
            if (watchAs->isStartPoint())
 
858
                return watchAs;
 
859
        }
 
860
    }
 
861
    return nil;
 
862
}
 
863
 
 
864
void AutoscanInotify::addDescendant(int startPointWd, int addWd, Ref<AutoscanDirectory> adir)
 
865
{
 
866
//    log_debug("called for %d, (adir->path=%s); adding %d\n", startPointWd, adir->getLocation().c_str(), addWd);
 
867
    Ref<Wd> wdObj = watches->get(startPointWd);
 
868
    if (wdObj == nil)
 
869
        return;
 
870
//   log_debug("found wdObj\n");
 
871
    Ref<WatchAutoscan> watch = getAppropriateAutoscan(wdObj, adir);
 
872
    if (watch == nil)
 
873
        return;
 
874
//    log_debug("adding descendant\n");
 
875
    watch->addDescendant(addWd);
 
876
//    log_debug("added descendant to %d (adir->path=%s): %d; now: %s\n", startPointWd, adir->getLocation().c_str(), addWd, watch->getDescendants()->toCSV().c_str());
 
877
}
 
878
 
 
879
void AutoscanInotify::removeDescendants(int wd)
 
880
{
 
881
    Ref<Wd> wdObj = watches->get(wd);
 
882
    if (wdObj == nil)
 
883
        return;
 
884
    Ref<Array<Watch> > wdWatches = wdObj->getWdWatches();
 
885
    if (wdWatches == nil)
 
886
        return;
 
887
    
 
888
    Ref<Watch> watch;
 
889
    Ref<WatchAutoscan> watchAs;
 
890
    for (int i = 0; i < wdWatches->size(); i++)
 
891
    {
 
892
        watch = wdWatches->get(i);
 
893
        if (watch->getType() == WatchAutoscanType)
 
894
        {
 
895
            watchAs = RefCast(watch, WatchAutoscan);
 
896
            Ref<IntArray> descendants = watchAs->getDescendants();
 
897
            if (descendants != nil)
 
898
            {
 
899
                for (int i = 0; i < descendants->size(); i++)
 
900
                {
 
901
                    int descWd = descendants->get(i);
 
902
                    inotify->removeWatch(descWd);
 
903
                }
 
904
            }
 
905
        }
 
906
    }
 
907
}
 
908
 
 
909
String AutoscanInotify::normalizePathNoEx(String path)
 
910
{
 
911
    try
 
912
    {
 
913
        return normalizePath(path);
 
914
    }
 
915
    catch (Exception e)
 
916
    {
 
917
        log_error("%s\n", e.getMessage().c_str());
 
918
        return nil;
 
919
    }
 
920
}
 
921
 
 
922
#endif // HAVE_INOTIFY