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
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #include "kdirwatch.h"
00045 #include "kdirwatch_p.h"
00046
00047 #include <config-kdirwatch.h>
00048 #include <config.h>
00049
00050 #include <sys/stat.h>
00051 #include <assert.h>
00052 #include <QtCore/QDir>
00053 #include <QtCore/QFile>
00054 #include <QtCore/QSocketNotifier>
00055 #include <QtCore/QTimer>
00056
00057 #include <kapplication.h>
00058 #include <kdebug.h>
00059 #include <kconfig.h>
00060 #include <kglobal.h>
00061 #include <kde_file.h>
00062 #include <kconfiggroup.h>
00063 #include "kmountpoint.h"
00064
00065 #include <stdlib.h>
00066
00067
00068 #include <sys/ioctl.h>
00069
00070
00071 #include <sys/utsname.h>
00072
00073 #define NO_NOTIFY (time_t) 0
00074
00075 static KDirWatchPrivate* dwp_self = 0;
00076 static KDirWatchPrivate* createPrivate() {
00077 if (!dwp_self)
00078 dwp_self = new KDirWatchPrivate;
00079 return dwp_self;
00080 }
00081
00082
00083
00084 static KDirWatchPrivate::WatchMethod methodFromString(const QString& method) {
00085 if (method == "Fam") {
00086 return KDirWatchPrivate::Fam;
00087 } else if (method == "Stat") {
00088 return KDirWatchPrivate::Stat;
00089 } else if (method == "QFSWatch") {
00090 return KDirWatchPrivate::QFSWatch;
00091 } else {
00092 #ifdef Q_OS_WIN
00093 return KDirWatchPrivate::QFSWatch;
00094 #elif defined(Q_OS_FREEBSD)
00095 return KDirWatchPrivate::Stat;
00096 #else
00097 return KDirWatchPrivate::INotify;
00098 #endif
00099 }
00100 }
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 KDirWatchPrivate::KDirWatchPrivate()
00132 : timer(),
00133 freq( 3600000 ),
00134 statEntries( 0 ),
00135 m_ref( 0 ),
00136 delayRemove( false ),
00137 rescan_all( false ),
00138 rescan_timer()
00139 {
00140 timer.setObjectName( "KDirWatchPrivate::timer" );
00141 connect (&timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00142
00143 KConfigGroup config(KGlobal::config(), "DirWatch");
00144 m_nfsPollInterval = config.readEntry("NFSPollInterval", 5000);
00145 m_PollInterval = config.readEntry("PollInterval", 500);
00146
00147 QString method = config.readEntry("PreferredMethod", "inotify");
00148 m_preferredMethod = methodFromString(method);
00149
00150
00151 m_nfsPreferredMethod = methodFromString(config.readEntry("nfsPreferredMethod", method));
00152
00153 QStringList availableMethods;
00154
00155 availableMethods << "Stat";
00156
00157
00158 rescan_timer.setObjectName( "KDirWatchPrivate::rescan_timer" );
00159 rescan_timer.setSingleShot( true );
00160 connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00161
00162 #ifdef HAVE_FAM
00163
00164 if (FAMOpen(&fc) ==0) {
00165 availableMethods << "FAM";
00166 use_fam=true;
00167 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00168 QSocketNotifier::Read, this);
00169 connect( sn, SIGNAL(activated(int)),
00170 this, SLOT(famEventReceived()) );
00171 }
00172 else {
00173 kDebug(7001) << "Can't use FAM (fam daemon not running?)";
00174 use_fam=false;
00175 }
00176 #endif
00177
00178 #ifdef HAVE_SYS_INOTIFY_H
00179 supports_inotify = true;
00180
00181 m_inotify_fd = inotify_init();
00182
00183 if ( m_inotify_fd <= 0 ) {
00184 kDebug(7001) << "Can't use Inotify, kernel doesn't support it";
00185 supports_inotify = false;
00186 }
00187
00188 {
00189 struct utsname uts;
00190 int major, minor, patch;
00191 if (uname(&uts) < 0)
00192 supports_inotify = false;
00193 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00194 supports_inotify = false;
00195 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
00196 kDebug(7001) << "Can't use INotify, Linux kernel too old";
00197 supports_inotify = false;
00198 }
00199 }
00200
00201 if ( supports_inotify ) {
00202 availableMethods << "INotify";
00203 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
00204
00205 mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
00206 connect( mSn, SIGNAL(activated( int )),
00207 this, SLOT( inotifyEventReceived() ) );
00208 }
00209 #endif
00210 #ifdef HAVE_QFILESYSTEMWATCHER
00211 availableMethods << "QFileSystemWatcher";
00212 fsWatcher = new KFileSystemWatcher();
00213 connect(fsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(fswEventReceived(QString)));
00214 connect(fsWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fswEventReceived(QString)));
00215 #endif
00216 kDebug(7001) << "Available methods: " << availableMethods;
00217 }
00218
00219
00220 KDirWatchPrivate::~KDirWatchPrivate()
00221 {
00222 timer.stop();
00223
00224
00225 removeEntries(0);
00226
00227 #ifdef HAVE_FAM
00228 if (use_fam) {
00229 FAMClose(&fc);
00230 }
00231 #endif
00232 #ifdef HAVE_SYS_INOTIFY_H
00233 if ( supports_inotify )
00234 ::close( m_inotify_fd );
00235 #endif
00236 #ifdef HAVE_QFILESYSTEMWATCHER
00237 delete fsWatcher;
00238 #endif
00239 }
00240
00241 void KDirWatchPrivate::inotifyEventReceived()
00242 {
00243
00244 #ifdef HAVE_SYS_INOTIFY_H
00245 if ( !supports_inotify )
00246 return;
00247
00248 int pending = -1;
00249 int offset = 0;
00250 char buf[4096];
00251 assert( m_inotify_fd > -1 );
00252 ioctl( m_inotify_fd, FIONREAD, &pending );
00253
00254 while ( pending > 0 ) {
00255
00256 if ( pending > (int)sizeof( buf ) )
00257 pending = sizeof( buf );
00258
00259 pending = read( m_inotify_fd, buf, pending);
00260
00261 while ( pending > 0 ) {
00262 struct inotify_event *event = (struct inotify_event *) &buf[offset];
00263 pending -= sizeof( struct inotify_event ) + event->len;
00264 offset += sizeof( struct inotify_event ) + event->len;
00265
00266 QString path;
00267 QByteArray cpath(event->name, event->len);
00268 if(event->len)
00269 path = QFile::decodeName ( cpath );
00270
00271 if ( path.length() && isNoisyFile( cpath ) )
00272 continue;
00273
00274
00275
00276
00277 for ( EntryMap::Iterator it = m_mapEntries.begin();
00278 it != m_mapEntries.end(); ) {
00279 Entry* e = &( *it );
00280 ++it;
00281 if ( e->wd == event->wd ) {
00282 e->dirty = true;
00283
00284 if( event->mask & IN_DELETE_SELF) {
00285 kDebug(7001) << "-->got deleteself signal for" << e->path;
00286 e->m_status = NonExistent;
00287 if (e->isDir)
00288 addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00289 else
00290 addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
00291 }
00292 if ( event->mask & IN_IGNORED ) {
00293
00294
00295 }
00296 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
00297 Entry* sub_entry = e->findSubEntry(e->path + '/' + path);
00298
00299 if (sub_entry ) {
00300 removeEntry(0, e, sub_entry);
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 if(!useINotify(sub_entry))
00311 useStat(sub_entry);
00312 sub_entry->dirty = true;
00313 }
00314 else if ((e->isDir) && (!e->m_clients.empty())) {
00315
00316 const QString tpath = e->path + QLatin1Char('/') + path;
00317 bool isDir = false;
00318 const QList<Client *> clients = e->clientsForFileOrDir(tpath, &isDir);
00319 Q_FOREACH(Client *client, clients) {
00320
00321
00322 if (isDir) {
00323 addEntry(client->instance, tpath, 0, isDir,
00324 isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
00325 }
00326 }
00327 if (!clients.isEmpty()) {
00328 emitEvent(e, Created, e->path+'/'+path);
00329 kDebug(7001).nospace() << clients.count() << " instance(s) monitoring the new "
00330 << (isDir ? "dir " : "file ") << tpath;
00331 }
00332 }
00333 }
00334 if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
00335 if ((e->isDir) && (!e->m_clients.empty())) {
00336 Client* client = 0;
00337
00338
00339
00340
00341 KDE_struct_stat stat_buf;
00342 QString tpath = e->path + QLatin1Char('/') + path;
00343
00344
00345 KDirWatch::WatchModes flag = KDirWatch::WatchSubDirs | KDirWatch::WatchFiles;
00346 if (KDE::stat(tpath, &stat_buf) == 0) {
00347 bool isDir = S_ISDIR(stat_buf.st_mode);
00348 flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
00349 }
00350 int counter = 0;
00351 Q_FOREACH(client, e->m_clients) {
00352 if (client->m_watchModes & flag) {
00353 counter++;
00354 }
00355 }
00356 if (counter != 0) {
00357 emitEvent (e, Deleted, e->path+'/'+path);
00358 }
00359 }
00360 }
00361 if (event->mask & (IN_MODIFY|IN_ATTRIB)) {
00362 if ((e->isDir) && (!e->m_clients.empty())) {
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378 e->m_pendingFileChanges.append(e->path+'/'+path);
00379 }
00380 }
00381
00382 if (!rescan_timer.isActive())
00383 rescan_timer.start(m_PollInterval);
00384
00385 break;
00386 }
00387 }
00388 }
00389 }
00390 #endif
00391 }
00392
00393
00394
00395
00396
00397 void KDirWatchPrivate::Entry::propagate_dirty()
00398 {
00399 foreach(Entry *sub_entry, m_entries)
00400 {
00401 if (!sub_entry->dirty)
00402 {
00403 sub_entry->dirty = true;
00404 sub_entry->propagate_dirty();
00405 }
00406 }
00407 }
00408
00409
00410
00411
00412
00413 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance,
00414 KDirWatch::WatchModes watchModes)
00415 {
00416 if (instance == 0)
00417 return;
00418
00419 foreach(Client* client, m_clients) {
00420 if (client->instance == instance) {
00421 client->count++;
00422 client->m_watchModes = watchModes;
00423 return;
00424 }
00425 }
00426
00427 Client* client = new Client;
00428 client->instance = instance;
00429 client->count = 1;
00430 client->watchingStopped = instance->isStopped();
00431 client->pending = NoChange;
00432 client->m_watchModes = watchModes;
00433
00434 m_clients.append(client);
00435 }
00436
00437 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00438 {
00439 QList<Client *>::iterator it = m_clients.begin();
00440 const QList<Client *>::iterator end = m_clients.end();
00441 for ( ; it != end ; ++it ) {
00442 Client* client = *it;
00443 if (client->instance == instance) {
00444 client->count--;
00445 if (client->count == 0) {
00446 m_clients.erase(it);
00447 delete client;
00448 }
00449 return;
00450 }
00451 }
00452 }
00453
00454
00455 int KDirWatchPrivate::Entry::clients()
00456 {
00457 int clients = 0;
00458 foreach(Client* client, m_clients)
00459 clients += client->count;
00460
00461 return clients;
00462 }
00463
00464 QList<KDirWatchPrivate::Client *> KDirWatchPrivate::Entry::clientsForFileOrDir(const QString& tpath, bool* isDir) const
00465 {
00466 QList<Client *> ret;
00467 KDE_struct_stat stat_buf;
00468 if (KDE::stat(tpath, &stat_buf) == 0) {
00469 *isDir = S_ISDIR(stat_buf.st_mode);
00470 const KDirWatch::WatchModes flag =
00471 *isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
00472 Q_FOREACH(Client *client, this->m_clients) {
00473 if (client->m_watchModes & flag) {
00474 ret.append(client);
00475 }
00476 }
00477 } else {
00478
00479
00480 }
00481
00482
00483 return ret;
00484 }
00485
00486 QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
00487 {
00488 debug.space() << entry.path << (entry.isDir ? "dir" : "file");
00489 if (entry.m_status == KDirWatchPrivate::NonExistent)
00490 debug << "NonExistent";
00491 debug << "mode:" << entry.m_mode
00492 << entry.m_clients.count() << "clients";
00493 if (!entry.m_entries.isEmpty())
00494 debug << entry.m_entries.count() << "nonexistent entries";
00495 return debug;
00496 }
00497
00498 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00499 {
00500
00501 if (_path.isEmpty() || QDir::isRelativePath(_path)) {
00502 return 0;
00503 }
00504
00505 QString path (_path);
00506
00507 if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
00508 path.truncate( path.length() - 1 );
00509
00510 EntryMap::Iterator it = m_mapEntries.find( path );
00511 if ( it == m_mapEntries.end() )
00512 return 0;
00513 else
00514 return &(*it);
00515 }
00516
00517
00518 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00519 {
00520 e->freq = newFreq;
00521
00522
00523 if (e->freq < freq) {
00524 freq = e->freq;
00525 if (timer.isActive()) timer.start(freq);
00526 kDebug(7001) << "Global Poll Freq is now" << freq << "msec";
00527 }
00528 }
00529
00530
00531 #if defined(HAVE_FAM)
00532
00533 bool KDirWatchPrivate::useFAM(Entry* e)
00534 {
00535 if (!use_fam) return false;
00536
00537
00538
00539 famEventReceived();
00540
00541 e->m_mode = FAMMode;
00542 e->dirty = false;
00543
00544 if (e->isDir) {
00545 if (e->m_status == NonExistent) {
00546
00547 addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00548 }
00549 else {
00550 int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00551 &(e->fr), e);
00552 if (res<0) {
00553 e->m_mode = UnknownMode;
00554 use_fam=false;
00555 delete sn; sn = 0;
00556 return false;
00557 }
00558 kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00559 << ") for " << e->path;
00560 }
00561 }
00562 else {
00563 if (e->m_status == NonExistent) {
00564
00565 addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
00566 }
00567 else {
00568 int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00569 &(e->fr), e);
00570 if (res<0) {
00571 e->m_mode = UnknownMode;
00572 use_fam=false;
00573 delete sn; sn = 0;
00574 return false;
00575 }
00576
00577 removeEntry(0, QDir::cleanPath(e->path + "/.."), e);
00578
00579 kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00580 << ") for " << e->path;
00581 }
00582 }
00583
00584
00585
00586 famEventReceived();
00587
00588 return true;
00589 }
00590 #endif
00591
00592 #ifdef HAVE_SYS_INOTIFY_H
00593
00594 bool KDirWatchPrivate::useINotify( Entry* e )
00595 {
00596
00597
00598 e->wd = 0;
00599 e->dirty = false;
00600
00601 if (!supports_inotify) return false;
00602
00603 e->m_mode = INotifyMode;
00604
00605 if ( e->m_status == NonExistent ) {
00606 addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00607 return true;
00608 }
00609
00610
00611 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW|IN_MOVED_FROM|IN_MODIFY|IN_ATTRIB;
00612
00613 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
00614 QFile::encodeName( e->path ), mask) ) > 0)
00615 {
00616
00617 return true;
00618 }
00619
00620 return false;
00621 }
00622 #endif
00623 #ifdef HAVE_QFILESYSTEMWATCHER
00624 bool KDirWatchPrivate::useQFSWatch(Entry* e)
00625 {
00626 e->m_mode = QFSWatchMode;
00627 e->dirty = false;
00628
00629 if ( e->m_status == NonExistent ) {
00630 addEntry( 0, QDir::cleanPath( e->path + "/.." ), e, true );
00631 return true;
00632 }
00633
00634 fsWatcher->addPath( e->path );
00635 return true;
00636 }
00637 #endif
00638
00639 bool KDirWatchPrivate::useStat(Entry* e)
00640 {
00641 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(e->path);
00642 const bool slow = mp ? mp->probablySlow() : false;
00643 if (slow)
00644 useFreq(e, m_nfsPollInterval);
00645 else
00646 useFreq(e, m_PollInterval);
00647
00648 if (e->m_mode != StatMode) {
00649 e->m_mode = StatMode;
00650 statEntries++;
00651
00652 if ( statEntries == 1 ) {
00653
00654 timer.start(freq);
00655 kDebug(7001) << " Started Polling Timer, freq " << freq;
00656 }
00657 }
00658
00659 kDebug(7001) << " Setup Stat (freq " << e->freq << ") for " << e->path;
00660
00661 return true;
00662 }
00663
00664
00665
00666
00667
00668
00669
00670 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00671 Entry* sub_entry, bool isDir, KDirWatch::WatchModes watchModes)
00672 {
00673 QString path (_path);
00674 if (path.isEmpty()
00675 #ifndef Q_WS_WIN
00676 || path.startsWith("/dev/") || (path == "/dev")
00677 #endif
00678 )
00679 return;
00680
00681 if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
00682 path.truncate( path.length() - 1 );
00683
00684 EntryMap::Iterator it = m_mapEntries.find( path );
00685 if ( it != m_mapEntries.end() )
00686 {
00687 if (sub_entry) {
00688 (*it).m_entries.append(sub_entry);
00689 kDebug(7001) << "Added already watched Entry" << path
00690 << "(for" << sub_entry->path << ")";
00691 #ifdef HAVE_SYS_INOTIFY_H
00692 Entry* e = &(*it);
00693 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
00694 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
00695 if(!e->isDir)
00696 mask |= IN_MODIFY|IN_ATTRIB;
00697 else
00698 mask |= IN_ONLYDIR;
00699
00700 inotify_rm_watch (m_inotify_fd, e->wd);
00701 e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ),
00702 mask);
00703 }
00704 #endif
00705 }
00706 else {
00707 (*it).addClient(instance, watchModes);
00708 kDebug(7001) << "Added already watched Entry" << path
00709 << "(now" << (*it).clients() << "clients)"
00710 << QString("[%1]").arg(instance->objectName());
00711 }
00712 return;
00713 }
00714
00715
00716
00717 KDE_struct_stat stat_buf;
00718 bool exists = (KDE::stat(path, &stat_buf) == 0);
00719
00720 EntryMap::iterator newIt = m_mapEntries.insert( path, Entry() );
00721
00722 Entry* e = &(*newIt);
00723
00724 if (exists) {
00725 e->isDir = S_ISDIR(stat_buf.st_mode);
00726
00727 if (e->isDir && !isDir) {
00728 KDE::lstat(path, &stat_buf);
00729 if (S_ISLNK(stat_buf.st_mode))
00730
00731 e->isDir = false;
00732 else
00733 qWarning() << "KDirWatch:" << path << "is a directory. Use addDir!";
00734 } else if (!e->isDir && isDir)
00735 qWarning("KDirWatch: %s is a file. Use addFile!", qPrintable(path));
00736
00737 if (!e->isDir && ( watchModes != KDirWatch::WatchDirOnly)) {
00738 qWarning() << "KDirWatch:" << path << "is a file. You can't use recursive or "
00739 "watchFiles options";
00740 watchModes = KDirWatch::WatchDirOnly;
00741 }
00742
00743 #ifdef Q_OS_WIN
00744
00745 e->m_ctime = stat_buf.st_mtime;
00746 #else
00747 e->m_ctime = stat_buf.st_ctime;
00748 #endif
00749 e->m_status = Normal;
00750 e->m_nlink = stat_buf.st_nlink;
00751 }
00752 else {
00753 e->isDir = isDir;
00754 e->m_ctime = invalid_ctime;
00755 e->m_status = NonExistent;
00756 e->m_nlink = 0;
00757 }
00758
00759 e->path = path;
00760 if (sub_entry)
00761 e->m_entries.append(sub_entry);
00762 else
00763 e->addClient(instance, watchModes);
00764
00765 kDebug(7001).nospace() << "Added " << (e->isDir ? "Dir " : "File ") << path
00766 << (e->m_status == NonExistent ? " NotExisting" : "")
00767 << " for " << (sub_entry ? sub_entry->path : "")
00768 << " [" << (instance ? instance->objectName() : "") << "]";
00769
00770
00771 e->m_mode = UnknownMode;
00772 e->msecLeft = 0;
00773
00774 if ( isNoisyFile( QFile::encodeName( path ) ) )
00775 return;
00776
00777 if (exists && e->isDir && (watchModes != KDirWatch::WatchDirOnly)) {
00778 QFlags<QDir::Filter> filters = QDir::NoDotAndDotDot;
00779
00780 if ((watchModes & KDirWatch::WatchSubDirs) &&
00781 (watchModes & KDirWatch::WatchFiles)) {
00782 filters |= (QDir::Dirs|QDir::Files);
00783 } else if (watchModes & KDirWatch::WatchSubDirs) {
00784 filters |= QDir::Dirs;
00785 } else if (watchModes & KDirWatch::WatchFiles) {
00786 filters |= QDir::Files;
00787 }
00788
00789 #if defined(HAVE_SYS_INOTIFY_H)
00790 if (e->m_mode == INotifyMode || (e->m_mode == UnknownMode && m_preferredMethod == INotify) )
00791 {
00792 kDebug (7001) << "Ignoring WatchFiles directive - this is implicit with inotify";
00793
00794
00795
00796 filters &= ~QDir::Files;
00797 }
00798 #endif
00799
00800 QDir basedir (e->path);
00801 const QFileInfoList contents = basedir.entryInfoList(filters);
00802 for (QFileInfoList::const_iterator iter = contents.constBegin();
00803 iter != contents.constEnd(); ++iter)
00804 {
00805 const QFileInfo &fileInfo = *iter;
00806
00807 bool isDir = fileInfo.isDir() && !fileInfo.isSymLink();
00808
00809 addEntry (instance, fileInfo.absoluteFilePath(), 0, isDir,
00810 isDir ? watchModes : KDirWatch::WatchDirOnly);
00811 }
00812 }
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822 WatchMethod preferredMethod = m_preferredMethod;
00823 if (m_nfsPreferredMethod != m_preferredMethod) {
00824 KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(e->path);
00825 if (mountPoint && mountPoint->probablySlow()) {
00826 preferredMethod = m_nfsPreferredMethod;
00827 }
00828 }
00829
00830
00831 bool entryAdded = false;
00832 switch (preferredMethod) {
00833 #if defined(HAVE_FAM)
00834 case Fam: entryAdded = useFAM(e); break;
00835 #endif
00836 #if defined(HAVE_SYS_INOTIFY_H)
00837 case INotify: entryAdded = useINotify(e); break;
00838 #endif
00839 #if defined(HAVE_QFILESYSTEMWATCHER)
00840 case QFSWatch: entryAdded = useQFSWatch(e); break;
00841 #endif
00842 case Stat: entryAdded = useStat(e); break;
00843 default: break;
00844 }
00845
00846
00847 if (!entryAdded) {
00848 #if defined(HAVE_SYS_INOTIFY_H)
00849 if (useINotify(e)) return;
00850 #endif
00851 #if defined(HAVE_FAM)
00852 if (useFAM(e)) return;
00853 #endif
00854 #if defined(HAVE_QFILESYSTEMWATCHER)
00855 if (useQFSWatch(e)) return;
00856 #endif
00857 useStat(e);
00858 }
00859 }
00860
00861
00862 void KDirWatchPrivate::removeEntry(KDirWatch* instance,
00863 const QString& _path,
00864 Entry* sub_entry)
00865 {
00866
00867 Entry* e = entry(_path);
00868 if (!e) {
00869 kWarning(7001) << "doesn't know" << _path;
00870 return;
00871 }
00872
00873 removeEntry(instance, e, sub_entry);
00874 }
00875
00876 void KDirWatchPrivate::removeEntry(KDirWatch* instance,
00877 Entry* e,
00878 Entry* sub_entry)
00879 {
00880 removeList.remove(e);
00881
00882 if (sub_entry)
00883 e->m_entries.removeAll(sub_entry);
00884 else
00885 e->removeClient(instance);
00886
00887 if (e->m_clients.count() || e->m_entries.count())
00888 return;
00889
00890 if (delayRemove) {
00891 removeList.insert(e);
00892
00893 return;
00894 }
00895
00896 #ifdef HAVE_FAM
00897 if (e->m_mode == FAMMode) {
00898 if ( e->m_status == Normal) {
00899 FAMCancelMonitor(&fc, &(e->fr) );
00900 kDebug(7001).nospace() << "Cancelled FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00901 << ") for " << e->path;
00902 }
00903 else {
00904 if (e->isDir)
00905 removeEntry(0, QDir::cleanPath(e->path+"/.."), e);
00906 else
00907 removeEntry(0, QFileInfo(e->path).absolutePath(), e);
00908 }
00909 }
00910 #endif
00911
00912 #ifdef HAVE_SYS_INOTIFY_H
00913 if (e->m_mode == INotifyMode) {
00914 if ( e->m_status == Normal ) {
00915 (void) inotify_rm_watch( m_inotify_fd, e->wd );
00916 kDebug(7001).nospace() << "Cancelled INotify (fd " << m_inotify_fd << ", "
00917 << e->wd << ") for " << e->path;
00918 }
00919 else {
00920 if (e->isDir)
00921 removeEntry(0, QDir::cleanPath(e->path+"/.."), e);
00922 else
00923 removeEntry(0, QFileInfo(e->path).absolutePath(), e);
00924 }
00925 }
00926 #endif
00927
00928 #ifdef HAVE_QFILESYSTEMWATCHER
00929 if (e->m_mode == QFSWatchMode) {
00930 fsWatcher->removePath(e->path);
00931 }
00932 #endif
00933 if (e->m_mode == StatMode) {
00934 statEntries--;
00935 if ( statEntries == 0 ) {
00936 timer.stop();
00937 kDebug(7001) << " Stopped Polling Timer";
00938 }
00939 }
00940
00941
00942
00943
00944 m_mapEntries.remove( e->path );
00945 }
00946
00947
00948
00949
00950
00951 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00952 {
00953 int minfreq = 3600000;
00954
00955 QStringList pathList;
00956
00957 EntryMap::Iterator it = m_mapEntries.begin();
00958 for( ; it != m_mapEntries.end(); ++it ) {
00959 Client* c = 0;
00960 foreach(Client* client, (*it).m_clients) {
00961 if (client->instance == instance) {
00962 c = client;
00963 break;
00964 }
00965 }
00966 if (c) {
00967 c->count = 1;
00968 pathList.append((*it).path);
00969 }
00970 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00971 minfreq = (*it).freq;
00972 }
00973
00974 foreach(const QString &path, pathList)
00975 removeEntry(instance, path, 0);
00976
00977 if (minfreq > freq) {
00978
00979 freq = minfreq;
00980 if (timer.isActive()) timer.start(freq);
00981 kDebug(7001) << "Poll Freq now" << freq << "msec";
00982 }
00983 }
00984
00985
00986 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00987 {
00988 int stillWatching = 0;
00989 foreach(Client* client, e->m_clients) {
00990 if (!instance || instance == client->instance)
00991 client->watchingStopped = true;
00992 else if (!client->watchingStopped)
00993 stillWatching += client->count;
00994 }
00995
00996 kDebug(7001) << (instance ? instance->objectName() : "all")
00997 << "stopped scanning" << e->path << "(now"
00998 << stillWatching << "watchers)";
00999
01000 if (stillWatching == 0) {
01001
01002 if ( e->m_mode != INotifyMode ) {
01003 e->m_ctime = invalid_ctime;
01004 e->m_status = NonExistent;
01005 }
01006
01007 }
01008 return true;
01009 }
01010
01011
01012 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
01013 bool notify)
01014 {
01015 int wasWatching = 0, newWatching = 0;
01016 foreach(Client* client, e->m_clients) {
01017 if (!client->watchingStopped)
01018 wasWatching += client->count;
01019 else if (!instance || instance == client->instance) {
01020 client->watchingStopped = false;
01021 newWatching += client->count;
01022 }
01023 }
01024 if (newWatching == 0)
01025 return false;
01026
01027 kDebug(7001) << (instance ? instance->objectName() : "all")
01028 << "restarted scanning" << e->path
01029 << "(now" << wasWatching+newWatching << "watchers)";
01030
01031
01032
01033 int ev = NoChange;
01034 if (wasWatching == 0) {
01035 if (!notify) {
01036 KDE_struct_stat stat_buf;
01037 bool exists = (KDE::stat(e->path, &stat_buf) == 0);
01038 if (exists) {
01039 #ifdef Q_OS_WIN
01040
01041 e->m_ctime = stat_buf.st_mtime;
01042 #else
01043 e->m_ctime = stat_buf.st_ctime;
01044 #endif
01045 e->m_status = Normal;
01046 e->m_nlink = stat_buf.st_nlink;
01047 }
01048 else {
01049 e->m_ctime = invalid_ctime;
01050 e->m_status = NonExistent;
01051 e->m_nlink = 0;
01052 }
01053 }
01054 e->msecLeft = 0;
01055 ev = scanEntry(e);
01056 }
01057 emitEvent(e,ev);
01058
01059 return true;
01060 }
01061
01062
01063 void KDirWatchPrivate::stopScan(KDirWatch* instance)
01064 {
01065 EntryMap::Iterator it = m_mapEntries.begin();
01066 for( ; it != m_mapEntries.end(); ++it )
01067 stopEntryScan(instance, &(*it));
01068 }
01069
01070
01071 void KDirWatchPrivate::startScan(KDirWatch* instance,
01072 bool notify, bool skippedToo )
01073 {
01074 if (!notify)
01075 resetList(instance,skippedToo);
01076
01077 EntryMap::Iterator it = m_mapEntries.begin();
01078 for( ; it != m_mapEntries.end(); ++it )
01079 restartEntryScan(instance, &(*it), notify);
01080
01081
01082 }
01083
01084
01085
01086 void KDirWatchPrivate::resetList( KDirWatch* , bool skippedToo )
01087 {
01088 EntryMap::Iterator it = m_mapEntries.begin();
01089 for( ; it != m_mapEntries.end(); ++it ) {
01090
01091 foreach(Client* client, (*it).m_clients) {
01092 if (!client->watchingStopped || skippedToo)
01093 client->pending = NoChange;
01094 }
01095 }
01096 }
01097
01098
01099
01100 int KDirWatchPrivate::scanEntry(Entry* e)
01101 {
01102 #ifdef HAVE_FAM
01103 if (e->m_mode == FAMMode) {
01104
01105 if(!e->dirty) return NoChange;
01106 e->dirty = false;
01107 }
01108 #endif
01109
01110
01111 if (e->m_mode == UnknownMode) return NoChange;
01112
01113 #if defined( HAVE_SYS_INOTIFY_H )
01114 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
01115
01116 if(!e->dirty) return NoChange;
01117 e->dirty = false;
01118 }
01119 #endif
01120
01121 #if defined( HAVE_QFILESYSTEMWATCHER )
01122 if (e->m_mode == QFSWatchMode ) {
01123
01124 if(!e->dirty) return NoChange;
01125 e->dirty = false;
01126 }
01127 #endif
01128
01129 if (e->m_mode == StatMode) {
01130
01131
01132
01133
01134 e->msecLeft -= freq;
01135 if (e->msecLeft>0) return NoChange;
01136 e->msecLeft += e->freq;
01137 }
01138
01139 KDE_struct_stat stat_buf;
01140 bool exists = (KDE::stat(e->path, &stat_buf) == 0);
01141 if (exists) {
01142
01143 if (e->m_status == NonExistent) {
01144 #ifdef Q_OS_WIN
01145
01146 e->m_ctime = stat_buf.st_mtime;
01147 #else
01148 e->m_ctime = stat_buf.st_ctime;
01149 #endif
01150 e->m_status = Normal;
01151 e->m_nlink = stat_buf.st_nlink;
01152 return Created;
01153 }
01154
01155 #ifdef Q_OS_WIN
01156 stat_buf.st_ctime = stat_buf.st_mtime;
01157 #endif
01158 if ( (e->m_ctime != invalid_ctime) &&
01159 ((stat_buf.st_ctime != e->m_ctime) ||
01160 (stat_buf.st_nlink != (nlink_t) e->m_nlink))
01161 #if defined( HAVE_QFILESYSTEMWATCHER )
01162
01163
01164
01165 ||(e->m_mode == QFSWatchMode )
01166 #endif
01167 ) {
01168 e->m_ctime = stat_buf.st_ctime;
01169 e->m_nlink = stat_buf.st_nlink;
01170 return Changed;
01171 }
01172
01173 return NoChange;
01174 }
01175
01176
01177
01178 if (e->m_ctime == invalid_ctime) {
01179 e->m_nlink = 0;
01180 e->m_status = NonExistent;
01181 return NoChange;
01182 }
01183
01184 e->m_ctime = invalid_ctime;
01185 e->m_nlink = 0;
01186 e->m_status = NonExistent;
01187
01188 return Deleted;
01189 }
01190
01191
01192
01193
01194
01195 void KDirWatchPrivate::emitEvent(const Entry* e, int event, const QString &fileName)
01196 {
01197 QString path (e->path);
01198 if (!fileName.isEmpty()) {
01199 if (!QDir::isRelativePath(fileName))
01200 path = fileName;
01201 else
01202 #ifdef Q_OS_UNIX
01203 path += '/' + fileName;
01204 #elif defined(Q_WS_WIN)
01205
01206 path += QDir::currentPath().left(2) + '/' + fileName;
01207 #endif
01208 }
01209
01210 foreach(Client* c, e->m_clients)
01211 {
01212 if (c->instance==0 || c->count==0) continue;
01213
01214 if (c->watchingStopped) {
01215
01216 if (event == Changed)
01217 c->pending |= event;
01218 else if (event == Created || event == Deleted)
01219 c->pending = event;
01220 continue;
01221 }
01222
01223 if (event == NoChange || event == Changed)
01224 event |= c->pending;
01225 c->pending = NoChange;
01226 if (event == NoChange) continue;
01227
01228
01229
01230 if (event & Deleted) {
01231 QMetaObject::invokeMethod(c->instance, "setDeleted", Qt::QueuedConnection, Q_ARG(QString, path));
01232
01233 continue;
01234 }
01235
01236 if (event & Created) {
01237 QMetaObject::invokeMethod(c->instance, "setCreated", Qt::QueuedConnection, Q_ARG(QString, path));
01238
01239 }
01240
01241 if (event & Changed) {
01242 QMetaObject::invokeMethod(c->instance, "setDirty", Qt::QueuedConnection, Q_ARG(QString, path));
01243 }
01244 }
01245 }
01246
01247
01248 void KDirWatchPrivate::slotRemoveDelayed()
01249 {
01250 delayRemove = false;
01251
01252
01253
01254 while (!removeList.isEmpty()) {
01255 Entry* entry = *removeList.begin();
01256 removeEntry(0, entry, 0);
01257 }
01258 }
01259
01260
01261
01262
01263 void KDirWatchPrivate::slotRescan()
01264 {
01265 EntryMap::Iterator it;
01266
01267
01268
01269
01270 bool timerRunning = timer.isActive();
01271 if ( timerRunning )
01272 timer.stop();
01273
01274
01275
01276 delayRemove = true;
01277
01278 if (rescan_all)
01279 {
01280
01281 it = m_mapEntries.begin();
01282 for( ; it != m_mapEntries.end(); ++it )
01283 (*it).dirty = true;
01284 rescan_all = false;
01285 }
01286 else
01287 {
01288
01289 it = m_mapEntries.begin();
01290 for( ; it != m_mapEntries.end(); ++it )
01291 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
01292 (*it).propagate_dirty();
01293 }
01294
01295 #ifdef HAVE_SYS_INOTIFY_H
01296 QList<Entry*> dList, cList;
01297 #endif
01298
01299 it = m_mapEntries.begin();
01300 for( ; it != m_mapEntries.end(); ++it ) {
01301
01302 if (!(*it).isValid()) continue;
01303
01304 int ev = scanEntry( &(*it) );
01305
01306 #ifdef HAVE_SYS_INOTIFY_H
01307 if ((*it).m_mode == INotifyMode) {
01308 if ( ev == Deleted ) {
01309 addEntry(0, QDir::cleanPath( ( *it ).path+"/.."), &*it, true);
01310 }
01311 }
01312 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
01313 cList.append( &(*it) );
01314 if (! useINotify( &(*it) )) {
01315 useStat( &(*it) );
01316 }
01317 }
01318
01319 if ((*it).isDir)
01320 {
01321
01322
01323
01324
01325 QList<QString> pendingFileChanges = (*it).m_pendingFileChanges.toSet().toList();
01326 Q_FOREACH(QString changedFilename, pendingFileChanges )
01327 {
01328 emitEvent(&(*it), Changed, changedFilename);
01329 }
01330 (*it).m_pendingFileChanges.clear();
01331 }
01332 #endif
01333
01334 if ( ev != NoChange )
01335 emitEvent( &(*it), ev);
01336 }
01337
01338 if ( timerRunning )
01339 timer.start(freq);
01340
01341 #ifdef HAVE_SYS_INOTIFY_H
01342
01343 Q_FOREACH(Entry* e, cList)
01344 removeEntry(0, QDir::cleanPath( e->path+"/.."), e);
01345 #endif
01346
01347 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01348 }
01349
01350 bool KDirWatchPrivate::isNoisyFile( const char * filename )
01351 {
01352
01353 if ( *filename == '.') {
01354 if (strncmp(filename, ".X.err", 6) == 0) return true;
01355 if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
01356
01357
01358 if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
01359 }
01360
01361 return false;
01362 }
01363
01364 #ifdef HAVE_FAM
01365 void KDirWatchPrivate::famEventReceived()
01366 {
01367 static FAMEvent fe;
01368
01369 delayRemove = true;
01370
01371
01372
01373 while(use_fam && FAMPending(&fc)) {
01374 if (FAMNextEvent(&fc, &fe) == -1) {
01375 kWarning(7001) << "FAM connection problem, switching to polling.";
01376 use_fam = false;
01377 delete sn; sn = 0;
01378
01379
01380 EntryMap::Iterator it;
01381 it = m_mapEntries.begin();
01382 for( ; it != m_mapEntries.end(); ++it )
01383 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01384 #ifdef HAVE_SYS_INOTIFY_H
01385 if (useINotify( &(*it) )) continue;
01386 #endif
01387 useStat( &(*it) );
01388 }
01389 }
01390 else
01391 checkFAMEvent(&fe);
01392 }
01393
01394 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01395 }
01396
01397 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01398 {
01399
01400
01401
01402 if ((fe->code == FAMExists) ||
01403 (fe->code == FAMEndExist) ||
01404 (fe->code == FAMAcknowledge)) return;
01405
01406 if ( isNoisyFile( fe->filename ) )
01407 return;
01408
01409 Entry* e = 0;
01410 EntryMap::Iterator it = m_mapEntries.begin();
01411 for( ; it != m_mapEntries.end(); ++it )
01412 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01413 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01414 e = &(*it);
01415 break;
01416 }
01417
01418
01419
01420 #if 0 // #88538
01421 kDebug(7001) << "Processing FAM event ("
01422 << ((fe->code == FAMChanged) ? "FAMChanged" :
01423 (fe->code == FAMDeleted) ? "FAMDeleted" :
01424 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01425 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01426 (fe->code == FAMCreated) ? "FAMCreated" :
01427 (fe->code == FAMMoved) ? "FAMMoved" :
01428 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01429 (fe->code == FAMExists) ? "FAMExists" :
01430 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01431 << ", " << fe->filename
01432 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) << ")";
01433 #endif
01434
01435 if (!e) {
01436
01437
01438 return;
01439 }
01440
01441 if (e->m_status == NonExistent) {
01442 kDebug(7001) << "FAM event for nonExistent entry " << e->path;
01443 return;
01444 }
01445
01446
01447 e->dirty = true;
01448 if (!rescan_timer.isActive())
01449 rescan_timer.start(m_PollInterval);
01450
01451
01452 if (e->isDir)
01453 switch (fe->code)
01454 {
01455 case FAMDeleted:
01456
01457 if (!QDir::isRelativePath(fe->filename))
01458 {
01459
01460
01461 e->m_status = NonExistent;
01462 FAMCancelMonitor(&fc, &(e->fr) );
01463 kDebug(7001) << "Cancelled FAMReq"
01464 << FAMREQUEST_GETREQNUM(&(e->fr))
01465 << "for" << e->path;
01466
01467 addEntry(0, QDir::cleanPath( e->path+"/.."), e, true);
01468 }
01469 break;
01470
01471 case FAMCreated: {
01472
01473 QString tpath(e->path + QLatin1Char('/') + fe->filename);
01474
01475 Entry* sub_entry = e->findSubEntry(tpath);
01476 if (sub_entry && sub_entry->isDir) {
01477 removeEntry(0, e, sub_entry);
01478 sub_entry->m_status = Normal;
01479 if (!useFAM(sub_entry)) {
01480 #ifdef HAVE_SYS_INOTIFY_H
01481 if (!useINotify(sub_entry ))
01482 #endif
01483 useStat(sub_entry);
01484 }
01485 }
01486 else if ((sub_entry == 0) && (!e->m_clients.empty())) {
01487 bool isDir = false;
01488 const QList<Client *> clients = e->clientsForFileOrDir(tpath, &isDir);
01489 Q_FOREACH(Client *client, clients) {
01490 addEntry (client->instance, tpath, 0, isDir,
01491 isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
01492 }
01493
01494 if (!clients.isEmpty()) {
01495 emitEvent(e, Created, tpath);
01496
01497 QString msg (QString::number(clients.count()));
01498 msg += " instance/s monitoring the new ";
01499 msg += (isDir ? "dir " : "file ") + tpath;
01500 kDebug(7001) << msg;
01501 }
01502 }
01503 }
01504 break;
01505 default:
01506 break;
01507 }
01508 }
01509 #else
01510 void KDirWatchPrivate::famEventReceived()
01511 {
01512 kWarning (7001) << "Fam event received but FAM is not supported";
01513 }
01514 #endif
01515
01516
01517 void KDirWatchPrivate::statistics()
01518 {
01519 EntryMap::Iterator it;
01520
01521 kDebug(7001) << "Entries watched:";
01522 if (m_mapEntries.count()==0) {
01523 kDebug(7001) << " None.";
01524 }
01525 else {
01526 it = m_mapEntries.begin();
01527 for( ; it != m_mapEntries.end(); ++it ) {
01528 Entry* e = &(*it);
01529 kDebug(7001) << " " << e->path << " ("
01530 << ((e->m_status==Normal)?"":"Nonexistent ")
01531 << (e->isDir ? "Dir":"File") << ", using "
01532 << ((e->m_mode == FAMMode) ? "FAM" :
01533 (e->m_mode == INotifyMode) ? "INotify" :
01534 (e->m_mode == DNotifyMode) ? "DNotify" :
01535 (e->m_mode == QFSWatchMode) ? "QFSWatch" :
01536 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01537 << ")";
01538
01539 foreach(Client* c, e->m_clients) {
01540 QString pending;
01541 if (c->watchingStopped) {
01542 if (c->pending & Deleted) pending += "deleted ";
01543 if (c->pending & Created) pending += "created ";
01544 if (c->pending & Changed) pending += "changed ";
01545 if (!pending.isEmpty()) pending = " (pending: " + pending + ')';
01546 pending = ", stopped" + pending;
01547 }
01548 kDebug(7001) << " by " << c->instance->objectName()
01549 << " (" << c->count << " times)" << pending;
01550 }
01551 if (e->m_entries.count()>0) {
01552 kDebug(7001) << " dependent entries:";
01553 foreach(Entry *d, e->m_entries) {
01554 kDebug(7001) << " " << d->path;
01555 }
01556 }
01557 }
01558 }
01559 }
01560
01561 #ifdef HAVE_QFILESYSTEMWATCHER
01562
01563 void KDirWatchPrivate::fswEventReceived(const QString &path)
01564 {
01565 EntryMap::Iterator it;
01566 it = m_mapEntries.find(path);
01567 if(it != m_mapEntries.end()) {
01568 Entry entry = *it;
01569 Entry *e = &entry;
01570 e->dirty = true;
01571 int ev = scanEntry(e);
01572 if (ev != NoChange)
01573 emitEvent(e, ev);
01574 if(ev == Deleted) {
01575 if (e->isDir)
01576 addEntry(0, QDir::cleanPath(e->path + "/.."), e, true);
01577 else
01578 addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
01579 } else
01580 if (ev == Changed && e->isDir && e->m_entries.count()) {
01581 Entry* sub_entry = 0;
01582 Q_FOREACH(sub_entry, e->m_entries) {
01583 if(e->isDir) {
01584 if (QFileInfo(sub_entry->path).isDir())
01585 break;
01586 } else {
01587 if (QFileInfo(sub_entry->path).isFile())
01588 break;
01589 }
01590 }
01591 if (sub_entry) {
01592 removeEntry(0, e, sub_entry);
01593
01594
01595
01596
01597 if(!useQFSWatch(sub_entry))
01598 #ifdef HAVE_SYS_INOTIFY_H
01599 if(!useINotify(sub_entry))
01600 #endif
01601 useStat(sub_entry);
01602 fswEventReceived(sub_entry->path);
01603 }
01604 }
01605 }
01606 }
01607 #else
01608 void KDirWatchPrivate::fswEventReceived(const QString &path)
01609 {
01610 Q_UNUSED(path);
01611 kWarning (7001) << "QFileSystemWatcher event received but QFileSystemWatcher is not supported";
01612 }
01613 #endif // HAVE_QFILESYSTEMWATCHER
01614
01615
01616
01617
01618
01619 K_GLOBAL_STATIC(KDirWatch, s_pKDirWatchSelf)
01620 KDirWatch* KDirWatch::self()
01621 {
01622 return s_pKDirWatchSelf;
01623 }
01624
01625 bool KDirWatch::exists()
01626 {
01627 return s_pKDirWatchSelf != 0;
01628 }
01629
01630 KDirWatch::KDirWatch (QObject* parent)
01631 : QObject(parent), d(createPrivate())
01632 {
01633 static int nameCounter = 0;
01634
01635 nameCounter++;
01636 setObjectName(QString("KDirWatch-%1").arg(nameCounter) );
01637
01638 d->ref();
01639
01640 d->_isStopped = false;
01641 }
01642
01643 KDirWatch::~KDirWatch()
01644 {
01645 d->removeEntries(this);
01646 if ( d->deref() )
01647 {
01648
01649 delete d;
01650 dwp_self = 0;
01651 }
01652 }
01653
01654 void KDirWatch::addDir( const QString& _path, WatchModes watchModes)
01655 {
01656 if (d) d->addEntry(this, _path, 0, true, watchModes);
01657 }
01658
01659 void KDirWatch::addFile( const QString& _path )
01660 {
01661 if (d) d->addEntry(this, _path, 0, false);
01662 }
01663
01664 QDateTime KDirWatch::ctime( const QString &_path ) const
01665 {
01666 KDirWatchPrivate::Entry* e = d->entry(_path);
01667
01668 if (!e)
01669 return QDateTime();
01670
01671 QDateTime result;
01672 result.setTime_t(e->m_ctime);
01673 return result;
01674 }
01675
01676 void KDirWatch::removeDir( const QString& _path )
01677 {
01678 if (d) d->removeEntry(this, _path, 0);
01679 }
01680
01681 void KDirWatch::removeFile( const QString& _path )
01682 {
01683 if (d) d->removeEntry(this, _path, 0);
01684 }
01685
01686 bool KDirWatch::stopDirScan( const QString& _path )
01687 {
01688 if (d) {
01689 KDirWatchPrivate::Entry *e = d->entry(_path);
01690 if (e && e->isDir) return d->stopEntryScan(this, e);
01691 }
01692 return false;
01693 }
01694
01695 bool KDirWatch::restartDirScan( const QString& _path )
01696 {
01697 if (d) {
01698 KDirWatchPrivate::Entry *e = d->entry(_path);
01699 if (e && e->isDir)
01700
01701 return d->restartEntryScan(this, e, false);
01702 }
01703 return false;
01704 }
01705
01706 void KDirWatch::stopScan()
01707 {
01708 if (d) {
01709 d->stopScan(this);
01710 d->_isStopped = true;
01711 }
01712 }
01713
01714 bool KDirWatch::isStopped()
01715 {
01716 return d->_isStopped;
01717 }
01718
01719 void KDirWatch::startScan( bool notify, bool skippedToo )
01720 {
01721 if (d) {
01722 d->_isStopped = false;
01723 d->startScan(this, notify, skippedToo);
01724 }
01725 }
01726
01727
01728 bool KDirWatch::contains( const QString& _path ) const
01729 {
01730 KDirWatchPrivate::Entry* e = d->entry(_path);
01731 if (!e)
01732 return false;
01733
01734 foreach(KDirWatchPrivate::Client* client, e->m_clients) {
01735 if (client->instance == this)
01736 return true;
01737 }
01738
01739 return false;
01740 }
01741
01742 void KDirWatch::statistics()
01743 {
01744 if (!dwp_self) {
01745 kDebug(7001) << "KDirWatch not used";
01746 return;
01747 }
01748 dwp_self->statistics();
01749 }
01750
01751
01752 void KDirWatch::setCreated( const QString & _file )
01753 {
01754 kDebug(7001) << objectName() << "emitting created" << _file;
01755 emit created( _file );
01756 }
01757
01758 void KDirWatch::setDirty( const QString & _file )
01759 {
01760 kDebug(7001) << objectName() << "emitting dirty" << _file;
01761 emit dirty( _file );
01762 }
01763
01764 void KDirWatch::setDeleted( const QString & _file )
01765 {
01766 kDebug(7001) << objectName() << "emitting deleted" << _file;
01767 emit deleted( _file );
01768 }
01769
01770 KDirWatch::Method KDirWatch::internalMethod()
01771 {
01772 #ifdef HAVE_FAM
01773 if (d->use_fam)
01774 return KDirWatch::FAM;
01775 #endif
01776 #ifdef HAVE_SYS_INOTIFY_H
01777 if (d->supports_inotify)
01778 return KDirWatch::INotify;
01779 #endif
01780 return KDirWatch::Stat;
01781 }
01782
01783
01784 #include "kdirwatch.moc"
01785 #include "kdirwatch_p.moc"
01786
01787
01788
01789