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->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
00185 rescan_all =
false;
00186 connect(&rescan_timer, SIGNAL(timeout()),
this, SLOT(slotRescan()));
00187
00188
#ifdef HAVE_FAM
00189
00190
if (FAMOpen(&fc) ==0) {
00191 available +=
", FAM";
00192 use_fam=
true;
00193 sn =
new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00194 QSocketNotifier::Read,
this);
00195 connect( sn, SIGNAL(activated(
int)),
00196
this, SLOT(famEventReceived()) );
00197 }
00198
else {
00199
kdDebug(7001) <<
"Can't use FAM (fam daemon not running?)" <<
endl;
00200 use_fam=
false;
00201 }
00202
#endif
00203
00204
#ifdef HAVE_DNOTIFY
00205
supports_dnotify =
true;
00206
struct utsname uts;
00207
int major, minor, patch;
00208
if (uname(&uts) < 0)
00209 supports_dnotify =
false;
00210
else if (sscanf(uts.release,
"%d.%d.%d", &major, &minor, &patch) != 3)
00211 supports_dnotify =
false;
00212
else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
00213
kdDebug(7001) <<
"Can't use DNotify, Linux kernel too old" <<
endl;
00214 supports_dnotify =
false;
00215 }
00216
00217
if( supports_dnotify ) {
00218 available +=
", DNotify";
00219
00220 pipe(mPipe);
00221 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00222 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00223 mSn =
new QSocketNotifier( mPipe[0], QSocketNotifier::Read,
this);
00224 connect(mSn, SIGNAL(activated(
int)),
this, SLOT(slotActivated()));
00225
00226
if ( dnotify_signal == 0 )
00227 {
00228 dnotify_signal = SIGRTMIN + 8;
00229
00230
struct sigaction act;
00231 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00232 sigemptyset(&act.sa_mask);
00233 act.sa_flags = SA_SIGINFO;
00234
#ifdef SA_RESTART
00235
act.sa_flags |= SA_RESTART;
00236
#endif
00237
sigaction(dnotify_signal, &act, NULL);
00238
00239 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00240 sigaction(SIGIO, &act, &old_sigio_act);
00241 }
00242 }
00243
else
00244 {
00245 mPipe[0] = -1;
00246 mPipe[1] = -1;
00247 }
00248
#endif
00249
00250
kdDebug(7001) <<
"Available methods: " << available <<
endl;
00251 }
00252
00253
00254 KDirWatchPrivate::~KDirWatchPrivate()
00255 {
00256 timer->stop();
00257
00258
00259 removeEntries(0);
00260
00261
#ifdef HAVE_FAM
00262
if (use_fam) {
00263 FAMClose(&fc);
00264
kdDebug(7001) <<
"KDirWatch deleted (FAM closed)" <<
endl;
00265 }
00266
#endif
00267
#ifdef HAVE_DNOTIFY
00268
close(mPipe[0]);
00269
close(mPipe[1]);
00270
#endif
00271
}
00272
00273
void KDirWatchPrivate::slotActivated()
00274 {
00275
#ifdef HAVE_DNOTIFY
00276
char dummy_buf[100];
00277 read(mPipe[0], &dummy_buf, 100);
00278
00279
if (!rescan_timer.isActive())
00280 rescan_timer.start(m_PollInterval,
true);
00281
#endif
00282
}
00283
00284
00285
00286
00287
00288
void KDirWatchPrivate::Entry::propagate_dirty()
00289 {
00290 Entry* sub_entry;
00291
for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
00292 {
00293
if (!sub_entry->dirty)
00294 {
00295 sub_entry->dirty =
true;
00296 sub_entry->propagate_dirty();
00297 }
00298 }
00299 }
00300
00301
00302
00303
00304
00305
void KDirWatchPrivate::Entry::addClient(
KDirWatch* instance)
00306 {
00307 Client* client = m_clients.first();
00308
for(;client; client = m_clients.next())
00309
if (client->instance ==
instance)
break;
00310
00311
if (client) {
00312 client->count++;
00313
return;
00314 }
00315
00316 client =
new Client;
00317 client->instance =
instance;
00318 client->count = 1;
00319 client->watchingStopped =
instance->isStopped();
00320 client->pending = NoChange;
00321
00322 m_clients.append(client);
00323 }
00324
00325
void KDirWatchPrivate::Entry::removeClient(
KDirWatch* instance)
00326 {
00327 Client* client = m_clients.first();
00328
for(;client; client = m_clients.next())
00329
if (client->instance ==
instance)
break;
00330
00331
if (client) {
00332 client->count--;
00333
if (client->count == 0) {
00334 m_clients.removeRef(client);
00335
delete client;
00336 }
00337 }
00338 }
00339
00340
00341
int KDirWatchPrivate::Entry::clients()
00342 {
00343
int clients = 0;
00344 Client* client = m_clients.first();
00345
for(;client; client = m_clients.next())
00346 clients += client->count;
00347
00348
return clients;
00349 }
00350
00351
00352 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(
const QString& _path)
00353 {
00354
00355
if (_path.left(1) !=
"/") {
00356
return 0;
00357 }
00358
00359
QString path = _path;
00360
00361
if ( path.length() > 1 && path.right(1) ==
"/" )
00362 path.truncate( path.length() - 1 );
00363
00364 EntryMap::Iterator it = m_mapEntries.find( path );
00365
if ( it == m_mapEntries.end() )
00366
return 0;
00367
else
00368
return &(*it);
00369 }
00370
00371
00372
void KDirWatchPrivate::useFreq(Entry* e,
int newFreq)
00373 {
00374 e->freq = newFreq;
00375
00376
00377
if (e->freq < freq) {
00378 freq = e->freq;
00379
if (timer->isActive()) timer->changeInterval(freq);
00380
kdDebug(7001) <<
"Global Poll Freq is now " << freq <<
" msec" <<
endl;
00381 }
00382 }
00383
00384
00385
#if defined(HAVE_FAM)
00386
00387
bool KDirWatchPrivate::useFAM(Entry* e)
00388 {
00389
if (!use_fam)
return false;
00390
00391 e->m_mode = FAMMode;
00392 e->dirty =
false;
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->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
QCString tpath = QFile::encodeName(path);
00591
bool exists = (
stat(tpath, &stat_buf) == 0);
00592
00593 Entry newEntry;
00594 m_mapEntries.insert( path, newEntry );
00595
00596 Entry* e = &(m_mapEntries[path]);
00597
00598
if (exists) {
00599 e->isDir = S_ISDIR(stat_buf.st_mode);
00600
00601
if (e->isDir && !isDir)
00602 qWarning(
"KDirWatch: %s is a directory. Use addDir!", path.ascii());
00603
else if (!e->isDir && isDir)
00604 qWarning(
"KDirWatch: %s is a file. Use addFile!", path.ascii());
00605
00606 e->m_ctime = stat_buf.st_ctime;
00607 e->m_status = Normal;
00608 e->m_nlink = stat_buf.st_nlink;
00609 }
00610
else {
00611 e->isDir = isDir;
00612 e->m_ctime = invalid_ctime;
00613 e->m_status = NonExistent;
00614 e->m_nlink = 0;
00615 }
00616
00617 e->path = path;
00618
if (sub_entry)
00619 e->m_entries.append(sub_entry);
00620
else
00621 e->addClient(instance);
00622
00623
kdDebug(7001) <<
"Added " << (e->isDir ?
"Dir ":
"File ") << path
00624 << (e->m_status == NonExistent ?
" NotExisting" :
"")
00625 << (sub_entry ?
QString(
" for %1").arg(sub_entry->path) :
QString(
""))
00626 << (
instance ?
QString(
" [%1]").arg(
instance->
name()) :
QString(
""))
00627 <<
endl;
00628
00629
00630
00631 e->m_mode = UnknownMode;
00632 e->msecLeft = 0;
00633
00634
if ( isNoisyFile( tpath ) )
00635
return;
00636
00637
#if defined(HAVE_FAM)
00638
if (useFAM(e))
return;
00639
#endif
00640
00641
#ifdef HAVE_DNOTIFY
00642
if (useDNotify(e))
return;
00643
#endif
00644
00645 useStat(e);
00646 }
00647
00648
00649
void KDirWatchPrivate::removeEntry(
KDirWatch* instance,
00650
const QString& _path, Entry* sub_entry )
00651 {
00652 Entry* e = entry(_path);
00653
if (!e) {
00654
kdWarning(7001) <<
"KDirWatch::removeDir can't handle '" << _path <<
"'" <<
endl;
00655
return;
00656 }
00657
00658
if (sub_entry)
00659 e->m_entries.removeRef(sub_entry);
00660
else
00661 e->removeClient(instance);
00662
00663
if (e->m_clients.count() || e->m_entries.count())
00664
return;
00665
00666
if (delayRemove) {
00667
00668
if (removeList.findRef(e)==-1)
00669 removeList.append(e);
00670
00671
return;
00672 }
00673
00674
#ifdef HAVE_FAM
00675
if (e->m_mode == FAMMode) {
00676
if ( e->m_status == Normal) {
00677 FAMCancelMonitor(&fc, &(e->fr) );
00678
kdDebug(7001) <<
"Cancelled FAM (Req "
00679 << FAMREQUEST_GETREQNUM(&(e->fr))
00680 <<
") for " << e->path <<
endl;
00681 }
00682
else {
00683
if (e->isDir)
00684 removeEntry(0, QDir::cleanDirPath(e->path+
"/.."), e);
00685
else
00686 removeEntry(0,
QFileInfo(e->path).dirPath(
true), e);
00687 }
00688 }
00689
#endif
00690
00691
#ifdef HAVE_DNOTIFY
00692
if (e->m_mode == DNotifyMode) {
00693
if (!e->isDir) {
00694 removeEntry(0,
QFileInfo(e->path).dirPath(
true), e);
00695 }
00696
else {
00697
00698
if ( e->m_status == Normal) {
00699
if (e->dn_fd) {
00700 ::close(e->dn_fd);
00701 fd_Entry.remove(e->dn_fd);
00702
00703
kdDebug(7001) <<
"Cancelled DNotify (fd " << e->dn_fd
00704 <<
") for " << e->path <<
endl;
00705 e->dn_fd = 0;
00706
00707 }
00708 }
00709
else {
00710 removeEntry(0, QDir::cleanDirPath(e->path+
"/.."), e);
00711 }
00712 }
00713 }
00714
#endif
00715
00716
if (e->m_mode == StatMode) {
00717 statEntries--;
00718
if ( statEntries == 0 ) {
00719 timer->stop();
00720
kdDebug(7001) <<
" Stopped Polling Timer" <<
endl;
00721 }
00722 }
00723
00724
kdDebug(7001) <<
"Removed " << (e->isDir ?
"Dir ":
"File ") << e->path
00725 << (sub_entry ?
QString(
" for %1").arg(sub_entry->path) :
QString(
""))
00726 << (
instance ?
QString(
" [%1]").arg(
instance->
name()) :
QString(
""))
00727 <<
endl;
00728 m_mapEntries.remove( e->path );
00729 }
00730
00731
00732
00733
00734
00735
void KDirWatchPrivate::removeEntries(
KDirWatch* instance )
00736 {
00737
QPtrList<Entry> list;
00738
int minfreq = 3600000;
00739
00740
00741 EntryMap::Iterator it = m_mapEntries.begin();
00742
for( ; it != m_mapEntries.end(); ++it ) {
00743 Client* c = (*it).m_clients.first();
00744
for(;c;c=(*it).m_clients.next())
00745
if (c->instance ==
instance)
break;
00746
if (c) {
00747 c->count = 1;
00748 list.append(&(*it));
00749 }
00750
else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00751 minfreq = (*it).freq;
00752 }
00753
00754
for(Entry* e=list.first();e;e=list.next())
00755 removeEntry(instance, e->path, 0);
00756
00757
if (minfreq > freq) {
00758
00759 freq = minfreq;
00760
if (timer->isActive()) timer->changeInterval(freq);
00761
kdDebug(7001) <<
"Poll Freq now " << freq <<
" msec" <<
endl;
00762 }
00763 }
00764
00765
00766
bool KDirWatchPrivate::stopEntryScan(
KDirWatch* instance, Entry* e)
00767 {
00768
int stillWatching = 0;
00769 Client* c = e->m_clients.first();
00770
for(;c;c=e->m_clients.next()) {
00771
if (!
instance ||
instance == c->instance)
00772 c->watchingStopped =
true;
00773
else if (!c->watchingStopped)
00774 stillWatching += c->count;
00775 }
00776
00777
kdDebug(7001) <<
instance->name() <<
" stopped scanning " << e->path
00778 <<
" (now " << stillWatching <<
" watchers)" <<
endl;
00779
00780
if (stillWatching == 0) {
00781
00782 e->m_ctime = invalid_ctime;
00783
00784 }
00785
return true;
00786 }
00787
00788
00789
bool KDirWatchPrivate::restartEntryScan(
KDirWatch* instance, Entry* e,
00790
bool notify)
00791 {
00792
int wasWatching = 0, newWatching = 0;
00793 Client* c = e->m_clients.first();
00794
for(;c;c=e->m_clients.next()) {
00795
if (!c->watchingStopped)
00796 wasWatching += c->count;
00797
else if (!
instance ||
instance == c->instance) {
00798 c->watchingStopped =
false;
00799 newWatching += c->count;
00800 }
00801 }
00802
if (newWatching == 0)
00803
return false;
00804
00805
kdDebug(7001) <<
instance->name() <<
" restarted scanning " << e->path
00806 <<
" (now " << wasWatching+newWatching <<
" watchers)" <<
endl;
00807
00808
00809
00810
int ev = NoChange;
00811
if (wasWatching == 0) {
00812
if (!notify) {
00813
struct stat stat_buf;
00814
bool exists = (
stat(QFile::encodeName(e->path), &stat_buf) == 0);
00815
if (exists) {
00816 e->m_ctime = stat_buf.st_ctime;
00817 e->m_status = Normal;
00818 e->m_nlink = stat_buf.st_nlink;
00819 }
00820
else {
00821 e->m_ctime = invalid_ctime;
00822 e->m_status = NonExistent;
00823 e->m_nlink = 0;
00824 }
00825 }
00826 e->msecLeft = 0;
00827 ev = scanEntry(e);
00828 }
00829 emitEvent(e,ev);
00830
00831
return true;
00832 }
00833
00834
00835
void KDirWatchPrivate::stopScan(
KDirWatch* instance)
00836 {
00837 EntryMap::Iterator it = m_mapEntries.begin();
00838
for( ; it != m_mapEntries.end(); ++it )
00839 stopEntryScan(instance, &(*it));
00840 }
00841
00842
00843
void KDirWatchPrivate::startScan(
KDirWatch* instance,
00844
bool notify,
bool skippedToo )
00845 {
00846
if (!notify)
00847 resetList(instance,skippedToo);
00848
00849 EntryMap::Iterator it = m_mapEntries.begin();
00850
for( ; it != m_mapEntries.end(); ++it )
00851 restartEntryScan(instance, &(*it), notify);
00852
00853
00854 }
00855
00856
00857
00858
void KDirWatchPrivate::resetList(
KDirWatch* ,
00859
bool skippedToo )
00860 {
00861 EntryMap::Iterator it = m_mapEntries.begin();
00862
for( ; it != m_mapEntries.end(); ++it ) {
00863
00864 Client* c = (*it).m_clients.first();
00865
for(;c;c=(*it).m_clients.next())
00866
if (!c->watchingStopped || skippedToo)
00867 c->pending = NoChange;
00868 }
00869 }
00870
00871
00872
00873
int KDirWatchPrivate::scanEntry(Entry* e)
00874 {
00875
#ifdef HAVE_FAM
00876
if (e->m_mode == FAMMode) {
00877
00878
if(!e->dirty)
return NoChange;
00879 e->dirty =
false;
00880 }
00881
#endif
00882
00883
00884
if (e->m_mode == UnknownMode)
return NoChange;
00885
00886
#ifdef HAVE_DNOTIFY
00887
if (e->m_mode == DNotifyMode) {
00888
00889
if(!e->dirty)
return NoChange;
00890 e->dirty =
false;
00891 }
00892
#endif
00893
00894
if (e->m_mode == StatMode) {
00895
00896
00897
00898
00899 e->msecLeft -= freq;
00900
if (e->msecLeft>0)
return NoChange;
00901 e->msecLeft += e->freq;
00902 }
00903
00904
struct stat stat_buf;
00905
bool exists = (
stat(QFile::encodeName(e->path), &stat_buf) == 0);
00906
if (exists) {
00907
00908
if (e->m_status == NonExistent) {
00909 e->m_ctime = stat_buf.st_ctime;
00910 e->m_status = Normal;
00911 e->m_nlink = stat_buf.st_nlink;
00912
return Created;
00913 }
00914
00915
if ( (e->m_ctime != invalid_ctime) &&
00916 ((stat_buf.st_ctime != e->m_ctime) ||
00917 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
00918 e->m_ctime = stat_buf.st_ctime;
00919 e->m_nlink = stat_buf.st_nlink;
00920
return Changed;
00921 }
00922
00923
return NoChange;
00924 }
00925
00926
00927
00928
if (e->m_ctime == invalid_ctime)
00929
return NoChange;
00930
00931 e->m_ctime = invalid_ctime;
00932 e->m_nlink = 0;
00933 e->m_status = NonExistent;
00934
00935
return Deleted;
00936 }
00937
00938
00939
00940
00941
00942
void KDirWatchPrivate::emitEvent(Entry* e,
int event,
const QString &fileName)
00943 {
00944
QString path = e->path;
00945
if (!fileName.isEmpty()) {
00946
if (fileName[0] ==
'/')
00947 path = fileName;
00948
else
00949 path +=
"/" + fileName;
00950 }
00951
00952 Client* c = e->m_clients.first();
00953
for(;c;c=e->m_clients.next()) {
00954
if (c->instance==0 || c->count==0)
continue;
00955
00956
if (c->watchingStopped) {
00957
00958
if (
event == Changed)
00959 c->pending |=
event;
00960
else if (
event == Created ||
event == Deleted)
00961 c->pending =
event;
00962
continue;
00963 }
00964
00965
if (
event == NoChange ||
event == Changed)
00966
event |= c->pending;
00967 c->pending = NoChange;
00968
if (
event == NoChange)
continue;
00969
00970
if (
event & Deleted) {
00971 c->instance->setDeleted(path);
00972
00973
continue;
00974 }
00975
00976
if (
event & Created) {
00977 c->instance->setCreated(path);
00978
00979 }
00980
00981
if (
event & Changed)
00982 c->instance->setDirty(path);
00983 }
00984 }
00985
00986
00987
void KDirWatchPrivate::slotRemoveDelayed()
00988 {
00989 Entry* e;
00990 delayRemove =
false;
00991
for(e=removeList.first();e;e=removeList.next())
00992 removeEntry(0, e->path, 0);
00993 removeList.clear();
00994 }
00995
00996
00997
00998
00999
void KDirWatchPrivate::slotRescan()
01000 {
01001 EntryMap::Iterator it;
01002
01003
01004
01005
01006
bool timerRunning = timer->isActive();
01007
if ( timerRunning )
01008 timer->stop();
01009
01010
01011
01012 delayRemove =
true;
01013
01014
#ifdef HAVE_DNOTIFY
01015
QPtrList<Entry> dList, cList;
01016
#endif
01017
01018
if (rescan_all)
01019 {
01020
01021 it = m_mapEntries.begin();
01022
for( ; it != m_mapEntries.end(); ++it )
01023 (*it).dirty =
true;
01024 rescan_all =
false;
01025 }
01026
else
01027 {
01028
01029 it = m_mapEntries.begin();
01030
for( ; it != m_mapEntries.end(); ++it )
01031
if ( ((*it).m_mode == DNotifyMode) && (*it).dirty )
01032 (*it).propagate_dirty();
01033 }
01034
01035 it = m_mapEntries.begin();
01036
for( ; it != m_mapEntries.end(); ++it ) {
01037
01038
if (!(*it).isValid())
continue;
01039
01040
int ev = scanEntry( &(*it) );
01041
01042
#ifdef HAVE_DNOTIFY
01043
if ((*it).m_mode == DNotifyMode) {
01044
if ((*it).isDir && (ev == Deleted)) {
01045 dList.append( &(*it) );
01046
01047
01048
if ((*it).dn_fd) {
01049 ::close((*it).dn_fd);
01050 fd_Entry.remove((*it).dn_fd);
01051 (*it).dn_fd = 0;
01052 }
01053 }
01054
01055
else if ((*it).isDir && (ev == Created)) {
01056
01057
if ( (*it).dn_fd == 0) {
01058 cList.append( &(*it) );
01059
if (! useDNotify( &(*it) )) {
01060
01061 useStat( &(*it) );
01062 }
01063 }
01064 }
01065 }
01066
#endif
01067
01068
if ( ev != NoChange )
01069 emitEvent( &(*it), ev);
01070 }
01071
01072
01073
#ifdef HAVE_DNOTIFY
01074
01075 Entry* e;
01076
for(e=dList.first();e;e=dList.next())
01077 addEntry(0, QDir::cleanDirPath( e->path+
"/.."), e,
true);
01078
01079
01080
for(e=cList.first();e;e=cList.next())
01081 removeEntry(0, QDir::cleanDirPath( e->path+
"/.."), e);
01082
#endif
01083
01084
if ( timerRunning )
01085 timer->start(freq);
01086
01087 QTimer::singleShot(0,
this, SLOT(slotRemoveDelayed()));
01088 }
01089
01090
bool KDirWatchPrivate::isNoisyFile(
const char * filename )
01091 {
01092
01093
if ( *filename ==
'.') {
01094
if (strncmp(filename,
".X.err", 6) == 0)
return true;
01095
if (strncmp(filename,
".xsession-errors", 16) == 0)
return true;
01096
01097
01098
if (strncmp(filename,
".fonts.cache", 12) == 0)
return true;
01099 }
01100
01101
return false;
01102 }
01103
01104
#ifdef HAVE_FAM
01105
void KDirWatchPrivate::famEventReceived()
01106 {
01107
static FAMEvent fe;
01108
01109 delayRemove =
true;
01110
01111
while(use_fam && FAMPending(&fc)) {
01112
if (FAMNextEvent(&fc, &fe) == -1) {
01113
kdWarning(7001) <<
"FAM connection problem, switching to polling."
01114 <<
endl;
01115 use_fam =
false;
01116
delete sn; sn = 0;
01117
01118
01119 EntryMap::Iterator it;
01120 it = m_mapEntries.begin();
01121
for( ; it != m_mapEntries.end(); ++it )
01122
if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01123
#ifdef HAVE_DNOTIFY
01124
if (useDNotify( &(*it) ))
continue;
01125
#endif
01126
useStat( &(*it) );
01127 }
01128 }
01129
else
01130 checkFAMEvent(&fe);
01131 }
01132
01133 QTimer::singleShot(0,
this, SLOT(slotRemoveDelayed()));
01134 }
01135
01136
void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01137 {
01138
01139
if ((fe->code == FAMExists) ||
01140 (fe->code == FAMEndExist) ||
01141 (fe->code == FAMAcknowledge))
return;
01142
01143
if ( isNoisyFile( fe->filename ) )
01144
return;
01145
01146 Entry* e = 0;
01147 EntryMap::Iterator it = m_mapEntries.begin();
01148
for( ; it != m_mapEntries.end(); ++it )
01149
if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01150 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01151 e = &(*it);
01152
break;
01153 }
01154
01155
01156
01157
kdDebug(7001) <<
"Processing FAM event ("
01158 << ((fe->code == FAMChanged) ?
"FAMChanged" :
01159 (fe->code == FAMDeleted) ?
"FAMDeleted" :
01160 (fe->code == FAMStartExecuting) ?
"FAMStartExecuting" :
01161 (fe->code == FAMStopExecuting) ?
"FAMStopExecuting" :
01162 (fe->code == FAMCreated) ?
"FAMCreated" :
01163 (fe->code == FAMMoved) ?
"FAMMoved" :
01164 (fe->code == FAMAcknowledge) ?
"FAMAcknowledge" :
01165 (fe->code == FAMExists) ?
"FAMExists" :
01166 (fe->code == FAMEndExist) ?
"FAMEndExist" :
"Unknown Code")
01167 <<
", " << fe->filename
01168 <<
", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01169 <<
")" <<
endl;
01170
01171
if (!e) {
01172
01173
01174
return;
01175 }
01176
01177
if (e->m_status == NonExistent) {
01178
kdDebug(7001) <<
"FAM event for nonExistent entry " << e->path <<
endl;
01179
return;
01180 }
01181
01182
01183 e->dirty =
true;
01184
if (!rescan_timer.isActive())
01185 rescan_timer.start(m_PollInterval,
true);
01186
01187
01188
if (e->isDir)
01189
switch (fe->code)
01190 {
01191
case FAMDeleted:
01192
01193
if (fe->filename[0] ==
'/')
01194 {
01195
01196
01197 e->m_status = NonExistent;
01198 FAMCancelMonitor(&fc, &(e->fr) );
01199
kdDebug(7001) <<
"Cancelled FAMReq "
01200 << FAMREQUEST_GETREQNUM(&(e->fr))
01201 <<
" for " << e->path <<
endl;
01202
01203 addEntry(0, QDir::cleanDirPath( e->path+
"/.."), e,
true);
01204 }
01205
break;
01206
01207
case FAMCreated: {
01208
01209 Entry *sub_entry = e->m_entries.first();
01210
for(;sub_entry; sub_entry = e->m_entries.next())
01211
if (sub_entry->path == e->path +
"/" + fe->filename)
break;
01212
if (sub_entry && sub_entry->isDir) {
01213
QString path = e->path;
01214 removeEntry(0,e->path,sub_entry);
01215 sub_entry->m_status = Normal;
01216
if (!useFAM(sub_entry))
01217 useStat(sub_entry);
01218 }
01219
break;
01220 }
01221
01222
default:
01223
break;
01224 }
01225 }
01226
#else
01227
void KDirWatchPrivate::famEventReceived() {}
01228
#endif
01229
01230
01231
void KDirWatchPrivate::statistics()
01232 {
01233 EntryMap::Iterator it;
01234
01235
kdDebug(7001) <<
"Entries watched:" <<
endl;
01236
if (m_mapEntries.count()==0) {
01237
kdDebug(7001) <<
" None." <<
endl;
01238 }
01239
else {
01240 it = m_mapEntries.begin();
01241
for( ; it != m_mapEntries.end(); ++it ) {
01242 Entry* e = &(*it);
01243
kdDebug(7001) <<
" " << e->path <<
" ("
01244 << ((e->m_status==Normal)?
"":
"Nonexistent ")
01245 << (e->isDir ?
"Dir":
"File") <<
", using "
01246 << ((e->m_mode == FAMMode) ?
"FAM" :
01247 (e->m_mode == DNotifyMode) ?
"DNotify" :
01248 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
01249 <<
")" <<
endl;
01250
01251 Client* c = e->m_clients.first();
01252
for(;c; c = e->m_clients.next()) {
01253
QString pending;
01254
if (c->watchingStopped) {
01255
if (c->pending & Deleted) pending +=
"deleted ";
01256
if (c->pending & Created) pending +=
"created ";
01257
if (c->pending & Changed) pending +=
"changed ";
01258
if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
01259 pending =
", stopped" + pending;
01260 }
01261
kdDebug(7001) <<
" by " << c->instance->name()
01262 <<
" (" << c->count <<
" times)"
01263 << pending <<
endl;
01264 }
01265
if (e->m_entries.count()>0) {
01266
kdDebug(7001) <<
" dependent entries:" <<
endl;
01267 Entry* d = e->m_entries.first();
01268
for(;d; d = e->m_entries.next()) {
01269
kdDebug(7001) <<
" " << d->path <<
endl;
01270 }
01271 }
01272 }
01273 }
01274 }
01275
01276
01277
01278
01279
01280
01281
static KStaticDeleter<KDirWatch> sd_dw;
01282
KDirWatch* KDirWatch::s_pSelf = 0L;
01283
01284 KDirWatch*
KDirWatch::self()
01285 {
01286
if ( !s_pSelf ) {
01287 sd_dw.setObject( s_pSelf,
new KDirWatch );
01288 }
01289
01290
return s_pSelf;
01291 }
01292
01293 bool KDirWatch::exists()
01294 {
01295
return s_pSelf != 0;
01296 }
01297
01298 KDirWatch::KDirWatch (
QObject* parent,
const char* name)
01299 :
QObject(parent,name)
01300 {
01301
if (!name) {
01302
static int nameCounter = 0;
01303
01304 nameCounter++;
01305 setName(
QString(
"KDirWatch-%1").arg(nameCounter).ascii());
01306 }
01307
01308
if (!dwp_self)
01309 dwp_self =
new KDirWatchPrivate;
01310 d = dwp_self;
01311 d->ref();
01312
01313 _isStopped =
false;
01314 }
01315
01316 KDirWatch::~KDirWatch()
01317 {
01318
if (d) d->removeEntries(
this);
01319
if ( d->deref() )
01320 {
01321
01322
delete d;
01323 dwp_self = 0L;
01324 }
01325 }
01326
01327
01328
01329 void KDirWatch::addDir(
const QString& _path,
01330
bool watchFiles,
bool recursive)
01331 {
01332
if (watchFiles || recursive) {
01333
kdDebug(7001) <<
"addDir - recursive/watchFiles not supported in KDE 3.0"
01334 <<
endl;
01335 }
01336
if (d) d->addEntry(
this, _path, 0,
true);
01337 }
01338
01339 void KDirWatch::addFile(
const QString& _path )
01340 {
01341
if (d) d->addEntry(
this, _path, 0,
false);
01342 }
01343
01344 QDateTime KDirWatch::ctime(
const QString &_path )
01345 {
01346 KDirWatchPrivate::Entry* e = d->entry(_path);
01347
01348
if (!e)
01349
return QDateTime();
01350
01351
QDateTime result;
01352 result.setTime_t(e->m_ctime);
01353
return result;
01354 }
01355
01356 void KDirWatch::removeDir(
const QString& _path )
01357 {
01358
if (d) d->removeEntry(
this, _path, 0);
01359 }
01360
01361 void KDirWatch::removeFile(
const QString& _path )
01362 {
01363
if (d) d->removeEntry(
this, _path, 0);
01364 }
01365
01366 bool KDirWatch::stopDirScan(
const QString& _path )
01367 {
01368
if (d) {
01369 KDirWatchPrivate::Entry *e = d->entry(_path);
01370
if (e && e->isDir)
return d->stopEntryScan(
this, e);
01371 }
01372
return false;
01373 }
01374
01375 bool KDirWatch::restartDirScan(
const QString& _path )
01376 {
01377
if (d) {
01378 KDirWatchPrivate::Entry *e = d->entry(_path);
01379
if (e && e->isDir)
01380
01381
return d->restartEntryScan(
this, e,
false);
01382 }
01383
return false;
01384 }
01385
01386 void KDirWatch::stopScan()
01387 {
01388
if (d) d->stopScan(
this);
01389 _isStopped =
true;
01390 }
01391
01392 void KDirWatch::startScan(
bool notify,
bool skippedToo )
01393 {
01394 _isStopped =
false;
01395
if (d) d->startScan(
this, notify, skippedToo);
01396 }
01397
01398
01399 bool KDirWatch::contains(
const QString& _path )
const
01400
{
01401 KDirWatchPrivate::Entry* e = d->entry(_path);
01402
if (!e)
01403
return false;
01404
01405 KDirWatchPrivate::Client* c = e->m_clients.first();
01406
for(;c;c=e->m_clients.next())
01407
if (c->instance ==
this)
return true;
01408
01409
return false;
01410 }
01411
01412 void KDirWatch::statistics()
01413 {
01414
if (!dwp_self) {
01415
kdDebug(7001) <<
"KDirWatch not used" <<
endl;
01416
return;
01417 }
01418 dwp_self->statistics();
01419 }
01420
01421
01422 void KDirWatch::setCreated(
const QString & _file )
01423 {
01424
kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
01425 emit
created( _file );
01426 }
01427
01428 void KDirWatch::setDirty(
const QString & _file )
01429 {
01430
kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
01431 emit
dirty( _file );
01432 }
01433
01434 void KDirWatch::setDeleted(
const QString & _file )
01435 {
01436
kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
01437 emit
deleted( _file );
01438 }
01439
01440 KDirWatch::Method
KDirWatch::internalMethod()
01441 {
01442
#ifdef HAVE_FAM
01443
if (d->use_fam)
01444
return KDirWatch::FAM;
01445
#endif
01446
#ifdef HAVE_DNOTIFY
01447
if (d->supports_dnotify)
01448
return KDirWatch::DNotify;
01449
#endif
01450
return KDirWatch::Stat;
01451 }
01452
01453
01454
#include "kdirwatch.moc"
01455
#include "kdirwatch_p.moc"
01456
01457
01458
01459