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