• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

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

tdeio/tdeio

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

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.