kio Library API Documentation

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