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