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

kio

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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