• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kdirwatch.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 2 -*-
00002 /* This file is part of the KDE libraries
00003    Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
00004    Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
00005    Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Library General Public
00009    License version 2 as published by the Free Software Foundation.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 
00023 // CHANGES:
00024 // Aug 6,  2007 - KDirWatch::WatchModes support complete, flags work fine also
00025 // when using FAMD (Flavio Castelli)
00026 // Aug 3,  2007 - Handled KDirWatch::WatchModes flags when using inotify, now
00027 // recursive and file monitoring modes are implemented (Flavio Castelli)
00028 // Jul 30, 2007 - Substituted addEntry boolean params with KDirWatch::WatchModes
00029 // flag (Flavio Castelli)
00030 // Oct 4,  2005 - Inotify support (Dirk Mueller)
00031 // Februar 2002 - Add file watching and remote mount check for STAT
00032 // Mar 30, 2001 - Native support for Linux dir change notification.
00033 // Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
00034 // May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
00035 // May 23. 1998 - Removed static pointer - you can have more instances.
00036 // It was Needed for KRegistry. KDirWatch now emits signals and doesn't
00037 // call (or need) KFM. No more URL's - just plain paths. (sven)
00038 // Mar 29. 1998 - added docs, stop/restart for particular Dirs and
00039 // deep copies for list of dirs. (sven)
00040 // Mar 28. 1998 - Created.  (sven)
00041 
00042 #include "kdirwatch.h"
00043 #include "kdirwatch_p.h"
00044 
00045 #include <config-kdirwatch.h>
00046 #include <config.h>
00047 
00048 #include <sys/stat.h>
00049 #include <assert.h>
00050 #include <QtCore/QDir>
00051 #include <QtCore/QFile>
00052 #include <QtCore/QSocketNotifier>
00053 #include <QtCore/QTimer>
00054 
00055 #include <kapplication.h>
00056 #include <kdebug.h>
00057 #include <kconfig.h>
00058 #include <kglobal.h>
00059 #include <kde_file.h>
00060 #include <kconfiggroup.h>
00061 #include "kmountpoint.h"
00062 
00063 #include <stdlib.h>
00064 
00065 // debug
00066 #include <sys/ioctl.h>
00067 
00068 
00069 #include <sys/utsname.h>
00070 
00071 #define NO_NOTIFY (time_t) 0
00072 
00073 static KDirWatchPrivate* dwp_self = 0;
00074 static KDirWatchPrivate* createPrivate() {
00075   if (!dwp_self)
00076     dwp_self = new KDirWatchPrivate;
00077   return dwp_self;
00078 }
00079 
00080 //
00081 // Class KDirWatchPrivate (singleton)
00082 //
00083 
00084 /* All entries (files/directories) to be watched in the
00085  * application (coming from multiple KDirWatch instances)
00086  * are registered in a single KDirWatchPrivate instance.
00087  *
00088  * At the moment, the following methods for file watching
00089  * are supported:
00090  * - Polling: All files to be watched are polled regularly
00091  *   using stat (more precise: QFileInfo.lastModified()).
00092  *   The polling frequency is determined from global kconfig
00093  *   settings, defaulting to 500 ms for local directories
00094  *   and 5000 ms for remote mounts
00095  * - FAM (File Alternation Monitor): first used on IRIX, SGI
00096  *   has ported this method to LINUX. It uses a kernel part
00097  *   (IMON, sending change events to /dev/imon) and a user
00098  *   level damon (fam), to which applications connect for
00099  *   notification of file changes. For NFS, the fam damon
00100  *   on the NFS server machine is used; if IMON is not built
00101  *   into the kernel, fam uses polling for local files.
00102  * - INOTIFY: In LINUX 2.6.13, inode change notification was
00103  *   introduced. You're now able to watch arbitrary inode's
00104  *   for changes, and even get notification when they're
00105  *   unmounted.
00106  */
00107 
00108 KDirWatchPrivate::KDirWatchPrivate()
00109   : timer(),
00110     freq( 3600000 ), // 1 hour as upper bound
00111     statEntries( 0 ),
00112     m_ref( 0 ),
00113     delayRemove( false ),
00114     rescan_all( false ),
00115     rescan_timer()
00116 {
00117   timer.setObjectName( "KDirWatchPrivate::timer" );
00118   connect (&timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00119 
00120   KConfigGroup config(KGlobal::config(), QLatin1String("DirWatch"));
00121   m_nfsPollInterval = config.readEntry("NFSPollInterval", 5000);
00122   m_PollInterval = config.readEntry("PollInterval", 500);
00123 
00124   QString method = config.readEntry("PreferredMethod", "inotify");
00125   if (method == "Fam")
00126   {
00127     m_preferredMethod = Fam;
00128   }else if (method == "Stat")
00129   {
00130     m_preferredMethod = Stat;
00131   }else if (method == "QFSWatch") {
00132     m_preferredMethod = QFSWatch;
00133   }else
00134   {
00135 #ifdef Q_OS_WIN
00136     m_preferredMethod = QFSWatch;
00137 #else
00138     m_preferredMethod = INotify;
00139 #endif
00140   }
00141 
00142 
00143   QStringList availableMethods;
00144 
00145   availableMethods << "Stat";
00146 
00147   // used for FAM
00148   rescan_timer.setObjectName( "KDirWatchPrivate::rescan_timer" );
00149   rescan_timer.setSingleShot( true );
00150   connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00151 
00152 #ifdef HAVE_FAM
00153   // It's possible that FAM server can't be started
00154   if (FAMOpen(&fc) ==0) {
00155     availableMethods << "FAM";
00156     use_fam=true;
00157     sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00158                   QSocketNotifier::Read, this);
00159     connect( sn, SIGNAL(activated(int)),
00160          this, SLOT(famEventReceived()) );
00161   }
00162   else {
00163     kDebug(7001) << "Can't use FAM (fam daemon not running?)";
00164     use_fam=false;
00165   }
00166 #endif
00167 
00168 #ifdef HAVE_SYS_INOTIFY_H
00169   supports_inotify = true;
00170 
00171   m_inotify_fd = inotify_init();
00172 
00173   if ( m_inotify_fd <= 0 ) {
00174     kDebug(7001) << "Can't use Inotify, kernel doesn't support it";
00175     supports_inotify = false;
00176   }
00177 
00178   {
00179     struct utsname uts;
00180     int major, minor, patch;
00181     if (uname(&uts) < 0)
00182       supports_inotify = false; // *shrug*
00183     else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00184       supports_inotify = false; // *shrug*
00185     else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
00186       kDebug(7001) << "Can't use INotify, Linux kernel too old";
00187       supports_inotify = false;
00188     }
00189   }
00190 
00191   if ( supports_inotify ) {
00192     availableMethods << "INotify";
00193     fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
00194 
00195     mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
00196     connect( mSn, SIGNAL(activated( int )),
00197              this, SLOT( inotifyEventReceived() ) );
00198   }
00199 #endif
00200 #ifdef HAVE_QFILESYSTEMWATCHER
00201   availableMethods << "QFileSystemWatcher";
00202   fsWatcher = new KFileSystemWatcher();
00203   connect(fsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(fswEventReceived(QString)));
00204   connect(fsWatcher, SIGNAL(fileChanged(QString)),      this, SLOT(fswEventReceived(QString)));
00205 #endif
00206   kDebug(7001) << "Available methods: " << availableMethods;
00207 }
00208 
00209 /* This is called on app exit (K_GLOBAL_STATIC) */
00210 KDirWatchPrivate::~KDirWatchPrivate()
00211 {
00212   timer.stop();
00213 
00214   /* remove all entries being watched */
00215   removeEntries(0);
00216 
00217 #ifdef HAVE_FAM
00218   if (use_fam) {
00219     FAMClose(&fc);
00220   }
00221 #endif
00222 #ifdef HAVE_SYS_INOTIFY_H
00223   if ( supports_inotify )
00224     ::close( m_inotify_fd );
00225 #endif
00226 #ifdef HAVE_QFILESYSTEMWATCHER
00227   delete fsWatcher;
00228 #endif
00229 }
00230 
00231 void KDirWatchPrivate::inotifyEventReceived()
00232 {
00233   //kDebug(7001);
00234 #ifdef HAVE_SYS_INOTIFY_H
00235   if ( !supports_inotify )
00236     return;
00237 
00238   int pending = -1;
00239   int offset = 0;
00240   char buf[4096];
00241   assert( m_inotify_fd > -1 );
00242   ioctl( m_inotify_fd, FIONREAD, &pending );
00243 
00244   while ( pending > 0 ) {
00245 
00246     if ( pending > (int)sizeof( buf ) )
00247       pending = sizeof( buf );
00248 
00249     pending = read( m_inotify_fd, buf, pending);
00250 
00251     while ( pending > 0 ) {
00252       struct inotify_event *event = (struct inotify_event *) &buf[offset];
00253       pending -= sizeof( struct inotify_event ) + event->len;
00254       offset += sizeof( struct inotify_event ) + event->len;
00255 
00256       QString path;
00257       QByteArray cpath(event->name, event->len);
00258       if(event->len)
00259         path = QFile::decodeName ( cpath );
00260 
00261       if ( path.length() && isNoisyFile( cpath ) )
00262         continue;
00263 
00264       // now we're in deep trouble of finding the
00265       // associated entries
00266       // for now, we suck and iterate
00267       for ( EntryMap::Iterator it = m_mapEntries.begin();
00268             it != m_mapEntries.end();  ) {
00269         Entry* e = &( *it );
00270         ++it;
00271         if ( e->wd == event->wd ) {
00272           e->dirty = true;
00273 
00274           if( event->mask & IN_DELETE_SELF) {
00275             kDebug(7001) << "-->got deleteself signal for" << e->path;
00276             e->m_status = NonExistent;
00277             if (e->isDir)
00278               addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00279             else
00280               addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
00281           }
00282           if ( event->mask & IN_IGNORED ) {
00283             e->wd = 0;
00284           }
00285           if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
00286             Entry* sub_entry = 0;
00287             Q_FOREACH(sub_entry, e->m_entries)
00288               if (sub_entry->path == e->path + '/' + path) break;
00289 
00290             if (sub_entry /*&& sub_entry->isDir*/) {
00291               removeEntry(0, e, sub_entry);
00292               KDE_struct_stat stat_buf;
00293               QByteArray tpath = QFile::encodeName(path);
00294               KDE_stat(tpath, &stat_buf);
00295 
00296               //sub_entry->isDir = S_ISDIR(stat_buf.st_mode);
00297               //sub_entry->m_ctime = stat_buf.st_ctime;
00298               //sub_entry->m_status = Normal;
00299               //sub_entry->m_nlink = stat_buf.st_nlink;
00300 
00301               if(!useINotify(sub_entry))
00302                 useStat(sub_entry);
00303               sub_entry->dirty = true;
00304             }
00305             else if ((e->isDir) && (!e->m_clients.empty())) {
00306               Client* client = 0;
00307 
00308               KDE_struct_stat stat_buf;
00309               QByteArray tpath = QFile::encodeName(e->path+'/'+path);
00310               KDE_stat(tpath, &stat_buf);
00311               bool isDir = S_ISDIR(stat_buf.st_mode);
00312 
00313               KDirWatch::WatchModes flag;
00314               flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
00315 
00316               int counter = 0;
00317               Q_FOREACH(client, e->m_clients) {
00318                   if (client->m_watchModes & flag) {
00319                       addEntry (client->instance, tpath, 0, isDir,
00320                                 isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
00321                       counter++;
00322                     }
00323               }
00324 
00325               if (counter != 0)
00326                 emitEvent (e, Created, e->path+'/'+path);
00327 
00328               kDebug(7001).nospace() << counter << " instance(s) monitoring the new "
00329                 << (isDir ? "dir " : "file ") << tpath;
00330             }
00331           }
00332 
00333           if (!rescan_timer.isActive())
00334             rescan_timer.start(m_PollInterval); // singleshot
00335 
00336           break;
00337         }
00338       }
00339     }
00340   }
00341 #endif
00342 }
00343 
00344 /* In FAM mode, only entries which are marked dirty are scanned.
00345  * We first need to mark all yet nonexistent, but possible created
00346  * entries as dirty...
00347  */
00348 void KDirWatchPrivate::Entry::propagate_dirty()
00349 {
00350   foreach(Entry *sub_entry, m_entries)
00351   {
00352      if (!sub_entry->dirty)
00353      {
00354         sub_entry->dirty = true;
00355         sub_entry->propagate_dirty();
00356      }
00357   }
00358 }
00359 
00360 
00361 /* A KDirWatch instance is interested in getting events for
00362  * this file/Dir entry.
00363  */
00364 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance,
00365                                         KDirWatch::WatchModes watchModes)
00366 {
00367   if (instance == 0)
00368     return;
00369 
00370   foreach(Client* client, m_clients) {
00371     if (client->instance == instance) {
00372       client->count++;
00373       client->m_watchModes = watchModes;
00374       return;
00375     }
00376   }
00377 
00378   Client* client = new Client;
00379   client->instance = instance;
00380   client->count = 1;
00381   client->watchingStopped = instance->isStopped();
00382   client->pending = NoChange;
00383   client->m_watchModes = watchModes;
00384 
00385   m_clients.append(client);
00386 }
00387 
00388 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00389 {
00390   QList<Client *>::iterator it = m_clients.begin();
00391   const QList<Client *>::iterator end = m_clients.end();
00392   for ( ; it != end ; ++it ) {
00393     Client* client = *it;
00394     if (client->instance == instance) {
00395       client->count--;
00396       if (client->count == 0) {
00397         m_clients.erase(it);
00398         delete client;
00399       }
00400       return;
00401     }
00402   }
00403 }
00404 
00405 /* get number of clients */
00406 int KDirWatchPrivate::Entry::clients()
00407 {
00408   int clients = 0;
00409   foreach(Client* client, m_clients)
00410     clients += client->count;
00411 
00412   return clients;
00413 }
00414 
00415 
00416 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00417 {
00418 // we only support absolute paths
00419   if (_path.isEmpty() || QDir::isRelativePath(_path)) {
00420     return 0;
00421   }
00422 
00423   QString path (_path);
00424 
00425   if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
00426     path.truncate( path.length() - 1 );
00427 
00428   EntryMap::Iterator it = m_mapEntries.find( path );
00429   if ( it == m_mapEntries.end() )
00430     return 0;
00431   else
00432     return &(*it);
00433 }
00434 
00435 // set polling frequency for a entry and adjust global freq if needed
00436 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00437 {
00438   e->freq = newFreq;
00439 
00440   // a reasonable frequency for the global polling timer
00441   if (e->freq < freq) {
00442     freq = e->freq;
00443     if (timer.isActive()) timer.start(freq);
00444     kDebug(7001) << "Global Poll Freq is now" << freq << "msec";
00445   }
00446 }
00447 
00448 
00449 #if defined(HAVE_FAM)
00450 // setup FAM notification, returns false if not possible
00451 bool KDirWatchPrivate::useFAM(Entry* e)
00452 {
00453   if (!use_fam) return false;
00454 
00455   // handle FAM events to avoid deadlock
00456   // (FAM sends back all files in a directory when monitoring)
00457   famEventReceived();
00458 
00459   e->m_mode = FAMMode;
00460   e->dirty = false;
00461 
00462   if (e->isDir) {
00463     if (e->m_status == NonExistent) {
00464       // If the directory does not exist we watch the parent directory
00465       addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00466     }
00467     else {
00468       int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00469                    &(e->fr), e);
00470       if (res<0) {
00471     e->m_mode = UnknownMode;
00472     use_fam=false;
00473         delete sn; sn = 0;
00474     return false;
00475       }
00476       kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00477                    << ") for " << e->path;
00478     }
00479   }
00480   else {
00481     if (e->m_status == NonExistent) {
00482       // If the file does not exist we watch the directory
00483       addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
00484     }
00485     else {
00486       int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00487                    &(e->fr), e);
00488       if (res<0) {
00489     e->m_mode = UnknownMode;
00490     use_fam=false;
00491         delete sn; sn = 0;
00492     return false;
00493       }
00494 
00495       kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00496                    << ") for " << e->path;
00497     }
00498   }
00499 
00500   // handle FAM events to avoid deadlock
00501   // (FAM sends back all files in a directory when monitoring)
00502   famEventReceived();
00503 
00504   return true;
00505 }
00506 #endif
00507 
00508 #ifdef HAVE_SYS_INOTIFY_H
00509 // setup INotify notification, returns false if not possible
00510 bool KDirWatchPrivate::useINotify( Entry* e )
00511 {
00512   kDebug (7001) << "trying to use inotify for monitoring";
00513 
00514   e->wd = 0;
00515   e->dirty = false;
00516 
00517   if (!supports_inotify) return false;
00518 
00519   e->m_mode = INotifyMode;
00520 
00521   if ( e->m_status == NonExistent ) {
00522     addEntry(0, QDir::cleanPath(e->path+"/.."), e, true);
00523     return true;
00524   }
00525 
00526   int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
00527   if(!e->isDir)
00528     mask |= IN_MODIFY|IN_ATTRIB;
00529   else
00530     mask |= IN_ONLYDIR;
00531 
00532   // if dependant is a file watch, we check for MODIFY & ATTRIB too
00533   foreach(Entry *dep, e->m_entries) {
00534     if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
00535   }
00536 
00537   if ( ( e->wd = inotify_add_watch( m_inotify_fd,
00538                                     QFile::encodeName( e->path ), mask) ) > 0)
00539   {
00540     kDebug (7001) << "inotify successfully used for monitoring";
00541     return true;
00542   }
00543 
00544   return false;
00545 }
00546 #endif
00547 #ifdef HAVE_QFILESYSTEMWATCHER
00548 bool KDirWatchPrivate::useQFSWatch(Entry* e)
00549 {
00550   e->m_mode = QFSWatchMode;
00551   e->dirty = false;
00552 
00553   if ( e->m_status == NonExistent ) {
00554     addEntry( 0, QDir::cleanPath( e->path + "/.." ), e, true );
00555     return true;
00556   }
00557 
00558   fsWatcher->addPath( e->path );
00559   return true;
00560 }
00561 #endif
00562 
00563 bool KDirWatchPrivate::useStat(Entry* e)
00564 {
00565   KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(e->path);
00566   const bool slow = mp ? mp->probablySlow() : false;
00567   if (slow)
00568     useFreq(e, m_nfsPollInterval);
00569   else
00570     useFreq(e, m_PollInterval);
00571 
00572   if (e->m_mode != StatMode) {
00573     e->m_mode = StatMode;
00574     statEntries++;
00575 
00576     if ( statEntries == 1 ) {
00577       // if this was first STAT entry (=timer was stopped)
00578       timer.start(freq);      // then start the timer
00579       kDebug(7001) << " Started Polling Timer, freq " << freq;
00580     }
00581   }
00582 
00583   kDebug(7001) << " Setup Stat (freq " << e->freq << ") for " << e->path;
00584 
00585   return true;
00586 }
00587 
00588 
00589 /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
00590  * providing in <isDir> the type of the entry to be watched.
00591  * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
00592  * this entry needs another entry to watch himself (when notExistent).
00593  */
00594 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00595                 Entry* sub_entry, bool isDir, KDirWatch::WatchModes watchModes)
00596 {
00597   QString path (_path);
00598   if (path.isEmpty()
00599 #ifndef Q_WS_WIN
00600     || path.startsWith("/dev/") || (path == "/dev")
00601 #endif
00602   )
00603     return; // Don't even go there.
00604 
00605   if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
00606     path.truncate( path.length() - 1 );
00607 
00608   EntryMap::Iterator it = m_mapEntries.find( path );
00609   if ( it != m_mapEntries.end() )
00610   {
00611     if (sub_entry) {
00612        (*it).m_entries.append(sub_entry);
00613        kDebug(7001) << "Added already watched Entry" << path
00614                     << "(for" << sub_entry->path << ")";
00615 #ifdef HAVE_SYS_INOTIFY_H
00616        Entry* e = &(*it);
00617        if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
00618          int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
00619          if(!e->isDir)
00620            mask |= IN_MODIFY|IN_ATTRIB;
00621          else
00622            mask |= IN_ONLYDIR;
00623 
00624          inotify_rm_watch (m_inotify_fd, e->wd);
00625          e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ),
00626                                     mask);
00627        }
00628 #endif
00629     }
00630     else {
00631        (*it).addClient(instance, watchModes);
00632        kDebug(7001) << "Added already watched Entry" << path
00633              << "(now" <<  (*it).clients() << "clients)"
00634              << QString("[%1]").arg(instance->objectName());
00635     }
00636     return;
00637   }
00638 
00639   // we have a new path to watch
00640 
00641   KDE_struct_stat stat_buf;
00642   QByteArray tpath (QFile::encodeName(path));
00643   bool exists = (KDE_stat(tpath, &stat_buf) == 0);
00644 
00645   EntryMap::iterator newIt = m_mapEntries.insert( path, Entry() );
00646   // the insert does a copy, so we have to use <e> now
00647   Entry* e = &(*newIt);
00648 
00649   if (exists) {
00650     e->isDir = S_ISDIR(stat_buf.st_mode);
00651 
00652     if (e->isDir && !isDir)
00653       qWarning() << "KDirWatch:" << path << "is a directory. Use addDir!";
00654     else if (!e->isDir && isDir)
00655       qWarning("KDirWatch: %s is a file. Use addFile!", qPrintable(path));
00656 
00657     if (!e->isDir && ( watchModes != KDirWatch::WatchDirOnly)) {
00658       qWarning() << "KDirWatch:" << path << "is a file. You can't use recursive or "
00659                     "watchFiles options";
00660       watchModes = KDirWatch::WatchDirOnly;
00661     }
00662 
00663 #ifdef Q_OS_WIN
00664     // ctime is the 'creation time' on windows - use mtime instead
00665     e->m_ctime = stat_buf.st_mtime;
00666 #else
00667     e->m_ctime = stat_buf.st_ctime;
00668 #endif
00669     e->m_status = Normal;
00670     e->m_nlink = stat_buf.st_nlink;
00671   }
00672   else {
00673     e->isDir = isDir;
00674     e->m_ctime = invalid_ctime;
00675     e->m_status = NonExistent;
00676     e->m_nlink = 0;
00677   }
00678 
00679   e->path = path;
00680   if (sub_entry)
00681     e->m_entries.append(sub_entry);
00682   else
00683     e->addClient(instance, watchModes);
00684 
00685   kDebug(7001).nospace() << "Added " << (e->isDir ? "Dir " : "File ") << path
00686     << (e->m_status == NonExistent ? " NotExisting" : "")
00687     << " for " << (sub_entry ? sub_entry->path : "")
00688     << " [" << (instance ? instance->objectName() : "") << "]";
00689 
00690   // now setup the notification method
00691   e->m_mode = UnknownMode;
00692   e->msecLeft = 0;
00693 
00694   if ( isNoisyFile( tpath ) )
00695     return;
00696 
00697   if (exists && e->isDir && (watchModes != KDirWatch::WatchDirOnly)) {
00698     QFlags<QDir::Filter> filters = QDir::NoDotAndDotDot;
00699 
00700     if ((watchModes & KDirWatch::WatchSubDirs) &&
00701         (watchModes & KDirWatch::WatchFiles)) {
00702       filters |= (QDir::Dirs|QDir::Files);
00703     } else if (watchModes & KDirWatch::WatchSubDirs) {
00704       filters |= QDir::Dirs;
00705     } else if (watchModes & KDirWatch::WatchFiles) {
00706       filters |= QDir::Files;
00707     }
00708 
00709     QDir basedir (e->path);
00710     const QFileInfoList contents = basedir.entryInfoList(filters);
00711     for (QFileInfoList::const_iterator iter = contents.constBegin();
00712          iter != contents.constEnd(); ++iter)
00713     {
00714       const QFileInfo &fileInfo = *iter;
00715       bool isDir = fileInfo.isDir();
00716       addEntry (instance, fileInfo.absoluteFilePath(), 0, isDir,
00717                 isDir ? watchModes : KDirWatch::WatchDirOnly);
00718     }
00719   }
00720 
00721   // Now I've put inotify check before famd one, otherwise famd will be used
00722   // also when inotify is available. Since inotify works
00723   // better than famd, it is preferred to the last one
00724 
00725   //First try to use the preferred method, if that fails use the usual order:
00726   //inotify,fam,stat
00727   bool entryAdded = false;
00728   if (m_preferredMethod == Fam)
00729   {
00730 #if defined(HAVE_FAM)
00731     entryAdded = useFAM(e);
00732 #endif
00733   }else if (m_preferredMethod == INotify)
00734   {
00735 #if defined(HAVE_SYS_INOTIFY_H)
00736     entryAdded = useINotify(e);
00737 #endif
00738   }else if (m_preferredMethod == QFSWatch)
00739   {
00740 #ifdef HAVE_QFILESYSTEMWATCHER
00741     entryAdded = useQFSWatch(e);
00742 #endif
00743   }else if (m_preferredMethod == Stat)
00744   {
00745     entryAdded = useStat(e);
00746   }
00747 
00748   if (!entryAdded)
00749   {
00750 #if defined(HAVE_SYS_INOTIFY_H)
00751     if (useINotify(e)) return;
00752 #endif
00753 
00754 #if defined(HAVE_FAM)
00755     if (useFAM(e)) return;
00756 #endif
00757 
00758 #if defined(HAVE_QFILESYSTEMWATCHER)
00759     if (useQFSWatch(e)) return;
00760 #endif
00761 
00762     useStat(e);
00763   }
00764 }
00765 
00766 
00767 void KDirWatchPrivate::removeEntry(KDirWatch* instance,
00768                                    const QString& _path,
00769                                    Entry* sub_entry)
00770 {
00771   kDebug(7001) << "path=" << _path << "sub_entry:" << sub_entry;
00772   Entry* e = entry(_path);
00773   if (!e) {
00774     kWarning(7001) << "doesn't know" << _path;
00775     return;
00776   }
00777 
00778   removeEntry(instance, e, sub_entry);
00779 }
00780 
00781 void KDirWatchPrivate::removeEntry(KDirWatch* instance,
00782                                    Entry* e,
00783                                    Entry* sub_entry)
00784 {
00785   removeList.remove(e);
00786 
00787   if (sub_entry)
00788     e->m_entries.removeAll(sub_entry);
00789   else
00790     e->removeClient(instance);
00791 
00792   if (e->m_clients.count() || e->m_entries.count())
00793     return;
00794 
00795   if (delayRemove) {
00796     removeList.insert(e);
00797     // now e->isValid() is false
00798     return;
00799   }
00800 
00801 #ifdef HAVE_FAM
00802   if (e->m_mode == FAMMode) {
00803     if ( e->m_status == Normal) {
00804       FAMCancelMonitor(&fc, &(e->fr) );
00805       kDebug(7001).nospace()  << "Cancelled FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
00806                     << ") for " << e->path;
00807     }
00808     else {
00809       if (e->isDir)
00810     removeEntry(0, QDir::cleanPath(e->path+"/.."), e);
00811       else
00812     removeEntry(0, QFileInfo(e->path).absolutePath(), e);
00813     }
00814   }
00815 #endif
00816 
00817 #ifdef HAVE_SYS_INOTIFY_H
00818   if (e->m_mode == INotifyMode) {
00819     if ( e->m_status == Normal ) {
00820       (void) inotify_rm_watch( m_inotify_fd, e->wd );
00821       kDebug(7001).nospace() << "Cancelled INotify (fd " << m_inotify_fd << ", "
00822                              << e->wd << ") for " << e->path;
00823     }
00824     else {
00825       if (e->isDir)
00826         removeEntry(0, QDir::cleanPath(e->path+"/.."), e);
00827       else
00828         removeEntry(0, QFileInfo(e->path).absolutePath(), e);
00829     }
00830   }
00831 #endif
00832 
00833 #ifdef HAVE_QFILESYSTEMWATCHER
00834   if (e->m_mode == QFSWatchMode) {
00835     fsWatcher->removePath(e->path);
00836   }
00837 #endif
00838   if (e->m_mode == StatMode) {
00839     statEntries--;
00840     if ( statEntries == 0 ) {
00841       timer.stop(); // stop timer if lists are empty
00842       kDebug(7001) << " Stopped Polling Timer";
00843     }
00844   }
00845 
00846   kDebug(7001).nospace() << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00847      << " for " << (sub_entry ? sub_entry->path : "")
00848      << " [" << (instance ? instance->objectName() : "") << "]";
00849   m_mapEntries.remove( e->path ); // <e> not valid any more
00850 }
00851 
00852 
00853 /* Called from KDirWatch destructor:
00854  * remove <instance> as client from all entries
00855  */
00856 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00857 {
00858   int minfreq = 3600000;
00859 
00860   QStringList pathList;
00861   // put all entries where instance is a client in list
00862   EntryMap::Iterator it = m_mapEntries.begin();
00863   for( ; it != m_mapEntries.end(); ++it ) {
00864     Client* c = 0;
00865     foreach(Client* client, (*it).m_clients) {
00866       if (client->instance == instance) {
00867         c = client;
00868         break;
00869       }
00870     }
00871     if (c) {
00872       c->count = 1; // forces deletion of instance as client
00873       pathList.append((*it).path);
00874     }
00875     else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00876       minfreq = (*it).freq;
00877   }
00878 
00879   foreach(const QString &path, pathList)
00880     removeEntry(instance, path, 0);
00881 
00882   if (minfreq > freq) {
00883     // we can decrease the global polling frequency
00884     freq = minfreq;
00885     if (timer.isActive()) timer.start(freq);
00886     kDebug(7001) << "Poll Freq now" << freq << "msec";
00887   }
00888 }
00889 
00890 // instance ==0: stop scanning for all instances
00891 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00892 {
00893   int stillWatching = 0;
00894   foreach(Client* client, e->m_clients) {
00895     if (!instance || instance == client->instance)
00896       client->watchingStopped = true;
00897     else if (!client->watchingStopped)
00898       stillWatching += client->count;
00899   }
00900 
00901   kDebug(7001)  << (instance ? instance->objectName() : "all")
00902                 << "stopped scanning" << e->path << "(now"
00903                 << stillWatching << "watchers)";
00904 
00905   if (stillWatching == 0) {
00906     // if nobody is interested, we don't watch
00907     if ( e->m_mode != INotifyMode ) {
00908       e->m_ctime = invalid_ctime; // invalid
00909       e->m_status = NonExistent;
00910     }
00911     //    e->m_status = Normal;
00912   }
00913   return true;
00914 }
00915 
00916 // instance ==0: start scanning for all instances
00917 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
00918                      bool notify)
00919 {
00920   int wasWatching = 0, newWatching = 0;
00921   foreach(Client* client, e->m_clients) {
00922     if (!client->watchingStopped)
00923       wasWatching += client->count;
00924     else if (!instance || instance == client->instance) {
00925       client->watchingStopped = false;
00926       newWatching += client->count;
00927     }
00928   }
00929   if (newWatching == 0)
00930     return false;
00931 
00932   kDebug(7001)  << (instance ? instance->objectName() : "all")
00933                 << "restarted scanning" << e->path
00934                 << "(now" << wasWatching+newWatching << "watchers)";
00935 
00936   // restart watching and emit pending events
00937 
00938   int ev = NoChange;
00939   if (wasWatching == 0) {
00940     if (!notify) {
00941       KDE_struct_stat stat_buf;
00942       bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
00943       if (exists) {
00944 #ifdef Q_OS_WIN
00945         // ctime is the 'creation time' on windows - use mtime instead
00946         e->m_ctime = stat_buf.st_mtime;
00947 #else
00948         e->m_ctime = stat_buf.st_ctime;
00949 #endif
00950         e->m_status = Normal;
00951         e->m_nlink = stat_buf.st_nlink;
00952       }
00953       else {
00954         e->m_ctime = invalid_ctime;
00955         e->m_status = NonExistent;
00956         e->m_nlink = 0;
00957       }
00958     }
00959     e->msecLeft = 0;
00960     ev = scanEntry(e);
00961   }
00962   emitEvent(e,ev);
00963 
00964   return true;
00965 }
00966 
00967 // instance ==0: stop scanning for all instances
00968 void KDirWatchPrivate::stopScan(KDirWatch* instance)
00969 {
00970   EntryMap::Iterator it = m_mapEntries.begin();
00971   for( ; it != m_mapEntries.end(); ++it )
00972     stopEntryScan(instance, &(*it));
00973 }
00974 
00975 
00976 void KDirWatchPrivate::startScan(KDirWatch* instance,
00977                                  bool notify, bool skippedToo )
00978 {
00979   if (!notify)
00980     resetList(instance,skippedToo);
00981 
00982   EntryMap::Iterator it = m_mapEntries.begin();
00983   for( ; it != m_mapEntries.end(); ++it )
00984     restartEntryScan(instance, &(*it), notify);
00985 
00986   // timer should still be running when in polling mode
00987 }
00988 
00989 
00990 // clear all pending events, also from stopped
00991 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, bool skippedToo )
00992 {
00993   EntryMap::Iterator it = m_mapEntries.begin();
00994   for( ; it != m_mapEntries.end(); ++it ) {
00995 
00996     foreach(Client* client, (*it).m_clients) {
00997       if (!client->watchingStopped || skippedToo)
00998         client->pending = NoChange;
00999     }
01000   }
01001 }
01002 
01003 // Return event happened on <e>
01004 //
01005 int KDirWatchPrivate::scanEntry(Entry* e)
01006 {
01007 #ifdef HAVE_FAM
01008   if (e->m_mode == FAMMode) {
01009     // we know nothing has changed, no need to stat
01010     if(!e->dirty) return NoChange;
01011     e->dirty = false;
01012   }
01013 #endif
01014 
01015   // Shouldn't happen: Ignore "unknown" notification method
01016   if (e->m_mode == UnknownMode) return NoChange;
01017 
01018 #if defined( HAVE_SYS_INOTIFY_H )
01019   if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
01020     // we know nothing has changed, no need to stat
01021     if(!e->dirty) return NoChange;
01022     e->dirty = false;
01023   }
01024 #endif
01025 
01026 #if defined( HAVE_QFILESYSTEMWATCHER )
01027   if (e->m_mode == QFSWatchMode ) {
01028     // we know nothing has changed, no need to stat
01029     if(!e->dirty) return NoChange;
01030     e->dirty = false;
01031   }
01032 #endif
01033 
01034   if (e->m_mode == StatMode) {
01035     // only scan if timeout on entry timer happens;
01036     // e.g. when using 500msec global timer, a entry
01037     // with freq=5000 is only watched every 10th time
01038 
01039     e->msecLeft -= freq;
01040     if (e->msecLeft>0) return NoChange;
01041     e->msecLeft += e->freq;
01042   }
01043 
01044   KDE_struct_stat stat_buf;
01045   bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
01046   if (exists) {
01047 
01048     if (e->m_status == NonExistent) {
01049 #ifdef Q_OS_WIN
01050       // ctime is the 'creation time' on windows - use mtime instead
01051       e->m_ctime = stat_buf.st_mtime;
01052 #else
01053       e->m_ctime = stat_buf.st_ctime;
01054 #endif
01055       e->m_status = Normal;
01056       e->m_nlink = stat_buf.st_nlink;
01057       return Created;
01058     }
01059 
01060 #ifdef Q_OS_WIN
01061     stat_buf.st_ctime = stat_buf.st_mtime;
01062 #endif
01063     if ( (e->m_ctime != invalid_ctime) &&
01064           ((stat_buf.st_ctime != e->m_ctime) ||
01065           (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
01066       e->m_ctime = stat_buf.st_ctime;
01067       e->m_nlink = stat_buf.st_nlink;
01068       return Changed;
01069     }
01070 
01071     return NoChange;
01072   }
01073 
01074   // dir/file doesn't exist
01075 
01076   if (e->m_ctime == invalid_ctime) {
01077     e->m_nlink = 0;
01078     e->m_status = NonExistent;
01079     return NoChange;
01080   }
01081 
01082   e->m_ctime = invalid_ctime;
01083   e->m_nlink = 0;
01084   e->m_status = NonExistent;
01085 
01086   return Deleted;
01087 }
01088 
01089 /* Notify all interested KDirWatch instances about a given event on an entry
01090  * and stored pending events. When watching is stopped, the event is
01091  * added to the pending events.
01092  */
01093 void KDirWatchPrivate::emitEvent(const Entry* e, int event, const QString &fileName)
01094 {
01095   QString path (e->path);
01096   if (!fileName.isEmpty()) {
01097     if (!QDir::isRelativePath(fileName))
01098       path = fileName;
01099     else
01100 #ifdef Q_OS_UNIX
01101       path += '/' + fileName;
01102 #elif defined(Q_WS_WIN)
01103       //current drive is passed instead of /
01104       path += QDir::currentPath().left(2) + '/' + fileName;
01105 #endif
01106   }
01107 
01108   foreach(Client* c, e->m_clients)
01109   {
01110     if (c->instance==0 || c->count==0) continue;
01111 
01112     if (c->watchingStopped) {
01113       // add event to pending...
01114       if (event == Changed)
01115         c->pending |= event;
01116       else if (event == Created || event == Deleted)
01117         c->pending = event;
01118       continue;
01119     }
01120     // not stopped
01121     if (event == NoChange || event == Changed)
01122       event |= c->pending;
01123     c->pending = NoChange;
01124     if (event == NoChange) continue;
01125 
01126     if (event & Deleted) {
01127       c->instance->setDeleted(path);
01128       // emit only Deleted event...
01129       continue;
01130     }
01131 
01132     if (event & Created) {
01133       c->instance->setCreated(path);
01134       // possible emit Change event after creation
01135     }
01136 
01137     if (event & Changed)
01138       c->instance->setDirty(path);
01139   }
01140 }
01141 
01142 // Remove entries which were marked to be removed
01143 void KDirWatchPrivate::slotRemoveDelayed()
01144 {
01145   delayRemove = false;
01146   // Removing an entry could also take care of removing its parent
01147   // (e.g. in FAM or inotify mode), which would remove other entries in removeList,
01148   // so don't use foreach or iterators here...
01149   while (!removeList.isEmpty()) {
01150     Entry* entry = *removeList.begin();
01151     removeEntry(0, entry, 0); // this will remove entry from removeList
01152   }
01153 }
01154 
01155 /* Scan all entries to be watched for changes. This is done regularly
01156  * when polling. This is NOT used by FAM.
01157  */
01158 void KDirWatchPrivate::slotRescan()
01159 {
01160   EntryMap::Iterator it;
01161 
01162   // People can do very long things in the slot connected to dirty(),
01163   // like showing a message box. We don't want to keep polling during
01164   // that time, otherwise the value of 'delayRemove' will be reset.
01165   bool timerRunning = timer.isActive();
01166   if ( timerRunning )
01167     timer.stop();
01168 
01169   // We delay deletions of entries this way.
01170   // removeDir(), when called in slotDirty(), can cause a crash otherwise
01171   delayRemove = true;
01172 
01173   if (rescan_all)
01174   {
01175     // mark all as dirty
01176     it = m_mapEntries.begin();
01177     for( ; it != m_mapEntries.end(); ++it )
01178       (*it).dirty = true;
01179     rescan_all = false;
01180   }
01181   else
01182   {
01183     // progate dirty flag to dependant entries (e.g. file watches)
01184     it = m_mapEntries.begin();
01185     for( ; it != m_mapEntries.end(); ++it )
01186       if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
01187         (*it).propagate_dirty();
01188   }
01189 
01190 #ifdef HAVE_SYS_INOTIFY_H
01191   QList<Entry*> dList, cList;
01192 #endif
01193 
01194   it = m_mapEntries.begin();
01195   for( ; it != m_mapEntries.end(); ++it ) {
01196     // we don't check invalid entries (i.e. remove delayed)
01197     if (!(*it).isValid()) continue;
01198 
01199     int ev = scanEntry( &(*it) );
01200 
01201 #ifdef HAVE_SYS_INOTIFY_H
01202     if ((*it).m_mode == INotifyMode) {
01203       if ( ev == Deleted ) {
01204         addEntry(0, QDir::cleanPath( ( *it ).path+"/.."), &*it, true);
01205       }
01206     }
01207     if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
01208       cList.append( &(*it) );
01209       if (! useINotify( &(*it) )) {
01210         useStat( &(*it) );
01211       }
01212     }
01213 #endif
01214 
01215     if ( ev != NoChange )
01216       emitEvent( &(*it), ev);
01217   }
01218 
01219 
01220   if ( timerRunning )
01221     timer.start(freq);
01222 
01223 #ifdef HAVE_SYS_INOTIFY_H
01224   // Remove watch of parent of new created directories
01225   Q_FOREACH(Entry* e, cList)
01226     removeEntry(0, QDir::cleanPath( e->path+"/.."), e);
01227 #endif
01228 
01229   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01230 }
01231 
01232 bool KDirWatchPrivate::isNoisyFile( const char * filename )
01233 {
01234   // $HOME/.X.err grows with debug output, so don't notify change
01235   if ( *filename == '.') {
01236     if (strncmp(filename, ".X.err", 6) == 0) return true;
01237     if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
01238     // fontconfig updates the cache on every KDE app start
01239     // (inclusive kio_thumbnail slaves)
01240     if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
01241   }
01242 
01243   return false;
01244 }
01245 
01246 #ifdef HAVE_FAM
01247 void KDirWatchPrivate::famEventReceived()
01248 {
01249   static FAMEvent fe;
01250 
01251   delayRemove = true;
01252 
01253   //kDebug(7001) << "Fam event received";
01254 
01255   while(use_fam && FAMPending(&fc)) {
01256     if (FAMNextEvent(&fc, &fe) == -1) {
01257       kWarning(7001) << "FAM connection problem, switching to polling.";
01258       use_fam = false;
01259       delete sn; sn = 0;
01260 
01261       // Replace all FAMMode entries with DNotify/Stat
01262       EntryMap::Iterator it;
01263       it = m_mapEntries.begin();
01264       for( ; it != m_mapEntries.end(); ++it )
01265         if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01266 #ifdef HAVE_SYS_INOTIFY_H
01267           if (useINotify( &(*it) )) continue;
01268 #endif
01269           useStat( &(*it) );
01270         }
01271     }
01272     else
01273       checkFAMEvent(&fe);
01274   }
01275 
01276   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01277 }
01278 
01279 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01280 {
01281   //kDebug(7001);
01282 
01283   // Don't be too verbose ;-)
01284   if ((fe->code == FAMExists) ||
01285       (fe->code == FAMEndExist) ||
01286       (fe->code == FAMAcknowledge)) return;
01287 
01288   if ( isNoisyFile( fe->filename ) )
01289     return;
01290 
01291   Entry* e = 0;
01292   EntryMap::Iterator it = m_mapEntries.begin();
01293   for( ; it != m_mapEntries.end(); ++it )
01294     if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01295        FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01296       e = &(*it);
01297       break;
01298     }
01299 
01300   // Entry* e = static_cast<Entry*>(fe->userdata);
01301 
01302 #if 0 // #88538
01303   kDebug(7001)  << "Processing FAM event ("
01304                 << ((fe->code == FAMChanged) ? "FAMChanged" :
01305                     (fe->code == FAMDeleted) ? "FAMDeleted" :
01306                     (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01307                     (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01308                     (fe->code == FAMCreated) ? "FAMCreated" :
01309                     (fe->code == FAMMoved) ? "FAMMoved" :
01310                     (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01311                     (fe->code == FAMExists) ? "FAMExists" :
01312                     (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01313                 << ", " << fe->filename
01314                 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) << ")";
01315 #endif
01316 
01317   if (!e) {
01318     // this happens e.g. for FAMAcknowledge after deleting a dir...
01319     //    kDebug(7001) << "No entry for FAM event ?!";
01320     return;
01321   }
01322 
01323   if (e->m_status == NonExistent) {
01324     kDebug(7001) << "FAM event for nonExistent entry " << e->path;
01325     return;
01326   }
01327 
01328   // Delayed handling. This rechecks changes with own stat calls.
01329   e->dirty = true;
01330   if (!rescan_timer.isActive())
01331     rescan_timer.start(m_PollInterval); // singleshot
01332 
01333   // needed FAM control actions on FAM events
01334   if (e->isDir)
01335     switch (fe->code)
01336     {
01337       case FAMDeleted:
01338        // file absolute: watched dir
01339         if (!QDir::isRelativePath(fe->filename))
01340         {
01341           // a watched directory was deleted
01342 
01343           e->m_status = NonExistent;
01344           FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
01345           kDebug(7001)  << "Cancelled FAMReq"
01346                         << FAMREQUEST_GETREQNUM(&(e->fr))
01347                         << "for" << e->path;
01348           // Scan parent for a new creation
01349           addEntry(0, QDir::cleanPath( e->path+"/.."), e, true);
01350         }
01351         break;
01352 
01353       case FAMCreated: {
01354           // check for creation of a directory we have to watch
01355         QByteArray tpath(QFile::encodeName(e->path + '/' +
01356                                            fe->filename));
01357 
01358         Entry* sub_entry = 0;
01359         foreach(sub_entry, e->m_entries)
01360           if (sub_entry->path == tpath) break;
01361 
01362         if (sub_entry && sub_entry->isDir) {
01363           removeEntry(0, e, sub_entry);
01364           sub_entry->m_status = Normal;
01365           if (!useFAM(sub_entry)) {
01366 #ifdef HAVE_SYS_INOTIFY_H
01367             if (!useINotify(sub_entry ))
01368 #endif
01369               useStat(sub_entry);
01370           }
01371         }
01372         else if ((sub_entry == 0) && (!e->m_clients.empty())) {
01373           Client* client = 0;
01374 
01375           KDE_struct_stat stat_buf;
01376           KDE_stat(tpath, &stat_buf);
01377           bool isDir = S_ISDIR(stat_buf.st_mode);
01378 
01379           KDirWatch::WatchModes flag;
01380           flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
01381 
01382           int counter = 0;
01383           Q_FOREACH(client, e->m_clients) {
01384             if (client->m_watchModes & flag) {
01385               addEntry (client->instance, tpath, 0, isDir,
01386                         isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
01387               counter++;
01388             }
01389           }
01390 
01391           if (counter != 0)
01392             emitEvent (e, Created, tpath);
01393 
01394           QString msg (QString::number(counter));
01395           msg += " instance/s monitoring the new ";
01396           msg += (isDir ? "dir " : "file ") + tpath;
01397           kDebug(7001) << msg;
01398         }
01399       }
01400         break;
01401       default:
01402         break;
01403     }
01404 }
01405 #else
01406 void KDirWatchPrivate::famEventReceived()
01407 {
01408     kWarning (7001) << "Fam event received but FAM is not supported";
01409 }
01410 #endif
01411 
01412 
01413 void KDirWatchPrivate::statistics()
01414 {
01415   EntryMap::Iterator it;
01416 
01417   kDebug(7001) << "Entries watched:";
01418   if (m_mapEntries.count()==0) {
01419     kDebug(7001) << "  None.";
01420   }
01421   else {
01422     it = m_mapEntries.begin();
01423     for( ; it != m_mapEntries.end(); ++it ) {
01424       Entry* e = &(*it);
01425       kDebug(7001)  << "  " << e->path << " ("
01426                     << ((e->m_status==Normal)?"":"Nonexistent ")
01427                     << (e->isDir ? "Dir":"File") << ", using "
01428                     << ((e->m_mode == FAMMode) ? "FAM" :
01429                         (e->m_mode == INotifyMode) ? "INotify" :
01430                         (e->m_mode == DNotifyMode) ? "DNotify" :
01431                         (e->m_mode == QFSWatchMode) ? "QFSWatch" :
01432                         (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01433                     << ")";
01434 
01435       foreach(Client* c, e->m_clients) {
01436         QString pending;
01437         if (c->watchingStopped) {
01438           if (c->pending & Deleted) pending += "deleted ";
01439           if (c->pending & Created) pending += "created ";
01440           if (c->pending & Changed) pending += "changed ";
01441           if (!pending.isEmpty()) pending = " (pending: " + pending + ')';
01442           pending = ", stopped" + pending;
01443         }
01444         kDebug(7001)  << "    by " << c->instance->objectName()
01445                       << " (" << c->count << " times)" << pending;
01446       }
01447       if (e->m_entries.count()>0) {
01448         kDebug(7001) << "    dependent entries:";
01449         foreach(Entry *d, e->m_entries) {
01450           kDebug(7001) << "      " << d->path;
01451         }
01452       }
01453     }
01454   }
01455 }
01456 
01457 #ifdef HAVE_QFILESYSTEMWATCHER
01458 // Slot for QFileSystemWatcher
01459 void KDirWatchPrivate::fswEventReceived(const QString &path)
01460 {
01461   EntryMap::Iterator it;
01462   it = m_mapEntries.find(path);
01463   if(it != m_mapEntries.end()) {
01464     Entry entry = *it;  // deep copy to not point to uninialized data (can happen inside emitEvent() )
01465     Entry *e = &entry;
01466     e->dirty = true;
01467     int ev = scanEntry(e);
01468     if (ev != NoChange)
01469       emitEvent(e, ev);
01470     if(ev == Deleted) {
01471       if (e->isDir)
01472         addEntry(0, QDir::cleanPath(e->path + "/.."), e, true);
01473       else
01474         addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
01475     } else
01476     if (ev == Changed && e->isDir && e->m_entries.count()) {
01477       Entry* sub_entry = 0;
01478       Q_FOREACH(sub_entry, e->m_entries) {
01479         if(e->isDir) {
01480           if (QFileInfo(sub_entry->path).isDir())
01481             break;
01482         } else {
01483           if (QFileInfo(sub_entry->path).isFile())
01484             break;
01485         }
01486       }
01487       if (sub_entry) {
01488         removeEntry(0, e, sub_entry);
01489         KDE_struct_stat stat_buf;
01490         QByteArray tpath = QFile::encodeName(path);
01491         KDE_stat(tpath, &stat_buf);
01492 
01493         if(!useQFSWatch(sub_entry))
01494 #ifdef HAVE_SYS_INOTIFY_H
01495           if(!useINotify(sub_entry))
01496 #endif
01497             useStat(sub_entry);
01498         fswEventReceived(sub_entry->path);
01499       }
01500     }
01501   }
01502 }
01503 #else
01504 void KDirWatchPrivate::fswEventReceived(const QString &path)
01505 {
01506     Q_UNUSED(path);
01507     kWarning (7001) << "QFileSystemWatcher event received but QFileSystemWatcher is not supported";
01508 }
01509 #endif    // HAVE_QFILESYSTEMWATCHER
01510 
01511 //
01512 // Class KDirWatch
01513 //
01514 
01515 K_GLOBAL_STATIC(KDirWatch, s_pKDirWatchSelf)
01516 KDirWatch* KDirWatch::self()
01517 {
01518   return s_pKDirWatchSelf;
01519 }
01520 
01521 bool KDirWatch::exists()
01522 {
01523   return s_pKDirWatchSelf != 0;
01524 }
01525 
01526 KDirWatch::KDirWatch (QObject* parent)
01527   : QObject(parent), d(createPrivate())
01528 {
01529   static int nameCounter = 0;
01530 
01531   nameCounter++;
01532   setObjectName(QString("KDirWatch-%1").arg(nameCounter) );
01533 
01534   d->ref();
01535 
01536   d->_isStopped = false;
01537 }
01538 
01539 KDirWatch::~KDirWatch()
01540 {
01541   d->removeEntries(this);
01542   if ( d->deref() )
01543   {
01544     // delete it if it's the last one
01545     delete d;
01546     dwp_self = 0;
01547   }
01548 }
01549 
01550 void KDirWatch::addDir( const QString& _path, WatchModes watchModes)
01551 {
01552   if (d) d->addEntry(this, _path, 0, true, watchModes);
01553 }
01554 
01555 void KDirWatch::addFile( const QString& _path )
01556 {
01557   if (d) d->addEntry(this, _path, 0, false);
01558 }
01559 
01560 QDateTime KDirWatch::ctime( const QString &_path ) const
01561 {
01562   KDirWatchPrivate::Entry* e = d->entry(_path);
01563 
01564   if (!e)
01565     return QDateTime();
01566 
01567   QDateTime result;
01568   result.setTime_t(e->m_ctime);
01569   return result;
01570 }
01571 
01572 void KDirWatch::removeDir( const QString& _path )
01573 {
01574   if (d) d->removeEntry(this, _path, 0);
01575 }
01576 
01577 void KDirWatch::removeFile( const QString& _path )
01578 {
01579   if (d) d->removeEntry(this, _path, 0);
01580 }
01581 
01582 bool KDirWatch::stopDirScan( const QString& _path )
01583 {
01584   if (d) {
01585     KDirWatchPrivate::Entry *e = d->entry(_path);
01586     if (e && e->isDir) return d->stopEntryScan(this, e);
01587   }
01588   return false;
01589 }
01590 
01591 bool KDirWatch::restartDirScan( const QString& _path )
01592 {
01593   if (d) {
01594     KDirWatchPrivate::Entry *e = d->entry(_path);
01595     if (e && e->isDir)
01596       // restart without notifying pending events
01597       return d->restartEntryScan(this, e, false);
01598   }
01599   return false;
01600 }
01601 
01602 void KDirWatch::stopScan()
01603 {
01604   if (d) {
01605     d->stopScan(this);
01606     d->_isStopped = true;
01607   }
01608 }
01609 
01610 bool KDirWatch::isStopped()
01611 {
01612   return d->_isStopped;
01613 }
01614 
01615 void KDirWatch::startScan( bool notify, bool skippedToo )
01616 {
01617   if (d) {
01618     d->_isStopped = false;
01619     d->startScan(this, notify, skippedToo);
01620   }
01621 }
01622 
01623 
01624 bool KDirWatch::contains( const QString& _path ) const
01625 {
01626   KDirWatchPrivate::Entry* e = d->entry(_path);
01627   if (!e)
01628      return false;
01629 
01630   foreach(KDirWatchPrivate::Client* client, e->m_clients) {
01631     if (client->instance == this)
01632       return true;
01633   }
01634 
01635   return false;
01636 }
01637 
01638 void KDirWatch::statistics()
01639 {
01640   if (!dwp_self) {
01641     kDebug(7001) << "KDirWatch not used";
01642     return;
01643   }
01644   dwp_self->statistics();
01645 }
01646 
01647 
01648 void KDirWatch::setCreated( const QString & _file )
01649 {
01650   kDebug(7001) << objectName() << "emitting created" << _file;
01651   emit created( _file );
01652 }
01653 
01654 void KDirWatch::setDirty( const QString & _file )
01655 {
01656   kDebug(7001) << objectName() << "emitting dirty" << _file;
01657   emit dirty( _file );
01658 }
01659 
01660 void KDirWatch::setDeleted( const QString & _file )
01661 {
01662   kDebug(7001) << objectName() << "emitting deleted" << _file;
01663   emit deleted( _file );
01664 }
01665 
01666 KDirWatch::Method KDirWatch::internalMethod()
01667 {
01668 #ifdef HAVE_FAM
01669   if (d->use_fam)
01670     return KDirWatch::FAM;
01671 #endif
01672 #ifdef HAVE_SYS_INOTIFY_H
01673   if (d->supports_inotify)
01674     return KDirWatch::INotify;
01675 #endif
01676   return KDirWatch::Stat;
01677 }
01678 
01679 
01680 #include "kdirwatch.moc"
01681 #include "kdirwatch_p.moc"
01682 
01683 //sven
01684 
01685 // vim: sw=2 ts=8 et

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal