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
#include <stdio.h>
01230
#include <mntent.h>
01231
#include <string.h>
01232
bool KDirWatchPrivate::dir_isRO(
const QString & _path )
01233 {
01234
int s = 0;
01235 FILE *mnt = setmntent(_PATH_MOUNTED,
"r");
01236
struct mntent *ent;
01237
int ret = -1;
01238
01239
if (mnt != NULL) {
01240 ent = getmntent(mnt);
01241
while (ent) {
01242
int l = strlen(ent->mnt_dir);
01243
01244
if (!strncmp(ent->mnt_dir, _path.latin1(), l)) {
01245
if (l > s) {
01246 s = l;
01247 ret = (hasmntopt(ent, MNTOPT_RO) != NULL);
01248
kdDebug()<<
" ent->mnt_dir :"<<ent->mnt_dir<<
" l "<<l<<
endl;
01249 }
01250 }
01251 ent = getmntent(mnt);
01252 }
01253 endmntent(mnt);
01254 }
01255
return ret;
01256 }
01257
01258
void KDirWatchPrivate::statistics()
01259 {
01260 EntryMap::Iterator it;
01261
01262
kdDebug(7001) <<
"Entries watched:" <<
endl;
01263
if (m_mapEntries.count()==0) {
01264
kdDebug(7001) <<
" None." <<
endl;
01265 }
01266
else {
01267 it = m_mapEntries.begin();
01268
for( ; it != m_mapEntries.end(); ++it ) {
01269 Entry* e = &(*it);
01270
kdDebug(7001) <<
" " << e->path <<
" ("
01271 << ((e->m_status==Normal)?
"":
"Nonexistent ")
01272 << (e->isDir ?
"Dir":
"File") <<
", using "
01273 << ((e->m_mode == FAMMode) ?
"FAM" :
01274 (e->m_mode == DNotifyMode) ?
"DNotify" :
01275 (e->m_mode == StatMode) ?
"Stat" :
"Unknown Method")
01276 <<
")" <<
endl;
01277
01278 Client* c = e->m_clients.first();
01279
for(;c; c = e->m_clients.next()) {
01280 QString pending;
01281
if (c->watchingStopped) {
01282
if (c->pending & Deleted) pending +=
"deleted ";
01283
if (c->pending & Created) pending +=
"created ";
01284
if (c->pending & Changed) pending +=
"changed ";
01285
if (!pending.isEmpty()) pending =
" (pending: " + pending +
")";
01286 pending =
", stopped" + pending;
01287 }
01288
kdDebug(7001) <<
" by " << c->instance->name()
01289 <<
" (" << c->count <<
" times)"
01290 << pending <<
endl;
01291 }
01292
if (e->m_entries.count()>0) {
01293
kdDebug(7001) <<
" dependent entries:" <<
endl;
01294 Entry* d = e->m_entries.first();
01295
for(;d; d = e->m_entries.next()) {
01296
kdDebug(7001) <<
" " << d->path <<
endl;
01297 }
01298 }
01299 }
01300 }
01301 }
01302
01303
01304
01305
01306
01307
01308
static KStaticDeleter<KDirWatch> sd_dw;
01309
KDirWatch* KDirWatch::s_pSelf = 0L;
01310
01311 KDirWatch*
KDirWatch::self()
01312 {
01313
if ( !s_pSelf ) {
01314 sd_dw.setObject( s_pSelf,
new KDirWatch );
01315 }
01316
01317
return s_pSelf;
01318 }
01319
01320 bool KDirWatch::exists()
01321 {
01322
return s_pSelf != 0;
01323 }
01324
01325 KDirWatch::KDirWatch (
QObject* parent,
const char* name)
01326 :
QObject(parent,name)
01327 {
01328
if (!name) {
01329
static int nameCounter = 0;
01330
01331 nameCounter++;
01332 setName(QString(
"KDirWatch-%1").arg(nameCounter).ascii());
01333 }
01334
01335
if (!dwp_self)
01336 dwp_self =
new KDirWatchPrivate;
01337 d = dwp_self;
01338 d->ref();
01339
01340 _isStopped =
false;
01341 }
01342
01343 KDirWatch::~KDirWatch()
01344 {
01345
if (d) d->removeEntries(
this);
01346
if ( d->deref() )
01347 {
01348
01349
delete d;
01350 dwp_self = 0L;
01351 }
01352 }
01353
01354
01355
01356 void KDirWatch::addDir(
const QString& _path,
01357
bool watchFiles,
bool recursive)
01358 {
01359
if (watchFiles || recursive) {
01360
kdDebug(7001) <<
"addDir - recursive/watchFiles not supported in KDE 3.0"
01361 <<
endl;
01362 }
01363
if (d) d->addEntry(
this, _path, 0,
true);
01364 }
01365
01366 void KDirWatch::addFile(
const QString& _path )
01367 {
01368
if (d) d->addEntry(
this, _path, 0,
false);
01369 }
01370
01371 QDateTime KDirWatch::ctime(
const QString &_path )
01372 {
01373 KDirWatchPrivate::Entry* e = d->entry(_path);
01374
01375
if (!e)
01376
return QDateTime();
01377
01378
QDateTime result;
01379 result.setTime_t(e->m_ctime);
01380
return result;
01381 }
01382
01383 void KDirWatch::removeDir(
const QString& _path )
01384 {
01385
if (d) d->removeEntry(
this, _path, 0);
01386 }
01387
01388 void KDirWatch::removeFile(
const QString& _path )
01389 {
01390
if (d) d->removeEntry(
this, _path, 0);
01391 }
01392
01393 bool KDirWatch::stopDirScan(
const QString& _path )
01394 {
01395
if (d) {
01396 KDirWatchPrivate::Entry *e = d->entry(_path);
01397
if (e && e->isDir)
return d->stopEntryScan(
this, e);
01398 }
01399
return false;
01400 }
01401
01402 bool KDirWatch::restartDirScan(
const QString& _path )
01403 {
01404
if (d) {
01405 KDirWatchPrivate::Entry *e = d->entry(_path);
01406
if (e && e->isDir)
01407
01408
return d->restartEntryScan(
this, e,
false);
01409 }
01410
return false;
01411 }
01412
01413 void KDirWatch::stopScan()
01414 {
01415
if (d) d->stopScan(
this);
01416 _isStopped =
true;
01417 }
01418
01419 void KDirWatch::startScan(
bool notify,
bool skippedToo )
01420 {
01421 _isStopped =
false;
01422
if (d) d->startScan(
this, notify, skippedToo);
01423 }
01424
01425
01426 bool KDirWatch::contains(
const QString& _path )
const
01427
{
01428 KDirWatchPrivate::Entry* e = d->entry(_path);
01429
if (!e)
01430
return false;
01431
01432 KDirWatchPrivate::Client* c = e->m_clients.first();
01433
for(;c;c=e->m_clients.next())
01434
if (c->instance ==
this)
return true;
01435
01436
return false;
01437 }
01438
01439 void KDirWatch::statistics()
01440 {
01441
if (!dwp_self) {
01442
kdDebug(7001) <<
"KDirWatch not used" <<
endl;
01443
return;
01444 }
01445 dwp_self->statistics();
01446 }
01447
01448
01449 void KDirWatch::setCreated(
const QString & _file )
01450 {
01451
kdDebug(7001) << name() <<
" emitting created " << _file <<
endl;
01452 emit
created( _file );
01453 }
01454
01455 void KDirWatch::setDirty(
const QString & _file )
01456 {
01457
kdDebug(7001) << name() <<
" emitting dirty " << _file <<
endl;
01458 emit
dirty( _file );
01459 }
01460
01461 void KDirWatch::setDeleted(
const QString & _file )
01462 {
01463
kdDebug(7001) << name() <<
" emitting deleted " << _file <<
endl;
01464 emit
deleted( _file );
01465 }
01466
01467 KDirWatch::Method
KDirWatch::internalMethod()
01468 {
01469
#ifdef HAVE_FAM
01470
if (d->use_fam)
01471
return KDirWatch::FAM;
01472
#endif
01473
#ifdef HAVE_DNOTIFY
01474
if (d->supports_dnotify)
01475
return KDirWatch::DNotify;
01476
#endif
01477
return KDirWatch::Stat;
01478 }
01479
01480
01481
#include "kdirwatch.moc"
01482
#include "kdirwatch_p.moc"
01483
01484
01485
01486