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 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 if (fd<0) {
00455 e->m_mode = UnknownMode;
00456 return false;
00457 }
00458
00459 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00460
00461 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00462 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00463
00464 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
00465 fcntl(fd, F_NOTIFY, mask) < 0) {
00466
00467 kdDebug(7001) << "Not using Linux Directory Notifications."
00468 << endl;
00469 supports_dnotify = false;
00470 ::close(fd);
00471 e->m_mode = UnknownMode;
00472 return false;
00473 }
00474
00475 fd_Entry.replace(fd, e);
00476 e->dn_fd = fd;
00477
00478 kdDebug(7001) << " Setup DNotify (fd " << fd
00479 << ") for " << e->path << endl;
00480 }
00481 else {
00482 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00483 }
00484 }
00485 else {
00486
00487
00488 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00489 }
00490
00491 return true;
00492 }
00493 #endif
00494
00495
00496 bool KDirWatchPrivate::useStat(Entry* e)
00497 {
00498 if (KIO::probably_slow_mounted(e->path))
00499 useFreq(e, m_nfsPollInterval);
00500 else
00501 useFreq(e, m_PollInterval);
00502
00503 if (e->m_mode != StatMode) {
00504 e->m_mode = StatMode;
00505 statEntries++;
00506
00507 if ( statEntries == 1 ) {
00508
00509 timer->start(freq);
00510 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
00511 }
00512 }
00513
00514 kdDebug(7001) << " Setup Stat (freq " << e->freq
00515 << ") for " << e->path << endl;
00516
00517 return true;
00518 }
00519
00520
00521
00522
00523
00524
00525
00526 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00527 Entry* sub_entry, bool isDir)
00528 {
00529 QString path = _path;
00530 if (path.startsWith("/dev/") || (path == "/dev"))
00531 return;
00532
00533 if ( path.length() > 1 && path.right(1) == "/" )
00534 path.truncate( path.length() - 1 );
00535
00536 EntryMap::Iterator it = m_mapEntries.find( path );
00537 if ( it != m_mapEntries.end() )
00538 {
00539 if (sub_entry) {
00540 (*it).m_entries.append(sub_entry);
00541 kdDebug(7001) << "Added already watched Entry " << path
00542 << " (for " << sub_entry->path << ")" << endl;
00543 #ifdef HAVE_DNOTIFY
00544 Entry* e = &(*it);
00545 if( e->dn_fd > 0 ) {
00546 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00547
00548 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00549 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00550 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
00551 ::close(e->dn_fd);
00552 e->m_mode = UnknownMode;
00553 fd_Entry.remove(e->dn_fd);
00554 e->dn_fd = 0;
00555 useStat( e );
00556 }
00557 }
00558 #endif
00559 }
00560 else {
00561 (*it).addClient(instance);
00562 kdDebug(7001) << "Added already watched Entry " << path
00563 << " (now " << (*it).clients() << " clients)"
00564 << QString(" [%1]").arg(instance->name()) << endl;
00565 }
00566 return;
00567 }
00568
00569
00570
00571 QFileInfo info(path);
00572
00573 Entry newEntry;
00574 m_mapEntries.insert( path, newEntry );
00575
00576 Entry* e = &(m_mapEntries[path]);
00577
00578 if (info.exists()) {
00579 e->isDir = info.isDir();
00580
00581 if (e->isDir && !isDir)
00582 qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
00583 else if (!e->isDir && isDir)
00584 qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
00585
00586 e->m_ctime = info.lastModified();
00587 e->m_status = Normal;
00588 }
00589 else {
00590 e->isDir = isDir;
00591 e->m_ctime = QDateTime();
00592 e->m_status = NonExistent;
00593 }
00594
00595 e->path = path;
00596 if (sub_entry)
00597 e->m_entries.append(sub_entry);
00598 else
00599 e->addClient(instance);
00600
00601 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
00602 << (e->m_status == NonExistent ? " NotExisting" : "")
00603 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00604 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00605 << endl;
00606
00607
00608
00609 e->m_mode = UnknownMode;
00610 e->msecLeft = 0;
00611
00612 #if defined(HAVE_FAM)
00613 if (useFAM(e)) return;
00614 #endif
00615
00616 #ifdef HAVE_DNOTIFY
00617 if (useDNotify(e)) return;
00618 #endif
00619
00620 useStat(e);
00621 }
00622
00623
00624 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
00625 const QString& _path, Entry* sub_entry )
00626 {
00627 Entry* e = entry(_path);
00628 if (!e) {
00629 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
00630 return;
00631 }
00632
00633 if (sub_entry)
00634 e->m_entries.removeRef(sub_entry);
00635 else
00636 e->removeClient(instance);
00637
00638 if (e->m_clients.count() || e->m_entries.count())
00639 return;
00640
00641 if (delayRemove) {
00642
00643 if (removeList.findRef(e)==-1)
00644 removeList.append(e);
00645
00646 return;
00647 }
00648
00649 #ifdef HAVE_FAM
00650 if (e->m_mode == FAMMode) {
00651 if ( e->m_status == Normal) {
00652 FAMCancelMonitor(&fc, &(e->fr) );
00653 kdDebug(7001) << "Cancelled FAM (Req "
00654 << FAMREQUEST_GETREQNUM(&(e->fr))
00655 << ") for " << e->path << endl;
00656 }
00657 else {
00658 if (e->isDir)
00659 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00660 else
00661 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00662 }
00663 }
00664 #endif
00665
00666 #ifdef HAVE_DNOTIFY
00667 if (e->m_mode == DNotifyMode) {
00668 if (!e->isDir) {
00669 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00670 }
00671 else {
00672
00673 if ( e->m_status == Normal) {
00674 if (e->dn_fd) {
00675 ::close(e->dn_fd);
00676 fd_Entry.remove(e->dn_fd);
00677
00678 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
00679 << ") for " << e->path << endl;
00680 e->dn_fd = 0;
00681
00682 }
00683 }
00684 else {
00685 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00686 }
00687 }
00688 }
00689 #endif
00690
00691 if (e->m_mode == StatMode) {
00692 statEntries--;
00693 if ( statEntries == 0 ) {
00694 timer->stop();
00695 kdDebug(7001) << " Stopped Polling Timer" << endl;
00696 }
00697 }
00698
00699 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00700 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00701 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00702 << endl;
00703 m_mapEntries.remove( e->path );
00704 }
00705
00706
00707
00708
00709
00710 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00711 {
00712 QPtrList<Entry> list;
00713 int minfreq = 3600000;
00714
00715
00716 EntryMap::Iterator it = m_mapEntries.begin();
00717 for( ; it != m_mapEntries.end(); ++it ) {
00718 Client* c = (*it).m_clients.first();
00719 for(;c;c=(*it).m_clients.next())
00720 if (c->instance == instance) break;
00721 if (c) {
00722 c->count = 1;
00723 list.append(&(*it));
00724 }
00725 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00726 minfreq = (*it).freq;
00727 }
00728
00729 for(Entry* e=list.first();e;e=list.next())
00730 removeEntry(instance, e->path, 0);
00731
00732 if (minfreq > freq) {
00733
00734 freq = minfreq;
00735 if (timer->isActive()) timer->changeInterval(freq);
00736 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
00737 }
00738 }
00739
00740
00741 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00742 {
00743 int stillWatching = 0;
00744 Client* c = e->m_clients.first();
00745 for(;c;c=e->m_clients.next()) {
00746 if (!instance || instance == c->instance)
00747 c->watchingStopped = true;
00748 else if (!c->watchingStopped)
00749 stillWatching += c->count;
00750 }
00751
00752 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
00753 << " (now " << stillWatching << " watchers)" << endl;
00754
00755 if (stillWatching == 0) {
00756
00757 e->m_ctime = QDateTime();
00758
00759 }
00760 return true;
00761 }
00762
00763
00764 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
00765 bool notify)
00766 {
00767 int wasWatching = 0, newWatching = 0;
00768 Client* c = e->m_clients.first();
00769 for(;c;c=e->m_clients.next()) {
00770 if (!c->watchingStopped)
00771 wasWatching += c->count;
00772 else if (!instance || instance == c->instance) {
00773 c->watchingStopped = false;
00774 newWatching += c->count;
00775 }
00776 }
00777 if (newWatching == 0)
00778 return false;
00779
00780 kdDebug(7001) << instance->name() << " restarted scanning " << e->path
00781 << " (now " << wasWatching+newWatching << " watchers)" << endl;
00782
00783
00784
00785 int ev = NoChange;
00786 if (wasWatching == 0) {
00787 if (!notify) {
00788 QFileInfo info(e->path);
00789 if (info.exists()) {
00790 e->m_ctime = info.lastModified();
00791 e->m_status = Normal;
00792 }
00793 else {
00794 e->m_ctime = QDateTime();
00795 e->m_status = NonExistent;
00796 }
00797 }
00798 e->msecLeft = 0;
00799 ev = scanEntry(e);
00800 }
00801 emitEvent(e,ev);
00802
00803 return true;
00804 }
00805
00806
00807 void KDirWatchPrivate::stopScan(KDirWatch* instance)
00808 {
00809 EntryMap::Iterator it = m_mapEntries.begin();
00810 for( ; it != m_mapEntries.end(); ++it )
00811 stopEntryScan(instance, &(*it));
00812 }
00813
00814
00815 void KDirWatchPrivate::startScan(KDirWatch* instance,
00816 bool notify, bool skippedToo )
00817 {
00818 if (!notify)
00819 resetList(instance,skippedToo);
00820
00821 EntryMap::Iterator it = m_mapEntries.begin();
00822 for( ; it != m_mapEntries.end(); ++it )
00823 restartEntryScan(instance, &(*it), notify);
00824
00825
00826 }
00827
00828
00829
00830 void KDirWatchPrivate::resetList( KDirWatch* ,
00831 bool skippedToo )
00832 {
00833 EntryMap::Iterator it = m_mapEntries.begin();
00834 for( ; it != m_mapEntries.end(); ++it ) {
00835
00836 Client* c = (*it).m_clients.first();
00837 for(;c;c=(*it).m_clients.next())
00838 if (!c->watchingStopped || skippedToo)
00839 c->pending = NoChange;
00840 }
00841 }
00842
00843
00844
00845 int KDirWatchPrivate::scanEntry(Entry* e)
00846 {
00847 #ifdef HAVE_FAM
00848
00849 if (e->m_mode == FAMMode) return NoChange;
00850 #endif
00851
00852
00853 if (e->m_mode == UnknownMode) return NoChange;
00854
00855 #ifdef HAVE_DNOTIFY
00856 if (e->m_mode == DNotifyMode) {
00857
00858 if(!e->dn_dirty) return NoChange;
00859 e->dn_dirty = false;
00860 }
00861 #endif
00862
00863 if (e->m_mode == StatMode) {
00864
00865
00866
00867
00868 e->msecLeft -= freq;
00869 if (e->msecLeft>0) return NoChange;
00870 e->msecLeft += e->freq;
00871 }
00872
00873 QFileInfo info(e->path);
00874 if (info.exists()) {
00875
00876 if (e->m_status == NonExistent) {
00877 e->m_ctime = info.lastModified();
00878 e->m_status = Normal;
00879 return Created;
00880 }
00881
00882 if ( e->m_ctime.isValid() &&
00883 (info.lastModified() != e->m_ctime) ) {
00884 e->m_ctime = info.lastModified();
00885 return Changed;
00886 }
00887
00888 return NoChange;
00889 }
00890
00891
00892
00893 if (!e->m_ctime.isValid())
00894 return NoChange;
00895
00896 e->m_ctime = QDateTime();
00897 e->m_status = NonExistent;
00898
00899 return Deleted;
00900 }
00901
00902
00903
00904
00905
00906 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
00907 {
00908 QString path = e->path;
00909 if (!fileName.isEmpty()) {
00910 if (fileName[0] == '/')
00911 path = fileName;
00912 else
00913 path += "/" + fileName;
00914 }
00915
00916 Client* c = e->m_clients.first();
00917 for(;c;c=e->m_clients.next()) {
00918 if (c->instance==0 || c->count==0) continue;
00919
00920 if (c->watchingStopped) {
00921
00922 if (event == Changed)
00923 c->pending |= event;
00924 else if (event == Created || event == Deleted)
00925 c->pending = event;
00926 continue;
00927 }
00928
00929 if (event == NoChange || event == Changed)
00930 event |= c->pending;
00931 c->pending = NoChange;
00932 if (event == NoChange) continue;
00933
00934 if (event & Deleted) {
00935 c->instance->setDeleted(path);
00936
00937 continue;
00938 }
00939
00940 if (event & Created) {
00941 c->instance->setCreated(path);
00942
00943 }
00944
00945 if (event & Changed)
00946 c->instance->setDirty(path);
00947 }
00948 }
00949
00950
00951 void KDirWatchPrivate::slotRemoveDelayed()
00952 {
00953 Entry* e;
00954 delayRemove = false;
00955 for(e=removeList.first();e;e=removeList.next())
00956 removeEntry(0, e->path, 0);
00957 removeList.clear();
00958 }
00959
00960
00961
00962
00963 void KDirWatchPrivate::slotRescan()
00964 {
00965 EntryMap::Iterator it;
00966
00967
00968
00969
00970 bool timerRunning = timer->isActive();
00971 if ( timerRunning )
00972 timer->stop();
00973
00974
00975
00976 delayRemove = true;
00977
00978 #ifdef HAVE_DNOTIFY
00979 QPtrList<Entry> dList, cList;
00980
00981
00982 if (rescan_all)
00983 {
00984
00985 it = m_mapEntries.begin();
00986 for( ; it != m_mapEntries.end(); ++it )
00987 (*it).dn_dirty = true;
00988 rescan_all = false;
00989 }
00990 else
00991 {
00992
00993 it = m_mapEntries.begin();
00994 for( ; it != m_mapEntries.end(); ++it )
00995 if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
00996 (*it).propagate_dirty();
00997 }
00998
00999 #endif
01000
01001 it = m_mapEntries.begin();
01002 for( ; it != m_mapEntries.end(); ++it ) {
01003
01004 if (!(*it).isValid()) continue;
01005
01006 int ev = scanEntry( &(*it) );
01007
01008 #ifdef HAVE_DNOTIFY
01009 if ((*it).m_mode == DNotifyMode) {
01010 if ((*it).isDir && (ev == Deleted)) {
01011 dList.append( &(*it) );
01012
01013
01014 if ((*it).dn_fd) {
01015 ::close((*it).dn_fd);
01016 fd_Entry.remove((*it).dn_fd);
01017 (*it).dn_fd = 0;
01018 }
01019 }
01020
01021 else if ((*it).isDir && (ev == Created)) {
01022
01023 if ( (*it).dn_fd == 0) {
01024 cList.append( &(*it) );
01025 if (! useDNotify( &(*it) )) {
01026
01027 useStat( &(*it) );
01028 }
01029 }
01030 }
01031 }
01032 #endif
01033
01034 if ( ev != NoChange )
01035 emitEvent( &(*it), ev);
01036 }
01037
01038
01039 #ifdef HAVE_DNOTIFY
01040
01041 Entry* e;
01042 for(e=dList.first();e;e=dList.next())
01043 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01044
01045
01046 for(e=cList.first();e;e=cList.next())
01047 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
01048 #endif
01049
01050 if ( timerRunning )
01051 timer->start(freq);
01052
01053 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01054 }
01055
01056 #ifdef HAVE_FAM
01057 void KDirWatchPrivate::famEventReceived()
01058 {
01059 static FAMEvent fe;
01060
01061 delayRemove = true;
01062
01063 while(use_fam && FAMPending(&fc)) {
01064 if (FAMNextEvent(&fc, &fe) == -1) {
01065 kdWarning(7001) << "FAM connection problem, switching to polling."
01066 << endl;
01067 use_fam = false;
01068 delete sn; sn = 0;
01069
01070
01071 EntryMap::Iterator it;
01072 it = m_mapEntries.begin();
01073 for( ; it != m_mapEntries.end(); ++it )
01074 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01075 #ifdef HAVE_DNOTIFY
01076 if (useDNotify( &(*it) )) continue;
01077 #endif
01078 useStat( &(*it) );
01079 }
01080 }
01081 else
01082 checkFAMEvent(&fe);
01083 }
01084
01085 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01086 }
01087
01088 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01089 {
01090
01091 if ((fe->code == FAMExists) ||
01092 (fe->code == FAMEndExist) ||
01093 (fe->code == FAMAcknowledge)) return;
01094
01095
01096 if ( *(fe->filename) == '.') {
01097 if (strncmp(fe->filename, ".X.err", 6) == 0) return;
01098 if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return;
01099 }
01100
01101 Entry* e = 0;
01102 EntryMap::Iterator it = m_mapEntries.begin();
01103 for( ; it != m_mapEntries.end(); ++it )
01104 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01105 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01106 e = &(*it);
01107 break;
01108 }
01109
01110
01111
01112 kdDebug(7001) << "Processing FAM event ("
01113 << ((fe->code == FAMChanged) ? "FAMChanged" :
01114 (fe->code == FAMDeleted) ? "FAMDeleted" :
01115 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01116 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01117 (fe->code == FAMCreated) ? "FAMCreated" :
01118 (fe->code == FAMMoved) ? "FAMMoved" :
01119 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01120 (fe->code == FAMExists) ? "FAMExists" :
01121 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01122 << ", " << fe->filename
01123 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01124 << ")" << endl;
01125
01126 if (!e) {
01127
01128
01129 return;
01130 }
01131
01132 if (e->m_status == NonExistent) {
01133 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
01134 return;
01135 }
01136
01137 if (e->isDir)
01138 switch (fe->code)
01139 {
01140 case FAMDeleted:
01141
01142 if (fe->filename[0] == '/')
01143 {
01144
01145
01146 e->m_status = NonExistent;
01147 FAMCancelMonitor(&fc, &(e->fr) );
01148 kdDebug(7001) << "Cancelled FAMReq "
01149 << FAMREQUEST_GETREQNUM(&(e->fr))
01150 << " for " << e->path << endl;
01151
01152 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01153 }
01154 emitEvent(e, Deleted, QFile::decodeName(fe->filename));
01155 break;
01156
01157 case FAMCreated: {
01158
01159 Entry *sub_entry = e->m_entries.first();
01160 for(;sub_entry; sub_entry = e->m_entries.next())
01161 if (sub_entry->path == e->path + "/" + fe->filename) break;
01162 if (sub_entry && sub_entry->isDir) {
01163 QString path = e->path;
01164 removeEntry(0,e->path,sub_entry);
01165 sub_entry->m_status = Normal;
01166 if (!useFAM(sub_entry))
01167 useStat(sub_entry);
01168
01169 emitEvent(sub_entry, Created);
01170 }
01171 else emitEvent(e, Created, QFile::decodeName(fe->filename));
01172 break;
01173 }
01174
01175 case FAMChanged:
01176 emitEvent(e, Changed, QFile::decodeName(fe->filename));
01177
01178 default:
01179 break;
01180 }
01181 else switch (fe->code)
01182 {
01183 case FAMCreated: emitEvent(e, Created);
01184 break;
01185 case FAMDeleted: emitEvent(e, Deleted);
01186 break;
01187 case FAMChanged: emitEvent(e, Changed);
01188 break;
01189 default: break;
01190 }
01191 }
01192 #else
01193 void KDirWatchPrivate::famEventReceived() {}
01194 #endif
01195
01196
01197 void KDirWatchPrivate::statistics()
01198 {
01199 EntryMap::Iterator it;
01200
01201 kdDebug(7001) << "Entries watched:" << endl;
01202 if (m_mapEntries.count()==0) {
01203 kdDebug(7001) << " None." << endl;
01204 }
01205 else {
01206 it = m_mapEntries.begin();
01207 for( ; it != m_mapEntries.end(); ++it ) {
01208 Entry* e = &(*it);
01209 kdDebug(7001) << " " << e->path << " ("
01210 << ((e->m_status==Normal)?"":"Nonexistent ")
01211 << (e->isDir ? "Dir":"File") << ", using "
01212 << ((e->m_mode == FAMMode) ? "FAM" :
01213 (e->m_mode == DNotifyMode) ? "DNotify" :
01214 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01215 << ")" << endl;
01216
01217 Client* c = e->m_clients.first();
01218 for(;c; c = e->m_clients.next()) {
01219 QString pending;
01220 if (c->watchingStopped) {
01221 if (c->pending & Deleted) pending += "deleted ";
01222 if (c->pending & Created) pending += "created ";
01223 if (c->pending & Changed) pending += "changed ";
01224 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
01225 pending = ", stopped" + pending;
01226 }
01227 kdDebug(7001) << " by " << c->instance->name()
01228 << " (" << c->count << " times)"
01229 << pending << endl;
01230 }
01231 if (e->m_entries.count()>0) {
01232 kdDebug(7001) << " dependent entries:" << endl;
01233 Entry* d = e->m_entries.first();
01234 for(;d; d = e->m_entries.next()) {
01235 kdDebug(7001) << " " << d->path << endl;
01236 }
01237 }
01238 }
01239 }
01240 }
01241
01242
01243
01244
01245
01246
01247 static KStaticDeleter<KDirWatch> sd_dw;
01248 KDirWatch* KDirWatch::s_pSelf = 0L;
01249
01250 KDirWatch* KDirWatch::self()
01251 {
01252 if ( !s_pSelf ) {
01253 sd_dw.setObject( s_pSelf, new KDirWatch );
01254 }
01255
01256 return s_pSelf;
01257 }
01258
01259 bool KDirWatch::exists()
01260 {
01261 return s_pSelf != 0;
01262 }
01263
01264 KDirWatch::KDirWatch (QObject* parent, const char* name)
01265 : QObject(parent,name)
01266 {
01267 if (!name) {
01268 static int nameCounter = 0;
01269
01270 nameCounter++;
01271 setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
01272 }
01273
01274 if (!dwp_self)
01275 dwp_self = new KDirWatchPrivate;
01276 d = dwp_self;
01277 d->ref();
01278
01279 _isStopped = false;
01280 }
01281
01282 KDirWatch::~KDirWatch()
01283 {
01284 if (d) d->removeEntries(this);
01285 if ( d->deref() )
01286 {
01287
01288 delete d;
01289 dwp_self = 0L;
01290 }
01291 }
01292
01293
01294
01295 void KDirWatch::addDir( const QString& _path,
01296 bool watchFiles, bool recursive)
01297 {
01298 if (watchFiles || recursive) {
01299 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
01300 << endl;
01301 }
01302 if (d) d->addEntry(this, _path, 0, true);
01303 }
01304
01305 void KDirWatch::addFile( const QString& _path )
01306 {
01307 if (d) d->addEntry(this, _path, 0, false);
01308 }
01309
01310 QDateTime KDirWatch::ctime( const QString &_path )
01311 {
01312 KDirWatchPrivate::Entry* e = d->entry(_path);
01313
01314 if (!e)
01315 return QDateTime();
01316 else
01317 return e->m_ctime;
01318 }
01319
01320 void KDirWatch::removeDir( const QString& _path )
01321 {
01322 if (d) d->removeEntry(this, _path, 0);
01323 }
01324
01325 void KDirWatch::removeFile( const QString& _path )
01326 {
01327 if (d) d->removeEntry(this, _path, 0);
01328 }
01329
01330 bool KDirWatch::stopDirScan( const QString& _path )
01331 {
01332 if (d) {
01333 KDirWatchPrivate::Entry *e = d->entry(_path);
01334 if (e && e->isDir) return d->stopEntryScan(this, e);
01335 }
01336 return false;
01337 }
01338
01339 bool KDirWatch::restartDirScan( const QString& _path )
01340 {
01341 if (d) {
01342 KDirWatchPrivate::Entry *e = d->entry(_path);
01343 if (e && e->isDir)
01344
01345 return d->restartEntryScan(this, e, false);
01346 }
01347 return false;
01348 }
01349
01350 void KDirWatch::stopScan()
01351 {
01352 if (d) d->stopScan(this);
01353 _isStopped = true;
01354 }
01355
01356 void KDirWatch::startScan( bool notify, bool skippedToo )
01357 {
01358 _isStopped = false;
01359 if (d) d->startScan(this, notify, skippedToo);
01360 }
01361
01362
01363 bool KDirWatch::contains( const QString& _path ) const
01364 {
01365 KDirWatchPrivate::Entry* e = d->entry(_path);
01366 if (!e)
01367 return false;
01368
01369 KDirWatchPrivate::Client* c = e->m_clients.first();
01370 for(;c;c=e->m_clients.next())
01371 if (c->instance == this) return true;
01372
01373 return false;
01374 }
01375
01376 void KDirWatch::statistics()
01377 {
01378 if (!dwp_self) {
01379 kdDebug(7001) << "KDirWatch not used" << endl;
01380 return;
01381 }
01382 dwp_self->statistics();
01383 }
01384
01385
01386 void KDirWatch::setCreated( const QString & _file )
01387 {
01388 kdDebug(7001) << name() << " emitting created " << _file << endl;
01389 emit created( _file );
01390 }
01391
01392 void KDirWatch::setDirty( const QString & _file )
01393 {
01394 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
01395 emit dirty( _file );
01396 }
01397
01398 void KDirWatch::setDeleted( const QString & _file )
01399 {
01400 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
01401 emit deleted( _file );
01402 }
01403
01404 #include "kdirwatch.moc"
01405 #include "kdirwatch_p.moc"
01406
01407
01408
01409