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