00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
#include <config.h>
00035
00036
#ifdef HAVE_DNOTIFY
00037
#include <unistd.h>
00038
#include <time.h>
00039
#include <fcntl.h>
00040
#include <signal.h>
00041
#include <errno.h>
00042
#endif
00043
00044
#include <sys/stat.h>
00045
#include <assert.h>
00046
#include <qdir.h>
00047
#include <qfile.h>
00048
#include <qintdict.h>
00049
#include <qptrlist.h>
00050
#include <qsocketnotifier.h>
00051
#include <qstringlist.h>
00052
#include <qtimer.h>
00053
00054
#include <kapplication.h>
00055
#include <kdebug.h>
00056
#include <kconfig.h>
00057
#include <kglobal.h>
00058
#include <kstaticdeleter.h>
00059
00060
#include "kdirwatch.h"
00061
#include "kdirwatch_p.h"
00062
#include "global.h"
00063
00064
#define NO_NOTIFY (time_t) 0
00065
00066
static KDirWatchPrivate* dwp_self = 0;
00067
00068
#ifdef HAVE_DNOTIFY
00069
00070
#include <sys/utsname.h>
00071
00072
static int dnotify_signal = 0;
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
void KDirWatchPrivate::dnotify_handler(
int, siginfo_t *si,
void *)
00083 {
00084
if (!dwp_self)
return;
00085
00086
00087
00088
int saved_errno = errno;
00089
00090 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
00091
00092
00093
00094
00095
if(!e || e->dn_fd != si->si_fd) {
00096 qDebug(
"fatal error in KDirWatch");
00097 }
else
00098 e->dn_dirty =
true;
00099
00100
char c = 0;
00101 write(dwp_self->mPipe[1], &c, 1);
00102 errno = saved_errno;
00103 }
00104
00105
static struct sigaction old_sigio_act;
00106
00107
00108
00109
00110
void KDirWatchPrivate::dnotify_sigio_handler(
int sig, siginfo_t *si,
void *p)
00111 {
00112
if (dwp_self)
00113 {
00114
00115
00116
int saved_errno = errno;
00117
00118 dwp_self->rescan_all =
true;
00119
char c = 0;
00120 write(dwp_self->mPipe[1], &c, 1);
00121
00122 errno = saved_errno;
00123 }
00124
00125
00126
if (old_sigio_act.sa_flags & SA_SIGINFO)
00127 {
00128
if (old_sigio_act.sa_sigaction)
00129 (*old_sigio_act.sa_sigaction)(sig, si, p);
00130 }
00131
else
00132 {
00133
if ((old_sigio_act.sa_handler != SIG_DFL) &&
00134 (old_sigio_act.sa_handler != SIG_IGN))
00135 (*old_sigio_act.sa_handler)(sig);
00136 }
00137 }
00138
#endif
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 KDirWatchPrivate::KDirWatchPrivate()
00170 {
00171 timer =
new QTimer(
this);
00172
connect (timer, SIGNAL(timeout()),
this, SLOT(slotRescan()));
00173 freq = 3600000;
00174 statEntries = 0;
00175 delayRemove =
false;
00176 m_ref = 0;
00177
00178
KConfigGroup config(KGlobal::config(),
QCString(
"DirWatch"));
00179 m_nfsPollInterval = config.readNumEntry(
"NFSPollInterval", 5000);
00180 m_PollInterval = config.readNumEntry(
"PollInterval", 500);
00181
00182
QString available(
"Stat");
00183
00184
#ifdef HAVE_FAM
00185
00186
if (FAMOpen(&fc) ==0) {
00187 available +=
", FAM";
00188 use_fam=
true;
00189 sn =
new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00190 QSocketNotifier::Read,
this);
00191
connect( sn, SIGNAL(activated(
int)),
00192
this, SLOT(famEventReceived()) );
00193 }
00194
else {
00195
kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
00196 use_fam=
false;
00197 }
00198
#endif
00199
00200
#ifdef HAVE_DNOTIFY
00201
supports_dnotify =
true;
00202 rescan_all =
false;
00203
struct utsname uts;
00204
int major, minor, patch;
00205
if (uname(&uts) < 0)
00206 supports_dnotify =
false;
00207
else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
00208 supports_dnotify =
false;
00209
else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
00210
kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
00211 supports_dnotify =
false;
00212 }
00213
00214
if( supports_dnotify ) {
00215 available +=
", DNotify";
00216
00217 pipe(mPipe);
00218 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00219 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00220 mSn =
new QSocketNotifier( mPipe[0], QSocketNotifier::Read,
this);
00221
connect(mSn, SIGNAL(activated(
int)),
this, SLOT(slotActivated()));
00222
connect(&mTimer, SIGNAL(timeout()),
this, SLOT(slotRescan()));
00223
00224
if ( dnotify_signal == 0 )
00225 {
00226 dnotify_signal = SIGRTMIN + 8;
00227
00228
struct sigaction act;
00229 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00230 sigemptyset(&act.sa_mask);
00231 act.sa_flags = SA_SIGINFO;
00232
#ifdef SA_RESTART
00233
act.sa_flags |= SA_RESTART;
00234
#endif
00235
sigaction(dnotify_signal, &act, NULL);
00236
00237 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00238 sigaction(SIGIO, &act, &old_sigio_act);
00239 }
00240 }
00241
else
00242 {
00243 mPipe[0] = -1;
00244 mPipe[1] = -1;
00245 }
00246
#endif
00247
00248
kdDebug(7001) <<
"Available methods: " << available <<
endl;
00249 }
00250
00251
00252 KDirWatchPrivate::~KDirWatchPrivate()
00253 {
00254 timer->stop();
00255
00256
00257 removeEntries(0);
00258
00259
#ifdef HAVE_FAM
00260
if (use_fam) {
00261 FAMClose(&fc);
00262
kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" <<
endl;
00263 }
00264
#endif
00265
#ifdef HAVE_DNOTIFY
00266
close(mPipe[0]);
00267
close(mPipe[1]);
00268
#endif
00269
}
00270
00271
#ifdef HAVE_DNOTIFY
00272
void KDirWatchPrivate::slotActivated()
00273 {
00274
char dummy_buf[100];
00275 read(mPipe[0], &dummy_buf, 100);
00276
00277
if (!mTimer.isActive())
00278 mTimer.start(200,
true);
00279 }
00280
00281
00282
00283
00284
00285
void KDirWatchPrivate::Entry::propagate_dirty()
00286 {
00287 Entry* sub_entry;
00288
for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
00289 {
00290
if (!sub_entry->dn_dirty)
00291 {
00292 sub_entry->dn_dirty =
true;
00293 sub_entry->propagate_dirty();
00294 }
00295 }
00296 }
00297
00298
#else // !HAVE_DNOTIFY
00299
00300
void KDirWatchPrivate::slotActivated() {}
00301
#endif
00302
00303
00304
00305
00306
void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
00307 {
00308 Client* client = m_clients.first();
00309
for(;client; client = m_clients.next())
00310
if (client->instance ==
instance)
break;
00311
00312
if (client) {
00313 client->count++;
00314
return;
00315 }
00316
00317 client =
new Client;
00318 client->instance =
instance;
00319 client->count = 1;
00320 client->watchingStopped =
instance->isStopped();
00321 client->pending = NoChange;
00322
00323 m_clients.append(client);
00324 }
00325
00326
void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
00327 {
00328 Client* client = m_clients.first();
00329
for(;client; client = m_clients.next())
00330
if (client->instance ==
instance)
break;
00331
00332
if (client) {
00333 client->count--;
00334
if (client->count == 0) {
00335 m_clients.removeRef(client);
00336
delete client;
00337 }
00338 }
00339 }
00340
00341
00342
int KDirWatchPrivate::Entry::clients()
00343 {
00344
int clients = 0;
00345 Client* client = m_clients.first();
00346
for(;client; client = m_clients.next())
00347 clients += client->count;
00348
00349
return clients;
00350 }
00351
00352
00353 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const QString& _path)
00354 {
00355
00356
if (_path.
left(1) !=
"/") {
00357
return 0;
00358 }
00359
00360
QString path = _path;
00361
00362
if ( path.
length() > 1 && path.
right(1) ==
"/" )
00363 path.
truncate( path.
length() - 1 );
00364
00365 EntryMap::Iterator it = m_mapEntries.find( path );
00366
if ( it == m_mapEntries.end() )
00367
return 0;
00368
else
00369
return &(*it);
00370 }
00371
00372
00373
void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
00374 {
00375 e->freq = newFreq;
00376
00377
00378
if (e->freq < freq) {
00379 freq = e->freq;
00380
if (timer->isActive()) timer->changeInterval(freq);
00381
kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
00382 }
00383 }
00384
00385
00386
#if defined(HAVE_FAM)
00387
00388
bool KDirWatchPrivate::useFAM(Entry* e)
00389 {
00390
if (!use_fam)
return false;
00391
00392 e->m_mode = FAMMode;
00393
00394
if (e->isDir) {
00395
if (e->m_status == NonExistent) {
00396
00397 addEntry(0, QDir::cleanDirPath(e->path+
"/.."), e,
true);
00398 }
00399
else {
00400
int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00401 &(e->fr), e);
00402
if (res<0) {
00403 e->m_mode = UnknownMode;
00404 use_fam=
false;
00405
return false;
00406 }
00407
kdDebug(7001) <<
" Setup FAM (Req "
00408 << FAMREQUEST_GETREQNUM(&(e->fr))
00409 <<
") for " << e->path <<
endl;
00410 }
00411 }
00412
else {
00413
if (e->m_status == NonExistent) {
00414
00415 addEntry(0,
QFileInfo(e->path).dirPath(
true), e,
true);
00416 }
00417
else {
00418
int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00419 &(e->fr), e);
00420
if (res<0) {
00421 e->m_mode = UnknownMode;
00422 use_fam=
false;
00423
return false;
00424 }
00425
00426
kdDebug(7001) <<
" Setup FAM (Req "
00427 << FAMREQUEST_GETREQNUM(&(e->fr))
00428 <<
") for " << e->path <<
endl;
00429 }
00430 }
00431
00432
00433
00434 famEventReceived();
00435
00436
return true;
00437 }
00438
#endif
00439
00440
00441
#ifdef HAVE_DNOTIFY
00442
00443
bool KDirWatchPrivate::useDNotify(Entry* e)
00444 {
00445 e->dn_fd = 0;
00446
if (!supports_dnotify)
return false;
00447
00448 e->m_mode = DNotifyMode;
00449
00450
if (e->isDir) {
00451 e->dn_dirty =
false;
00452
if (e->m_status == Normal) {
00453
int fd =
open(QFile::encodeName(e->path).data(), O_RDONLY);
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
int fd2 = fcntl(fd, F_DUPFD, 128);
00467
if (fd2 >= 0)
00468 {
00469
close(fd);
00470 fd = fd2;
00471 }
00472
if (fd<0) {
00473 e->m_mode = UnknownMode;
00474
return false;
00475 }
00476
00477
int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00478
00479
for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00480
if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
00481
00482
if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
00483 fcntl(fd, F_NOTIFY, mask) < 0) {
00484
00485
kdDebug(7001) <<
"Not using Linux Directory Notifications."
00486 <<
endl;
00487 supports_dnotify =
false;
00488 ::close(fd);
00489 e->m_mode = UnknownMode;
00490
return false;
00491 }
00492
00493 fd_Entry.replace(fd, e);
00494 e->dn_fd = fd;
00495
00496
kdDebug(7001) <<
" Setup DNotify (fd " << fd
00497 <<
") for " << e->path <<
endl;
00498 }
00499
else {
00500 addEntry(0, QDir::cleanDirPath(e->path+
"/.."), e,
true);
00501 }
00502 }
00503
else {
00504
00505
00506 addEntry(0,
QFileInfo(e->path).dirPath(
true), e,
true);
00507 }
00508
00509
return true;
00510 }
00511
#endif
00512
00513
00514
bool KDirWatchPrivate::useStat(Entry* e)
00515 {
00516
if (
KIO::probably_slow_mounted(e->path))
00517 useFreq(e, m_nfsPollInterval);
00518
else
00519 useFreq(e, m_PollInterval);
00520
00521
if (e->m_mode != StatMode) {
00522 e->m_mode = StatMode;
00523 statEntries++;
00524
00525
if ( statEntries == 1 ) {
00526
00527 timer->start(freq);
00528
kdDebug(7001) <<
" Started Polling Timer, freq " << freq <<
endl;
00529 }
00530 }
00531
00532
kdDebug(7001) <<
" Setup Stat (freq " << e->freq
00533 <<
") for " << e->path <<
endl;
00534
00535
return true;
00536 }
00537
00538
00539
00540
00541
00542
00543
00544
void KDirWatchPrivate::addEntry(
KDirWatch* instance,
const QString& _path,
00545 Entry* sub_entry,
bool isDir)
00546 {
00547
QString path = _path;
00548
if (path.
startsWith(
"/dev/") || (path ==
"/dev"))
00549
return;
00550
00551
if ( path.
length() > 1 && path.
right(1) ==
"/" )
00552 path.
truncate( path.
length() - 1 );
00553
00554 EntryMap::Iterator it = m_mapEntries.find( path );
00555
if ( it != m_mapEntries.end() )
00556 {
00557
if (sub_entry) {
00558 (*it).m_entries.append(sub_entry);
00559
kdDebug(7001) <<
"Added already watched Entry " << path
00560 <<
" (for " << sub_entry->path <<
")" <<
endl;
00561
#ifdef HAVE_DNOTIFY
00562
Entry* e = &(*it);
00563
if( e->dn_fd > 0 ) {
00564
int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00565
00566
for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00567
if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB;
break; }
00568
if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
00569 ::close(e->dn_fd);
00570 e->m_mode = UnknownMode;
00571 fd_Entry.remove(e->dn_fd);
00572 e->dn_fd = 0;
00573 useStat( e );
00574 }
00575 }
00576
#endif
00577
}
00578
else {
00579 (*it).addClient(instance);
00580
kdDebug(7001) <<
"Added already watched Entry " << path
00581 <<
" (now " << (*it).clients() <<
" clients)"
00582 <<
QString(
" [%1]").
arg(
instance->name()) <<
endl;
00583 }
00584
return;
00585 }
00586
00587
00588
00589
struct stat stat_buf;
00590
bool exists = (
stat(QFile::encodeName(path), &stat_buf) == 0);
00591
00592 Entry newEntry;
00593 m_mapEntries.insert( path, newEntry );
00594
00595 Entry* e = &(m_mapEntries[path]);
00596
00597
if (exists) {
00598 e->isDir = S_ISDIR(stat_buf.st_mode);
00599
00600
if (e->isDir && !isDir)
00601 qWarning(
"KDirWatch: %s is a directory. Use addDir!", path.
ascii());
00602
else if (!e->isDir && isDir)
00603 qWarning(
"KDirWatch: %s is a file. Use addFile!", path.
ascii());
00604
00605 e->m_ctime = stat_buf.st_ctime;
00606 e->m_status = Normal;
00607 e->m_nlink = stat_buf.st_nlink;
00608 }
00609
else {
00610 e->isDir = isDir;
00611 e->m_ctime = invalid_ctime;
00612 e->m_status = NonExistent;
00613 e->m_nlink = 0;
00614 }
00615
00616 e->path = path;
00617
if (sub_entry)
00618 e->m_entries.
append(sub_entry);
00619
else
00620 e->addClient(instance);
00621
00622
kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
00623 << (e->m_status == NonExistent ?
" NotExisting" :
"")
00624 << (sub_entry ?
QString(
" for %1").arg(sub_entry->path) :
QString(
""))
00625 << (
instance ?
QString(
" [%1]").arg(
instance->
name()) :
QString(
""))
00626 <<
endl;
00627
00628
00629
00630 e->m_mode = UnknownMode;
00631 e->msecLeft = 0;
00632
00633
#if defined(HAVE_FAM)
00634
if (useFAM(e))
return;
00635
#endif
00636
00637
#ifdef HAVE_DNOTIFY
00638
if (useDNotify(e))
return;
00639
#endif
00640
00641 useStat(e);
00642 }
00643
00644
00645
void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
00646
const QString& _path, Entry* sub_entry )
00647 {
00648 Entry* e = entry(_path);
00649
if (!e) {
00650
kdWarning(7001) <<
"KDirWatch::removeDir can't handle '" << _path <<
"'" <<
endl;
00651
return;
00652 }
00653
00654
if (sub_entry)
00655 e->m_entries.removeRef(sub_entry);
00656
else
00657 e->removeClient(instance);
00658
00659
if (e->m_clients.count() || e->m_entries.count())
00660
return;
00661
00662
if (delayRemove) {
00663
00664
if (removeList.findRef(e)==-1)
00665 removeList.append(e);
00666
00667
return;
00668 }
00669
00670
#ifdef HAVE_FAM
00671
if (e->m_mode == FAMMode) {
00672
if ( e->m_status == Normal) {
00673 FAMCancelMonitor(&fc, &(e->fr) );
00674
kdDebug(7001) <<
"Cancelled FAM (Req "
00675 << FAMREQUEST_GETREQNUM(&(e->fr))
00676 <<
") for " << e->path <<
endl;
00677 }
00678
else {
00679
if (e->isDir)
00680 removeEntry(0, QDir::cleanDirPath(e->path+
"/.."), e);
00681
else
00682 removeEntry(0,
QFileInfo(e->path).dirPath(
true), e);
00683 }
00684 }
00685
#endif
00686
00687
#ifdef HAVE_DNOTIFY
00688
if (e->m_mode == DNotifyMode) {
00689
if (!e->isDir) {
00690 removeEntry(0,
QFileInfo(e->path).dirPath(
true), e);
00691 }
00692
else {
00693
00694
if ( e->m_status == Normal) {
00695
if (e->dn_fd) {
00696 ::close(e->dn_fd);
00697 fd_Entry.remove(e->dn_fd);
00698
00699
kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
00700 <<
") for " << e->path <<
endl;
00701 e->dn_fd = 0;
00702
00703 }
00704 }
00705
else {
00706 removeEntry(0, QDir::cleanDirPath(e->path+
"/.."), e);
00707 }
00708 }
00709 }
00710
#endif
00711
00712
if (e->m_mode == StatMode) {
00713 statEntries--;
00714
if ( statEntries == 0 ) {
00715 timer->stop();
00716
kdDebug(7001) <<
" Stopped Polling Timer" <<
endl;
00717 }
00718 }
00719
00720
kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
00721 << (sub_entry ?
QString(
" for %1").arg(sub_entry->path) :
QString(
""))
00722 << (
instance ?
QString(
" [%1]").arg(
instance->
name()) :
QString(
""))
00723 <<
endl;
00724 m_mapEntries.remove( e->path );
00725 }
00726
00727
00728
00729
00730
00731
void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
00732 {
00733
QPtrList<Entry> list;
00734
int minfreq = 3600000;
00735
00736
00737 EntryMap::Iterator it = m_mapEntries.begin();
00738
for( ; it != m_mapEntries.end(); ++it ) {
00739 Client* c = (*it).m_clients.first();
00740
for(;c;c=(*it).m_clients.next())
00741
if (c->instance ==
instance)
break;
00742
if (c) {
00743 c->count = 1;
00744 list.
append(&(*it));
00745 }
00746
else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00747 minfreq = (*it).freq;
00748 }
00749
00750
for(Entry* e=list.
first();e;e=list.
next())
00751 removeEntry(instance, e->path, 0);
00752
00753
if (minfreq > freq) {
00754
00755 freq = minfreq;
00756
if (timer->isActive()) timer->changeInterval(freq);
00757
kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
00758 }
00759 }
00760
00761
00762
bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
00763 {
00764
int stillWatching = 0;
00765 Client* c = e->m_clients.first();
00766
for(;c;c=e->m_clients.next()) {
00767
if (!
instance ||
instance == c->instance)
00768 c->watchingStopped =
true;
00769
else if (!c->watchingStopped)
00770 stillWatching += c->count;
00771 }
00772
00773
kdDebug(7001) <<
instance->name() <<
" stopped scanning " << e->path
00774 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
00775
00776
if (stillWatching == 0) {
00777
00778 e->m_ctime = invalid_ctime;
00779
00780 }
00781
return true;
00782 }
00783
00784
00785
bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
00786
bool notify)
00787 {
00788
int wasWatching = 0, newWatching = 0;
00789 Client* c = e->m_clients.first();
00790
for(;c;c=e->m_clients.next()) {
00791
if (!c->watchingStopped)
00792 wasWatching += c->count;
00793
else if (!
instance ||
instance == c->instance) {
00794 c->watchingStopped =
false;
00795 newWatching += c->count;
00796 }
00797 }
00798
if (newWatching == 0)
00799
return false;
00800
00801
kdDebug(7001) <<
instance->name() <<
" restarted scanning " << e->path
00802 <<
" (now " << wasWatching+newWatching <<
" watchers)" <<
endl;
00803
00804
00805
00806
int ev = NoChange;
00807
if (wasWatching == 0) {
00808
if (!notify) {
00809
struct stat stat_buf;
00810
bool exists = (
stat(QFile::encodeName(e->path), &stat_buf) == 0);
00811
if (exists) {
00812 e->m_ctime = stat_buf.st_ctime;
00813 e->m_status = Normal;
00814 e->m_nlink = stat_buf.st_nlink;
00815 }
00816
else {
00817 e->m_ctime = invalid_ctime;
00818 e->m_status = NonExistent;
00819 e->m_nlink = 0;
00820 }
00821 }
00822 e->msecLeft = 0;
00823 ev = scanEntry(e);
00824 }
00825 emitEvent(e,ev);
00826
00827
return true;
00828 }
00829
00830
00831
void KDirWatchPrivate::stopScan(
KDirWatch* instance)
00832 {
00833 EntryMap::Iterator it = m_mapEntries.begin();
00834
for( ; it != m_mapEntries.end(); ++it )
00835 stopEntryScan(instance, &(*it));
00836 }
00837
00838
00839
void KDirWatchPrivate::startScan(
KDirWatch* instance,
00840
bool notify,
bool skippedToo )
00841 {
00842
if (!notify)
00843 resetList(instance,skippedToo);
00844
00845 EntryMap::Iterator it = m_mapEntries.begin();
00846
for( ; it != m_mapEntries.end(); ++it )
00847 restartEntryScan(instance, &(*it), notify);
00848
00849
00850 }
00851
00852
00853
00854
void KDirWatchPrivate::resetList(
KDirWatch* ,
00855
bool skippedToo )
00856 {
00857 EntryMap::Iterator it = m_mapEntries.begin();
00858
for( ; it != m_mapEntries.end(); ++it ) {
00859
00860 Client* c = (*it).m_clients.first();
00861
for(;c;c=(*it).m_clients.next())
00862
if (!c->watchingStopped || skippedToo)
00863 c->pending = NoChange;
00864 }
00865 }
00866
00867
00868
00869
int KDirWatchPrivate::scanEntry(Entry* e)
00870 {
00871
#ifdef HAVE_FAM
00872
00873
if (e->m_mode == FAMMode)
return NoChange;
00874
#endif
00875
00876
00877
if (e->m_mode == UnknownMode)
return NoChange;
00878
00879
#ifdef HAVE_DNOTIFY
00880
if (e->m_mode == DNotifyMode) {
00881
00882
if(!e->dn_dirty)
return NoChange;
00883 e->dn_dirty =
false;
00884 }
00885
#endif
00886
00887
if (e->m_mode == StatMode) {
00888
00889
00890
00891
00892 e->msecLeft -= freq;
00893
if (e->msecLeft>0)
return NoChange;
00894 e->msecLeft += e->freq;
00895 }
00896
00897
struct stat stat_buf;
00898
bool exists = (
stat(QFile::encodeName(e->path), &stat_buf) == 0);
00899
if (exists) {
00900
00901
if (e->m_status == NonExistent) {
00902 e->m_ctime = stat_buf.st_ctime;
00903 e->m_status = Normal;
00904 e->m_nlink = stat_buf.st_nlink;
00905
return Created;
00906 }
00907
00908
if ( (e->m_ctime != invalid_ctime) &&
00909 ((stat_buf.st_ctime != e->m_ctime) ||
00910 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
00911 e->m_ctime = stat_buf.st_ctime;
00912 e->m_nlink = stat_buf.st_nlink;
00913
return Changed;
00914 }
00915
00916
return NoChange;
00917 }
00918
00919
00920
00921
if (e->m_ctime == invalid_ctime)
00922
return NoChange;
00923
00924 e->m_ctime = invalid_ctime;
00925 e->m_nlink = 0;
00926 e->m_status = NonExistent;
00927
00928
return Deleted;
00929 }
00930
00931
00932
00933
00934
00935
void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const QString &fileName)
00936 {
00937
QString path = e->path;
00938
if (!fileName.
isEmpty()) {
00939
if (fileName[0] ==
'/')
00940 path = fileName;
00941
else
00942 path +=
"/" + fileName;
00943 }
00944
00945 Client* c = e->m_clients.first();
00946
for(;c;c=e->m_clients.next()) {
00947
if (c->instance==0 || c->count==0)
continue;
00948
00949
if (c->watchingStopped) {
00950
00951
if (
event == Changed)
00952 c->pending |=
event;
00953
else if (
event == Created ||
event == Deleted)
00954 c->pending =
event;
00955
continue;
00956 }
00957
00958
if (
event == NoChange ||
event == Changed)
00959
event |= c->pending;
00960 c->pending = NoChange;
00961
if (
event == NoChange)
continue;
00962
00963
if (
event & Deleted) {
00964 c->instance->setDeleted(path);
00965
00966
continue;
00967 }
00968
00969
if (
event & Created) {
00970 c->instance->setCreated(path);
00971
00972 }
00973
00974
if (
event & Changed)
00975 c->instance->setDirty(path);
00976 }
00977 }
00978
00979
00980
void KDirWatchPrivate::slotRemoveDelayed()
00981 {
00982 Entry* e;
00983 delayRemove =
false;
00984
for(e=removeList.first();e;e=removeList.next())
00985 removeEntry(0, e->path, 0);
00986 removeList.clear();
00987 }
00988
00989
00990
00991
00992
void KDirWatchPrivate::slotRescan()
00993 {
00994 EntryMap::Iterator it;
00995
00996
00997
00998
00999
bool timerRunning = timer->isActive();
01000
if ( timerRunning )
01001 timer->stop();
01002
01003
01004
01005 delayRemove =
true;
01006
01007
#ifdef HAVE_DNOTIFY
01008
QPtrList<Entry> dList, cList;
01009
01010
01011
if (rescan_all)
01012 {
01013
01014 it = m_mapEntries.begin();
01015
for( ; it != m_mapEntries.end(); ++it )
01016 (*it).dn_dirty =
true;
01017 rescan_all =
false;
01018 }
01019
else
01020 {
01021
01022 it = m_mapEntries.begin();
01023
for( ; it != m_mapEntries.end(); ++it )
01024
if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
01025 (*it).propagate_dirty();
01026 }
01027
01028
#endif
01029
01030 it = m_mapEntries.begin();
01031
for( ; it != m_mapEntries.end(); ++it ) {
01032
01033
if (!(*it).isValid())
continue;
01034
01035
int ev = scanEntry( &(*it) );
01036
01037
#ifdef HAVE_DNOTIFY
01038
if ((*it).m_mode == DNotifyMode) {
01039
if ((*it).isDir && (ev == Deleted)) {
01040 dList.
append( &(*it) );
01041
01042
01043
if ((*it).dn_fd) {
01044 ::close((*it).dn_fd);
01045 fd_Entry.remove((*it).dn_fd);
01046 (*it).dn_fd = 0;
01047 }
01048 }
01049
01050
else if ((*it).isDir && (ev == Created)) {
01051
01052
if ( (*it).dn_fd == 0) {
01053 cList.
append( &(*it) );
01054
if (! useDNotify( &(*it) )) {
01055
01056 useStat( &(*it) );
01057 }
01058 }
01059 }
01060 }
01061
#endif
01062
01063
if ( ev != NoChange )
01064 emitEvent( &(*it), ev);
01065 }
01066
01067
01068
#ifdef HAVE_DNOTIFY
01069
01070 Entry* e;
01071
for(e=dList.
first();e;e=dList.
next())
01072 addEntry(0, QDir::cleanDirPath( e->path+
"/.."), e,
true);
01073
01074
01075
for(e=cList.
first();e;e=cList.
next())
01076 removeEntry(0, QDir::cleanDirPath( e->path+
"/.."), e);
01077
#endif
01078
01079
if ( timerRunning )
01080 timer->start(freq);
01081
01082
QTimer::singleShot(0,
this, SLOT(slotRemoveDelayed()));
01083 }
01084
01085
#ifdef HAVE_FAM
01086
void KDirWatchPrivate::famEventReceived()
01087 {
01088
static FAMEvent fe;
01089
01090 delayRemove =
true;
01091
01092
while(use_fam && FAMPending(&fc)) {
01093
if (FAMNextEvent(&fc, &fe) == -1) {
01094
kdWarning(7001) <<
"FAM connection problem, switching to polling."
01095 <<
endl;
01096 use_fam =
false;
01097
delete sn; sn = 0;
01098
01099
01100 EntryMap::Iterator it;
01101 it = m_mapEntries.begin();
01102
for( ; it != m_mapEntries.end(); ++it )
01103
if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01104
#ifdef HAVE_DNOTIFY
01105
if (useDNotify( &(*it) ))
continue;
01106
#endif
01107
useStat( &(*it) );
01108 }
01109 }
01110
else
01111 checkFAMEvent(&fe);
01112 }
01113
01114
QTimer::singleShot(0,
this, SLOT(slotRemoveDelayed()));
01115 }
01116
01117
void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01118 {
01119
01120
if ((fe->code == FAMExists) ||
01121 (fe->code == FAMEndExist) ||
01122 (fe->code == FAMAcknowledge))
return;
01123
01124
01125
if ( *(fe->filename) ==
'.') {
01126
if (strncmp(fe->filename,
".X.err", 6) == 0)
return;
01127
if (strncmp(fe->filename,
".xsession-errors", 16) == 0)
return;
01128
01129
01130
if (strncmp(fe->filename,
".fonts.cache", 12) == 0)
return;
01131 }
01132
01133 Entry* e = 0;
01134 EntryMap::Iterator it = m_mapEntries.begin();
01135
for( ; it != m_mapEntries.end(); ++it )
01136
if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01137 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01138 e = &(*it);
01139
break;
01140 }
01141
01142
01143
01144
kdDebug(7001) <<
"Processing FAM event ("
01145 << ((fe->code == FAMChanged) ?
"FAMChanged" :
01146 (fe->code == FAMDeleted) ?
"FAMDeleted" :
01147 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
01148 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
01149 (fe->code == FAMCreated) ?
"FAMCreated" :
01150 (fe->code == FAMMoved) ?
"FAMMoved" :
01151 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
01152 (fe->code == FAMExists) ?
"FAMExists" :
01153 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
01154 <<
", " << fe->filename
01155 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01156 <<
")" <<
endl;
01157
01158
if (!e) {
01159
01160
01161
return;
01162 }
01163
01164
if (e->m_status == NonExistent) {
01165
kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
01166
return;
01167 }
01168
01169
if (e->isDir)
01170
switch (fe->code)
01171 {
01172
case FAMDeleted:
01173
01174
if (fe->filename[0] ==
'/')
01175 {
01176
01177
01178 e->m_status = NonExistent;
01179 FAMCancelMonitor(&fc, &(e->fr) );
01180
kdDebug(7001) <<
"Cancelled FAMReq "
01181 << FAMREQUEST_GETREQNUM(&(e->fr))
01182 <<
" for " << e->path <<
endl;
01183
01184 addEntry(0, QDir::cleanDirPath( e->path+
"/.."), e,
true);
01185 }
01186 emitEvent(e, Deleted, QFile::decodeName(fe->filename));
01187
break;
01188
01189
case FAMCreated: {
01190
01191 Entry *sub_entry = e->m_entries.first();
01192
for(;sub_entry; sub_entry = e->m_entries.next())
01193
if (sub_entry->path == e->path +
"/" + fe->filename)
break;
01194
if (sub_entry && sub_entry->isDir) {
01195
QString path = e->path;
01196 removeEntry(0,e->path,sub_entry);
01197 sub_entry->m_status = Normal;
01198
if (!useFAM(sub_entry))
01199 useStat(sub_entry);
01200
01201 emitEvent(sub_entry, Created);
01202 }
01203
else emitEvent(e, Created, QFile::decodeName(fe->filename));
01204
break;
01205 }
01206
01207
case FAMChanged:
01208 emitEvent(e, Changed, QFile::decodeName(fe->filename));
01209
01210
default:
01211
break;
01212 }
01213
else switch (fe->code)
01214 {
01215
case FAMCreated: emitEvent(e, Created);
01216
break;
01217
case FAMDeleted: emitEvent(e, Deleted);
01218
break;
01219
case FAMChanged: emitEvent(e, Changed);
01220
break;
01221
default:
break;
01222 }
01223 }
01224
#else
01225
void KDirWatchPrivate::famEventReceived() {}
01226
#endif
01227
01228
01229
void KDirWatchPrivate::statistics()
01230 {
01231 EntryMap::Iterator it;
01232
01233
kdDebug(7001) <<
"Entries watched:" <<
endl;
01234
if (m_mapEntries.count()==0) {
01235
kdDebug(7001) <<
" None." <<
endl;
01236 }
01237
else {
01238 it = m_mapEntries.begin();
01239
for( ; it != m_mapEntries.end(); ++it ) {
01240 Entry* e = &(*it);
01241
kdDebug(7001) <<
" " << e->path <<
" ("
01242 << ((e->m_status==Normal)?
"":
"Nonexistent ")
01243 << (e->isDir ?
"Dir":
"File") <<
", using "
01244 << ((e->m_mode == FAMMode) ?
"FAM" :
01245 (e->m_mode == DNotifyMode) ?
"DNotify" :
01246 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
01247 <<
")" <<
endl;
01248
01249 Client* c = e->m_clients.first();
01250
for(;c; c = e->m_clients.next()) {
01251
QString pending;
01252
if (c->watchingStopped) {
01253
if (c->pending & Deleted) pending +=
"deleted ";
01254
if (c->pending & Created) pending +=
"created ";
01255
if (c->pending & Changed) pending +=
"changed ";
01256
if (!pending.
isEmpty()) pending =
" (pending: " + pending +
")";
01257 pending =
", stopped" + pending;
01258 }
01259
kdDebug(7001) <<
" by " << c->instance->name()
01260 <<
" (" << c->count <<
" times)"
01261 << pending <<
endl;
01262 }
01263
if (e->m_entries.count()>0) {
01264
kdDebug(7001) <<
" dependent entries:" <<
endl;
01265 Entry* d = e->m_entries.first();
01266
for(;d; d = e->m_entries.next()) {
01267
kdDebug(7001) <<
" " << d->path <<
endl;
01268 }
01269 }
01270 }
01271 }
01272 }
01273
01274
01275
01276
01277
01278
01279
static KStaticDeleter<KDirWatch> sd_dw;
01280
KDirWatch* KDirWatch::s_pSelf = 0L;
01281
01282 KDirWatch*
KDirWatch::self()
01283 {
01284
if ( !s_pSelf ) {
01285 sd_dw.setObject( s_pSelf,
new KDirWatch );
01286 }
01287
01288
return s_pSelf;
01289 }
01290
01291 bool KDirWatch::exists()
01292 {
01293
return s_pSelf != 0;
01294 }
01295
01296 KDirWatch::KDirWatch (
QObject* parent,
const char* name)
01297 :
QObject(parent,name)
01298 {
01299
if (!name) {
01300
static int nameCounter = 0;
01301
01302 nameCounter++;
01303
setName(
QString(
"KDirWatch-%1").arg(nameCounter).ascii());
01304 }
01305
01306
if (!dwp_self)
01307 dwp_self =
new KDirWatchPrivate;
01308 d = dwp_self;
01309 d->ref();
01310
01311 _isStopped =
false;
01312 }
01313
01314 KDirWatch::~KDirWatch()
01315 {
01316
if (d) d->removeEntries(
this);
01317
if ( d->deref() )
01318 {
01319
01320
delete d;
01321 dwp_self = 0L;
01322 }
01323 }
01324
01325
01326
01327 void KDirWatch::addDir(
const QString& _path,
01328
bool watchFiles,
bool recursive)
01329 {
01330
if (watchFiles || recursive) {
01331
kdDebug(7001) <<
"addDir - recursive/watchFiles not supported in KDE 3.0"
01332 <<
endl;
01333 }
01334
if (d) d->addEntry(
this, _path, 0,
true);
01335 }
01336
01337 void KDirWatch::addFile(
const QString& _path )
01338 {
01339
if (d) d->addEntry(
this, _path, 0,
false);
01340 }
01341
01342 QDateTime KDirWatch::ctime(
const QString &_path )
01343 {
01344 KDirWatchPrivate::Entry* e = d->entry(_path);
01345
01346
if (!e)
01347
return QDateTime();
01348
01349
QDateTime result;
01350 result.
setTime_t(e->m_ctime);
01351
return result;
01352 }
01353
01354 void KDirWatch::removeDir(
const QString& _path )
01355 {
01356
if (d) d->removeEntry(
this, _path, 0);
01357 }
01358
01359 void KDirWatch::removeFile(
const QString& _path )
01360 {
01361
if (d) d->removeEntry(
this, _path, 0);
01362 }
01363
01364 bool KDirWatch::stopDirScan(
const QString& _path )
01365 {
01366
if (d) {
01367 KDirWatchPrivate::Entry *e = d->entry(_path);
01368
if (e && e->isDir)
return d->stopEntryScan(
this, e);
01369 }
01370
return false;
01371 }
01372
01373 bool KDirWatch::restartDirScan(
const QString& _path )
01374 {
01375
if (d) {
01376 KDirWatchPrivate::Entry *e = d->entry(_path);
01377
if (e && e->isDir)
01378
01379
return d->restartEntryScan(
this, e,
false);
01380 }
01381
return false;
01382 }
01383
01384 void KDirWatch::stopScan()
01385 {
01386
if (d) d->stopScan(
this);
01387 _isStopped =
true;
01388 }
01389
01390 void KDirWatch::startScan(
bool notify,
bool skippedToo )
01391 {
01392 _isStopped =
false;
01393
if (d) d->startScan(
this, notify, skippedToo);
01394 }
01395
01396
01397 bool KDirWatch::contains(
const QString& _path )
const
01398
{
01399 KDirWatchPrivate::Entry* e = d->entry(_path);
01400
if (!e)
01401
return false;
01402
01403 KDirWatchPrivate::Client* c = e->m_clients.first();
01404
for(;c;c=e->m_clients.next())
01405
if (c->instance ==
this)
return true;
01406
01407
return false;
01408 }
01409
01410 void KDirWatch::statistics()
01411 {
01412
if (!dwp_self) {
01413
kdDebug(7001) <<
"KDirWatch not used" <<
endl;
01414
return;
01415 }
01416 dwp_self->statistics();
01417 }
01418
01419
01420 void KDirWatch::setCreated(
const QString & _file )
01421 {
01422
kdDebug(7001) <<
name() <<
" emitting created " << _file <<
endl;
01423 emit
created( _file );
01424 }
01425
01426 void KDirWatch::setDirty(
const QString & _file )
01427 {
01428
kdDebug(7001) <<
name() <<
" emitting dirty " << _file <<
endl;
01429 emit
dirty( _file );
01430 }
01431
01432 void KDirWatch::setDeleted(
const QString & _file )
01433 {
01434
kdDebug(7001) <<
name() <<
" emitting deleted " << _file <<
endl;
01435 emit
deleted( _file );
01436 }
01437
01438 KDirWatch::Method
KDirWatch::internalMethod()
01439 {
01440
#ifdef HAVE_FAM
01441
if (d->use_fam)
01442
return KDirWatch::FAM;
01443
#endif
01444
#ifdef HAVE_DNOTIFY
01445
if (d->supports_dnotify)
01446
return KDirWatch::DNotify;
01447
#endif
01448
return KDirWatch::Stat;
01449 }
01450
01451
01452
#include "kdirwatch.moc"
01453
#include "kdirwatch_p.moc"
01454
01455
01456
01457