• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

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

tdecore

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

tdecore

Skip menu "tdecore"
  • 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 tdecore by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.