summaryrefslogtreecommitdiffabout
path: root/microkde/kio/kio/kdirwatch.cpp
Side-by-side diff
Diffstat (limited to 'microkde/kio/kio/kdirwatch.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--microkde/kio/kio/kdirwatch.cpp12
1 files changed, 7 insertions, 5 deletions
diff --git a/microkde/kio/kio/kdirwatch.cpp b/microkde/kio/kio/kdirwatch.cpp
index 1596d1f..5f07c54 100644
--- a/microkde/kio/kio/kdirwatch.cpp
+++ b/microkde/kio/kio/kdirwatch.cpp
@@ -1,283 +1,285 @@
// -*- c-basic-offset: 2 -*-
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/*
Enhanced Version of the file for platform independent KDE tools.
Copyright (c) 2004 Ulf Schenk
$Id$
*/
// CHANGES:
// Februar 2002 - Add file watching and remote mount check for STAT
// Mar 30, 2001 - Native support for Linux dir change notification.
// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
// May 23. 1998 - Removed static pointer - you can have more instances.
// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
// call (or need) KFM. No more URL's - just plain paths. (sven)
// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
// deep copies for list of dirs. (sven)
// Mar 28. 1998 - Created. (sven)
//US #include <config.h>
#ifdef HAVE_DNOTIFY
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#endif
#include <sys/stat.h>
#include <assert.h>
#include <qdir.h>
#include <qfile.h>
-#include <qintdict.h>
-#include <qptrlist.h>
+#include <q3intdict.h>
+#include <q3ptrlist.h>
#include <qsocketnotifier.h>
#include <qstringlist.h>
#include <qtimer.h>
+//Added by qt3to4:
+#include <Q3CString>
#include <kapplication.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kconfigbase.h>
#include <kglobal.h>
#include <kstaticdeleter.h>
#include "kdirwatch.h"
#include "kdirwatch_p.h"
//US #include "global.h" // KIO::probably_slow_mounted
#define NO_NOTIFY (time_t) 0
static KDirWatchPrivate* dwp_self = 0;
#ifdef HAVE_DNOTIFY
#include <sys/utsname.h>
static int dnotify_signal = 0;
/* DNOTIFY signal handler
*
* As this is called asynchronously, only a flag is set and
* a rescan is requested.
* This is done by writing into a pipe to trigger a QSocketNotifier
* watching on this pipe: a timer is started and after a timeout,
* the rescan is done.
*/
void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
{
// write might change errno, we have to save it and restore it
// (Richard Stevens, Advanced programming in the Unix Environment)
int saved_errno = errno;
Entry* e = (dwp_self) ? dwp_self->fd_Entry.find(si->si_fd) :0;
// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
// << QString(e ? e->path:"unknown") << endl;
if(!e || e->dn_fd != si->si_fd) {
qDebug("fatal error in KDirWatch");
} else
e->dn_dirty = true;
char c = 0;
write(dwp_self->mPipe[1], &c, 1);
errno = saved_errno;
}
static struct sigaction old_sigio_act;
/* DNOTIFY SIGIO signal handler
*
* When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
*/
void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
{
// write might change errno, we have to save it and restore it
// (Richard Stevens, Advanced programming in the Unix Environment)
int saved_errno = errno;
if (dwp_self)
dwp_self->rescan_all = true;
char c = 0;
write(dwp_self->mPipe[1], &c, 1);
errno = saved_errno;
// Call previous signal handler
if (old_sigio_act.sa_flags & SA_SIGINFO)
{
if (old_sigio_act.sa_sigaction)
(*old_sigio_act.sa_sigaction)(sig, si, p);
}
else
{
if ((old_sigio_act.sa_handler != SIG_DFL) &&
(old_sigio_act.sa_handler != SIG_IGN))
(*old_sigio_act.sa_handler)(sig);
}
}
#endif
//
// Class KDirWatchPrivate (singleton)
//
/* All entries (files/directories) to be watched in the
* application (coming from multiple KDirWatch instances)
* are registered in a single KDirWatchPrivate instance.
*
* At the moment, the following methods for file watching
* are supported:
* - Polling: All files to be watched are polled regularly
* using stat (more precise: QFileInfo.lastModified()).
* The polling frequency is determined from global kconfig
* settings, defaulting to 500 ms for local directories
* and 5000 ms for remote mounts
* - FAM (File Alternation Monitor): first used on IRIX, SGI
* has ported this method to LINUX. It uses a kernel part
* (IMON, sending change events to /dev/imon) and a user
* level damon (fam), to which applications connect for
* notification of file changes. For NFS, the fam damon
* on the NFS server machine is used; if IMON is not built
* into the kernel, fam uses polling for local files.
* - DNOTIFY: In late LINUX 2.3.x, directory notification was
* introduced. By opening a directory, you can request for
* UNIX signals to be sent to the process when a directory
* is changed.
*/
KDirWatchPrivate::KDirWatchPrivate()
{
timer = new QTimer(this);
connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
freq = 3600000; // 1 hour as upper bound
statEntries = 0;
delayRemove = false;
m_ref = 0;
//US KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
//US m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
//US m_PollInterval = config.readNumEntry("PollInterval", 500);
KConfig *config = KGlobal::config();
- KConfigGroupSaver saver( config, QCString("DirWatch") );
+ KConfigGroupSaver saver( config, Q3CString("DirWatch") );
m_nfsPollInterval = config->readNumEntry("NFSPollInterval", 5000);
m_PollInterval = config->readNumEntry("PollInterval", 500);
QString available("Stat");
#ifdef HAVE_FAM
// It's possible that FAM server can't be started
if (FAMOpen(&fc) ==0) {
available += ", FAM";
use_fam=true;
sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
QSocketNotifier::Read, this);
connect( sn, SIGNAL(activated(int)),
this, SLOT(famEventReceived()) );
}
else {
kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
use_fam=false;
}
#endif
#ifdef HAVE_DNOTIFY
supports_dnotify = true; // not guilty until proven guilty
rescan_all = false;
struct utsname uts;
int major, minor, patch;
if (uname(&uts) < 0)
supports_dnotify = false; // *shrug*
else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
supports_dnotify = false; // *shrug*
else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
supports_dnotify = false;
}
if( supports_dnotify ) {
available += ", DNotify";
pipe(mPipe);
fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan()));
struct sigaction act;
act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
if( dnotify_signal == 0 )
dnotify_signal = SIGRTMIN + 8;
sigaction(dnotify_signal, &act, NULL);
act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
sigaction(SIGIO, &act, &old_sigio_act);
}
#endif
kdDebug(7001) << "Available methods: " << available << endl;
}
/* This should never be called, but doesn't harm */
KDirWatchPrivate::~KDirWatchPrivate()
{
timer->stop();
/* remove all entries being watched */
removeEntries(0);
#ifdef HAVE_FAM
if (use_fam) {
FAMClose(&fc);
kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
}
#endif
}
#ifdef HAVE_DNOTIFY
void KDirWatchPrivate::slotActivated()
{
char dummy_buf[100];
read(mPipe[0], &dummy_buf, 100);
if (!mTimer.isActive())
mTimer.start(200, true);
}
/* In DNOTIFY mode, only entries which are marked dirty are scanned.
* We first need to mark all yet nonexistant, but possible created
* entries as dirty...
*/
@@ -638,193 +640,193 @@ void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
#ifdef HAVE_DNOTIFY
if (useDNotify(e)) return;
#endif
useStat(e);
}
void KDirWatchPrivate::removeEntry( KDirWatch* instance,
const QString& _path, Entry* sub_entry )
{
Entry* e = entry(_path);
if (!e) {
kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
return;
}
if (sub_entry)
e->m_entries.removeRef(sub_entry);
else
e->removeClient(instance);
if (e->m_clients.count() || e->m_entries.count())
return;
if (delayRemove) {
// removeList is allowed to contain any entry at most once
if (removeList.findRef(e)==-1)
removeList.append(e);
// now e->isValid() is false
return;
}
#ifdef HAVE_FAM
if (e->m_mode == FAMMode) {
if ( e->m_status == Normal) {
FAMCancelMonitor(&fc, &(e->fr) );
kdDebug(7001) << "Cancelled FAM (Req "
<< FAMREQUEST_GETREQNUM(&(e->fr))
<< ") for " << e->path << endl;
}
else {
if (e->isDir)
removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
else
removeEntry(0, QFileInfo(e->path).dirPath(true), e);
}
}
#endif
#ifdef HAVE_DNOTIFY
if (e->m_mode == DNotifyMode) {
if (!e->isDir) {
removeEntry(0, QFileInfo(e->path).dirPath(true), e);
}
else { // isDir
// must close the FD.
if ( e->m_status == Normal) {
if (e->dn_fd) {
::close(e->dn_fd);
fd_Entry.remove(e->dn_fd);
kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
<< ") for " << e->path << endl;
e->dn_fd = 0;
}
}
else {
removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
}
}
}
#endif
if (e->m_mode == StatMode) {
statEntries--;
if ( statEntries == 0 ) {
timer->stop(); // stop timer if lists are empty
kdDebug(7001) << " Stopped Polling Timer" << endl;
}
}
kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
<< (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
<< (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
<< endl;
m_mapEntries.remove( e->path ); // <e> not valid any more
}
/* Called from KDirWatch destructor:
* remove <instance> as client from all entries
*/
void KDirWatchPrivate::removeEntries( KDirWatch* instance )
{
- QPtrList<Entry> list;
+ Q3PtrList<Entry> list;
int minfreq = 3600000;
// put all entries where instance is a client in list
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
Client* c = (*it).m_clients.first();
for(;c;c=(*it).m_clients.next())
if (c->instance == instance) break;
if (c) {
c->count = 1; // forces deletion of instance as client
list.append(&(*it));
}
else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
minfreq = (*it).freq;
}
for(Entry* e=list.first();e;e=list.next())
removeEntry(instance, e->path, 0);
if (minfreq > freq) {
// we can decrease the global polling frequency
freq = minfreq;
if (timer->isActive()) timer->changeInterval(freq);
kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
}
}
// instance ==0: stop scanning for all instances
bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
{
int stillWatching = 0;
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (!instance || instance == c->instance)
c->watchingStopped = true;
else if (!c->watchingStopped)
stillWatching += c->count;
}
kdDebug(7001) << instance->name() << " stopped scanning " << e->path
<< " (now " << stillWatching << " watchers)" << endl;
if (stillWatching == 0) {
// if nobody is interested, we don't watch
e->m_ctime = invalid_ctime; // invalid
// e->m_status = Normal;
}
return true;
}
// instance ==0: start scanning for all instances
bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
bool notify)
{
int wasWatching = 0, newWatching = 0;
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (!c->watchingStopped)
wasWatching += c->count;
else if (!instance || instance == c->instance) {
c->watchingStopped = false;
newWatching += c->count;
}
}
if (newWatching == 0)
return false;
kdDebug(7001) << instance->name() << " restarted scanning " << e->path
<< " (now " << wasWatching+newWatching << " watchers)" << endl;
// restart watching and emit pending events
int ev = NoChange;
if (wasWatching == 0) {
if (!notify) {
struct stat stat_buf;
bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
if (exists) {
e->m_ctime = stat_buf.st_ctime;
e->m_status = Normal;
e->m_nlink = stat_buf.st_nlink;
}
else {
e->m_ctime = invalid_ctime;
e->m_status = NonExistent;
e->m_nlink = 0;
}
}
e->msecLeft = 0;
ev = scanEntry(e);
}
emitEvent(e,ev);
return true;
}
@@ -914,193 +916,193 @@ int KDirWatchPrivate::scanEntry(Entry* e)
e->m_nlink = stat_buf.st_nlink;
return Changed;
}
return NoChange;
}
// dir/file doesn't exist
if (e->m_ctime == invalid_ctime)
return NoChange;
e->m_ctime = invalid_ctime;
e->m_nlink = 0;
e->m_status = NonExistent;
return Deleted;
}
/* Notify all interested KDirWatch instances about a given event on an entry
* and stored pending events. When watching is stopped, the event is
* added to the pending events.
*/
void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
{
QString path = e->path;
if (!fileName.isEmpty()) {
if (fileName[0] == '/')
path = fileName;
else
path += "/" + fileName;
}
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (c->instance==0 || c->count==0) continue;
if (c->watchingStopped) {
// add event to pending...
if (event == Changed)
c->pending |= event;
else if (event == Created || event == Deleted)
c->pending = event;
continue;
}
// not stopped
if (event == NoChange || event == Changed)
event |= c->pending;
c->pending = NoChange;
if (event == NoChange) continue;
if (event & Deleted) {
c->instance->setDeleted(path);
// emit only Deleted event...
continue;
}
if (event & Created) {
c->instance->setCreated(path);
// possible emit Change event after creation
}
if (event & Changed)
c->instance->setDirty(path);
}
}
// Remove entries which were marked to be removed
void KDirWatchPrivate::slotRemoveDelayed()
{
Entry* e;
delayRemove = false;
for(e=removeList.first();e;e=removeList.next())
removeEntry(0, e->path, 0);
removeList.clear();
}
/* Scan all entries to be watched for changes. This is done regularly
* when polling and once after a DNOTIFY signal. This is NOT used by FAM.
*/
void KDirWatchPrivate::slotRescan()
{
EntryMap::Iterator it;
// People can do very long things in the slot connected to dirty(),
// like showing a message box. We don't want to keep polling during
// that time, otherwise the value of 'delayRemove' will be reset.
bool timerRunning = timer->isActive();
if ( timerRunning )
timer->stop();
// We delay deletions of entries this way.
// removeDir(), when called in slotDirty(), can cause a crash otherwise
delayRemove = true;
#ifdef HAVE_DNOTIFY
- QPtrList<Entry> dList, cList;
+ Q3PtrList<Entry> dList, cList;
// for DNotify method,
if (rescan_all)
{
// mark all as dirty
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
(*it).dn_dirty = true;
rescan_all = false;
}
else
{
// progate dirty flag to dependant entries (e.g. file watches)
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
(*it).propagate_dirty();
}
#endif
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
// we don't check invalid entries (i.e. remove delayed)
if (!(*it).isValid()) continue;
int ev = scanEntry( &(*it) );
#ifdef HAVE_DNOTIFY
if ((*it).m_mode == DNotifyMode) {
if ((*it).isDir && (ev == Deleted)) {
dList.append( &(*it) );
// must close the FD.
if ((*it).dn_fd) {
::close((*it).dn_fd);
fd_Entry.remove((*it).dn_fd);
(*it).dn_fd = 0;
}
}
else if ((*it).isDir && (ev == Created)) {
// For created, but yet without DNOTIFYing ...
if ( (*it).dn_fd == 0) {
cList.append( &(*it) );
if (! useDNotify( &(*it) )) {
// if DNotify setup fails...
useStat( &(*it) );
}
}
}
}
#endif
if ( ev != NoChange )
emitEvent( &(*it), ev);
}
#ifdef HAVE_DNOTIFY
// Scan parent of deleted directories for new creation
Entry* e;
for(e=dList.first();e;e=dList.next())
addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
// Remove watch of parent of new created directories
for(e=cList.first();e;e=cList.next())
removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
#endif
if ( timerRunning )
timer->start(freq);
QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
}
#ifdef HAVE_FAM
void KDirWatchPrivate::famEventReceived()
{
static FAMEvent fe;
delayRemove = true;
while(use_fam && FAMPending(&fc)) {
if (FAMNextEvent(&fc, &fe) == -1) {
kdWarning(7001) << "FAM connection problem, switching to polling."
<< endl;
use_fam = false;
delete sn; sn = 0;
// Replace all FAMMode entries with DNotify/Stat
EntryMap::Iterator it;
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
#ifdef HAVE_DNOTIFY