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