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->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 // used for FAM and DNOTIFY 00185 rescan_all = false; 00186 connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan())); 00187 00188 #ifdef HAVE_FAM 00189 // It's possible that FAM server can't be started 00190 if (FAMOpen(&fc) ==0) { 00191 available += ", FAM"; 00192 use_fam=true; 00193 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc), 00194 QSocketNotifier::Read, this); 00195 connect( sn, SIGNAL(activated(int)), 00196 this, SLOT(famEventReceived()) ); 00197 } 00198 else { 00199 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl; 00200 use_fam=false; 00201 } 00202 #endif 00203 00204 #ifdef HAVE_DNOTIFY 00205 supports_dnotify = true; // not guilty until proven guilty 00206 struct utsname uts; 00207 int major, minor, patch; 00208 if (uname(&uts) < 0) 00209 supports_dnotify = false; // *shrug* 00210 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) 00211 supports_dnotify = false; // *shrug* 00212 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19 00213 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl; 00214 supports_dnotify = false; 00215 } 00216 00217 if( supports_dnotify ) { 00218 available += ", DNotify"; 00219 00220 pipe(mPipe); 00221 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC); 00222 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC); 00223 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this); 00224 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated())); 00225 // Install the signal handler only once 00226 if ( dnotify_signal == 0 ) 00227 { 00228 dnotify_signal = SIGRTMIN + 8; 00229 00230 struct sigaction act; 00231 act.sa_sigaction = KDirWatchPrivate::dnotify_handler; 00232 sigemptyset(&act.sa_mask); 00233 act.sa_flags = SA_SIGINFO; 00234 #ifdef SA_RESTART 00235 act.sa_flags |= SA_RESTART; 00236 #endif 00237 sigaction(dnotify_signal, &act, NULL); 00238 00239 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler; 00240 sigaction(SIGIO, &act, &old_sigio_act); 00241 } 00242 } 00243 else 00244 { 00245 mPipe[0] = -1; 00246 mPipe[1] = -1; 00247 } 00248 #endif 00249 00250 kdDebug(7001) << "Available methods: " << available << endl; 00251 } 00252 00253 /* This should never be called, but doesn't harm */ 00254 KDirWatchPrivate::~KDirWatchPrivate() 00255 { 00256 timer->stop(); 00257 00258 /* remove all entries being watched */ 00259 removeEntries(0); 00260 00261 #ifdef HAVE_FAM 00262 if (use_fam) { 00263 FAMClose(&fc); 00264 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl; 00265 } 00266 #endif 00267 #ifdef HAVE_DNOTIFY 00268 close(mPipe[0]); 00269 close(mPipe[1]); 00270 #endif 00271 } 00272 00273 void KDirWatchPrivate::slotActivated() 00274 { 00275 #ifdef HAVE_DNOTIFY 00276 char dummy_buf[100]; 00277 read(mPipe[0], &dummy_buf, 100); 00278 00279 if (!rescan_timer.isActive()) 00280 rescan_timer.start(m_PollInterval, true); 00281 #endif 00282 } 00283 00284 /* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned. 00285 * We first need to mark all yet nonexistent, but possible created 00286 * entries as dirty... 00287 */ 00288 void KDirWatchPrivate::Entry::propagate_dirty() 00289 { 00290 Entry* sub_entry; 00291 for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next()) 00292 { 00293 if (!sub_entry->dirty) 00294 { 00295 sub_entry->dirty = true; 00296 sub_entry->propagate_dirty(); 00297 } 00298 } 00299 } 00300 00301 00302 /* A KDirWatch instance is interested in getting events for 00303 * this file/Dir entry. 00304 */ 00305 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance) 00306 { 00307 Client* client = m_clients.first(); 00308 for(;client; client = m_clients.next()) 00309 if (client->instance == instance) break; 00310 00311 if (client) { 00312 client->count++; 00313 return; 00314 } 00315 00316 client = new Client; 00317 client->instance = instance; 00318 client->count = 1; 00319 client->watchingStopped = instance->isStopped(); 00320 client->pending = NoChange; 00321 00322 m_clients.append(client); 00323 } 00324 00325 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance) 00326 { 00327 Client* client = m_clients.first(); 00328 for(;client; client = m_clients.next()) 00329 if (client->instance == instance) break; 00330 00331 if (client) { 00332 client->count--; 00333 if (client->count == 0) { 00334 m_clients.removeRef(client); 00335 delete client; 00336 } 00337 } 00338 } 00339 00340 /* get number of clients */ 00341 int KDirWatchPrivate::Entry::clients() 00342 { 00343 int clients = 0; 00344 Client* client = m_clients.first(); 00345 for(;client; client = m_clients.next()) 00346 clients += client->count; 00347 00348 return clients; 00349 } 00350 00351 00352 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path) 00353 { 00354 // we only support absolute paths 00355 if (_path.left(1) != "/") { 00356 return 0; 00357 } 00358 00359 QString path = _path; 00360 00361 if ( path.length() > 1 && path.right(1) == "/" ) 00362 path.truncate( path.length() - 1 ); 00363 00364 EntryMap::Iterator it = m_mapEntries.find( path ); 00365 if ( it == m_mapEntries.end() ) 00366 return 0; 00367 else 00368 return &(*it); 00369 } 00370 00371 // set polling frequency for a entry and adjust global freq if needed 00372 void KDirWatchPrivate::useFreq(Entry* e, int newFreq) 00373 { 00374 e->freq = newFreq; 00375 00376 // a reasonable frequency for the global polling timer 00377 if (e->freq < freq) { 00378 freq = e->freq; 00379 if (timer->isActive()) timer->changeInterval(freq); 00380 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl; 00381 } 00382 } 00383 00384 00385 #if defined(HAVE_FAM) 00386 // setup FAM notification, returns false if not possible 00387 bool KDirWatchPrivate::useFAM(Entry* e) 00388 { 00389 if (!use_fam) return false; 00390 00391 e->m_mode = FAMMode; 00392 e->dirty = false; 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->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 QCString tpath = QFile::encodeName(path); 00591 bool exists = (stat(tpath, &stat_buf) == 0); 00592 00593 Entry newEntry; 00594 m_mapEntries.insert( path, newEntry ); 00595 // the insert does a copy, so we have to use <e> now 00596 Entry* e = &(m_mapEntries[path]); 00597 00598 if (exists) { 00599 e->isDir = S_ISDIR(stat_buf.st_mode); 00600 00601 if (e->isDir && !isDir) 00602 qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii()); 00603 else if (!e->isDir && isDir) 00604 qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii()); 00605 00606 e->m_ctime = stat_buf.st_ctime; 00607 e->m_status = Normal; 00608 e->m_nlink = stat_buf.st_nlink; 00609 } 00610 else { 00611 e->isDir = isDir; 00612 e->m_ctime = invalid_ctime; 00613 e->m_status = NonExistent; 00614 e->m_nlink = 0; 00615 } 00616 00617 e->path = path; 00618 if (sub_entry) 00619 e->m_entries.append(sub_entry); 00620 else 00621 e->addClient(instance); 00622 00623 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path 00624 << (e->m_status == NonExistent ? " NotExisting" : "") 00625 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) 00626 << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) 00627 << endl; 00628 00629 00630 // now setup the notification method 00631 e->m_mode = UnknownMode; 00632 e->msecLeft = 0; 00633 00634 if ( isNoisyFile( tpath ) ) 00635 return; 00636 00637 #if defined(HAVE_FAM) 00638 if (useFAM(e)) return; 00639 #endif 00640 00641 #ifdef HAVE_DNOTIFY 00642 if (useDNotify(e)) return; 00643 #endif 00644 00645 useStat(e); 00646 } 00647 00648 00649 void KDirWatchPrivate::removeEntry( KDirWatch* instance, 00650 const QString& _path, Entry* sub_entry ) 00651 { 00652 Entry* e = entry(_path); 00653 if (!e) { 00654 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl; 00655 return; 00656 } 00657 00658 if (sub_entry) 00659 e->m_entries.removeRef(sub_entry); 00660 else 00661 e->removeClient(instance); 00662 00663 if (e->m_clients.count() || e->m_entries.count()) 00664 return; 00665 00666 if (delayRemove) { 00667 // removeList is allowed to contain any entry at most once 00668 if (removeList.findRef(e)==-1) 00669 removeList.append(e); 00670 // now e->isValid() is false 00671 return; 00672 } 00673 00674 #ifdef HAVE_FAM 00675 if (e->m_mode == FAMMode) { 00676 if ( e->m_status == Normal) { 00677 FAMCancelMonitor(&fc, &(e->fr) ); 00678 kdDebug(7001) << "Cancelled FAM (Req " 00679 << FAMREQUEST_GETREQNUM(&(e->fr)) 00680 << ") for " << e->path << endl; 00681 } 00682 else { 00683 if (e->isDir) 00684 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); 00685 else 00686 removeEntry(0, QFileInfo(e->path).dirPath(true), e); 00687 } 00688 } 00689 #endif 00690 00691 #ifdef HAVE_DNOTIFY 00692 if (e->m_mode == DNotifyMode) { 00693 if (!e->isDir) { 00694 removeEntry(0, QFileInfo(e->path).dirPath(true), e); 00695 } 00696 else { // isDir 00697 // must close the FD. 00698 if ( e->m_status == Normal) { 00699 if (e->dn_fd) { 00700 ::close(e->dn_fd); 00701 fd_Entry.remove(e->dn_fd); 00702 00703 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd 00704 << ") for " << e->path << endl; 00705 e->dn_fd = 0; 00706 00707 } 00708 } 00709 else { 00710 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); 00711 } 00712 } 00713 } 00714 #endif 00715 00716 if (e->m_mode == StatMode) { 00717 statEntries--; 00718 if ( statEntries == 0 ) { 00719 timer->stop(); // stop timer if lists are empty 00720 kdDebug(7001) << " Stopped Polling Timer" << endl; 00721 } 00722 } 00723 00724 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path 00725 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) 00726 << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) 00727 << endl; 00728 m_mapEntries.remove( e->path ); // <e> not valid any more 00729 } 00730 00731 00732 /* Called from KDirWatch destructor: 00733 * remove <instance> as client from all entries 00734 */ 00735 void KDirWatchPrivate::removeEntries( KDirWatch* instance ) 00736 { 00737 QPtrList<Entry> list; 00738 int minfreq = 3600000; 00739 00740 // put all entries where instance is a client in list 00741 EntryMap::Iterator it = m_mapEntries.begin(); 00742 for( ; it != m_mapEntries.end(); ++it ) { 00743 Client* c = (*it).m_clients.first(); 00744 for(;c;c=(*it).m_clients.next()) 00745 if (c->instance == instance) break; 00746 if (c) { 00747 c->count = 1; // forces deletion of instance as client 00748 list.append(&(*it)); 00749 } 00750 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq ) 00751 minfreq = (*it).freq; 00752 } 00753 00754 for(Entry* e=list.first();e;e=list.next()) 00755 removeEntry(instance, e->path, 0); 00756 00757 if (minfreq > freq) { 00758 // we can decrease the global polling frequency 00759 freq = minfreq; 00760 if (timer->isActive()) timer->changeInterval(freq); 00761 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl; 00762 } 00763 } 00764 00765 // instance ==0: stop scanning for all instances 00766 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e) 00767 { 00768 int stillWatching = 0; 00769 Client* c = e->m_clients.first(); 00770 for(;c;c=e->m_clients.next()) { 00771 if (!instance || instance == c->instance) 00772 c->watchingStopped = true; 00773 else if (!c->watchingStopped) 00774 stillWatching += c->count; 00775 } 00776 00777 kdDebug(7001) << instance->name() << " stopped scanning " << e->path 00778 << " (now " << stillWatching << " watchers)" << endl; 00779 00780 if (stillWatching == 0) { 00781 // if nobody is interested, we don't watch 00782 e->m_ctime = invalid_ctime; // invalid 00783 // e->m_status = Normal; 00784 } 00785 return true; 00786 } 00787 00788 // instance ==0: start scanning for all instances 00789 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e, 00790 bool notify) 00791 { 00792 int wasWatching = 0, newWatching = 0; 00793 Client* c = e->m_clients.first(); 00794 for(;c;c=e->m_clients.next()) { 00795 if (!c->watchingStopped) 00796 wasWatching += c->count; 00797 else if (!instance || instance == c->instance) { 00798 c->watchingStopped = false; 00799 newWatching += c->count; 00800 } 00801 } 00802 if (newWatching == 0) 00803 return false; 00804 00805 kdDebug(7001) << instance->name() << " restarted scanning " << e->path 00806 << " (now " << wasWatching+newWatching << " watchers)" << endl; 00807 00808 // restart watching and emit pending events 00809 00810 int ev = NoChange; 00811 if (wasWatching == 0) { 00812 if (!notify) { 00813 struct stat stat_buf; 00814 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0); 00815 if (exists) { 00816 e->m_ctime = stat_buf.st_ctime; 00817 e->m_status = Normal; 00818 e->m_nlink = stat_buf.st_nlink; 00819 } 00820 else { 00821 e->m_ctime = invalid_ctime; 00822 e->m_status = NonExistent; 00823 e->m_nlink = 0; 00824 } 00825 } 00826 e->msecLeft = 0; 00827 ev = scanEntry(e); 00828 } 00829 emitEvent(e,ev); 00830 00831 return true; 00832 } 00833 00834 // instance ==0: stop scanning for all instances 00835 void KDirWatchPrivate::stopScan(KDirWatch* instance) 00836 { 00837 EntryMap::Iterator it = m_mapEntries.begin(); 00838 for( ; it != m_mapEntries.end(); ++it ) 00839 stopEntryScan(instance, &(*it)); 00840 } 00841 00842 00843 void KDirWatchPrivate::startScan(KDirWatch* instance, 00844 bool notify, bool skippedToo ) 00845 { 00846 if (!notify) 00847 resetList(instance,skippedToo); 00848 00849 EntryMap::Iterator it = m_mapEntries.begin(); 00850 for( ; it != m_mapEntries.end(); ++it ) 00851 restartEntryScan(instance, &(*it), notify); 00852 00853 // timer should still be running when in polling mode 00854 } 00855 00856 00857 // clear all pending events, also from stopped 00858 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, 00859 bool skippedToo ) 00860 { 00861 EntryMap::Iterator it = m_mapEntries.begin(); 00862 for( ; it != m_mapEntries.end(); ++it ) { 00863 00864 Client* c = (*it).m_clients.first(); 00865 for(;c;c=(*it).m_clients.next()) 00866 if (!c->watchingStopped || skippedToo) 00867 c->pending = NoChange; 00868 } 00869 } 00870 00871 // Return event happened on <e> 00872 // 00873 int KDirWatchPrivate::scanEntry(Entry* e) 00874 { 00875 #ifdef HAVE_FAM 00876 if (e->m_mode == FAMMode) { 00877 // we know nothing has changed, no need to stat 00878 if(!e->dirty) return NoChange; 00879 e->dirty = false; 00880 } 00881 #endif 00882 00883 // Shouldn't happen: Ignore "unknown" notification method 00884 if (e->m_mode == UnknownMode) return NoChange; 00885 00886 #ifdef HAVE_DNOTIFY 00887 if (e->m_mode == DNotifyMode) { 00888 // we know nothing has changed, no need to stat 00889 if(!e->dirty) return NoChange; 00890 e->dirty = false; 00891 } 00892 #endif 00893 00894 if (e->m_mode == StatMode) { 00895 // only scan if timeout on entry timer happens; 00896 // e.g. when using 500msec global timer, a entry 00897 // with freq=5000 is only watched every 10th time 00898 00899 e->msecLeft -= freq; 00900 if (e->msecLeft>0) return NoChange; 00901 e->msecLeft += e->freq; 00902 } 00903 00904 struct stat stat_buf; 00905 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0); 00906 if (exists) { 00907 00908 if (e->m_status == NonExistent) { 00909 e->m_ctime = stat_buf.st_ctime; 00910 e->m_status = Normal; 00911 e->m_nlink = stat_buf.st_nlink; 00912 return Created; 00913 } 00914 00915 if ( (e->m_ctime != invalid_ctime) && 00916 ((stat_buf.st_ctime != e->m_ctime) || 00917 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) { 00918 e->m_ctime = stat_buf.st_ctime; 00919 e->m_nlink = stat_buf.st_nlink; 00920 return Changed; 00921 } 00922 00923 return NoChange; 00924 } 00925 00926 // dir/file doesn't exist 00927 00928 if (e->m_ctime == invalid_ctime) 00929 return NoChange; 00930 00931 e->m_ctime = invalid_ctime; 00932 e->m_nlink = 0; 00933 e->m_status = NonExistent; 00934 00935 return Deleted; 00936 } 00937 00938 /* Notify all interested KDirWatch instances about a given event on an entry 00939 * and stored pending events. When watching is stopped, the event is 00940 * added to the pending events. 00941 */ 00942 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName) 00943 { 00944 QString path = e->path; 00945 if (!fileName.isEmpty()) { 00946 if (fileName[0] == '/') 00947 path = fileName; 00948 else 00949 path += "/" + fileName; 00950 } 00951 00952 Client* c = e->m_clients.first(); 00953 for(;c;c=e->m_clients.next()) { 00954 if (c->instance==0 || c->count==0) continue; 00955 00956 if (c->watchingStopped) { 00957 // add event to pending... 00958 if (event == Changed) 00959 c->pending |= event; 00960 else if (event == Created || event == Deleted) 00961 c->pending = event; 00962 continue; 00963 } 00964 // not stopped 00965 if (event == NoChange || event == Changed) 00966 event |= c->pending; 00967 c->pending = NoChange; 00968 if (event == NoChange) continue; 00969 00970 if (event & Deleted) { 00971 c->instance->setDeleted(path); 00972 // emit only Deleted event... 00973 continue; 00974 } 00975 00976 if (event & Created) { 00977 c->instance->setCreated(path); 00978 // possible emit Change event after creation 00979 } 00980 00981 if (event & Changed) 00982 c->instance->setDirty(path); 00983 } 00984 } 00985 00986 // Remove entries which were marked to be removed 00987 void KDirWatchPrivate::slotRemoveDelayed() 00988 { 00989 Entry* e; 00990 delayRemove = false; 00991 for(e=removeList.first();e;e=removeList.next()) 00992 removeEntry(0, e->path, 0); 00993 removeList.clear(); 00994 } 00995 00996 /* Scan all entries to be watched for changes. This is done regularly 00997 * when polling and once after a DNOTIFY signal. This is NOT used by FAM. 00998 */ 00999 void KDirWatchPrivate::slotRescan() 01000 { 01001 EntryMap::Iterator it; 01002 01003 // People can do very long things in the slot connected to dirty(), 01004 // like showing a message box. We don't want to keep polling during 01005 // that time, otherwise the value of 'delayRemove' will be reset. 01006 bool timerRunning = timer->isActive(); 01007 if ( timerRunning ) 01008 timer->stop(); 01009 01010 // We delay deletions of entries this way. 01011 // removeDir(), when called in slotDirty(), can cause a crash otherwise 01012 delayRemove = true; 01013 01014 #ifdef HAVE_DNOTIFY 01015 QPtrList<Entry> dList, cList; 01016 #endif 01017 01018 if (rescan_all) 01019 { 01020 // mark all as dirty 01021 it = m_mapEntries.begin(); 01022 for( ; it != m_mapEntries.end(); ++it ) 01023 (*it).dirty = true; 01024 rescan_all = false; 01025 } 01026 else 01027 { 01028 // progate dirty flag to dependant entries (e.g. file watches) 01029 it = m_mapEntries.begin(); 01030 for( ; it != m_mapEntries.end(); ++it ) 01031 if ( ((*it).m_mode == DNotifyMode) && (*it).dirty ) 01032 (*it).propagate_dirty(); 01033 } 01034 01035 it = m_mapEntries.begin(); 01036 for( ; it != m_mapEntries.end(); ++it ) { 01037 // we don't check invalid entries (i.e. remove delayed) 01038 if (!(*it).isValid()) continue; 01039 01040 int ev = scanEntry( &(*it) ); 01041 01042 #ifdef HAVE_DNOTIFY 01043 if ((*it).m_mode == DNotifyMode) { 01044 if ((*it).isDir && (ev == Deleted)) { 01045 dList.append( &(*it) ); 01046 01047 // must close the FD. 01048 if ((*it).dn_fd) { 01049 ::close((*it).dn_fd); 01050 fd_Entry.remove((*it).dn_fd); 01051 (*it).dn_fd = 0; 01052 } 01053 } 01054 01055 else if ((*it).isDir && (ev == Created)) { 01056 // For created, but yet without DNOTIFYing ... 01057 if ( (*it).dn_fd == 0) { 01058 cList.append( &(*it) ); 01059 if (! useDNotify( &(*it) )) { 01060 // if DNotify setup fails... 01061 useStat( &(*it) ); 01062 } 01063 } 01064 } 01065 } 01066 #endif 01067 01068 if ( ev != NoChange ) 01069 emitEvent( &(*it), ev); 01070 } 01071 01072 01073 #ifdef HAVE_DNOTIFY 01074 // Scan parent of deleted directories for new creation 01075 Entry* e; 01076 for(e=dList.first();e;e=dList.next()) 01077 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); 01078 01079 // Remove watch of parent of new created directories 01080 for(e=cList.first();e;e=cList.next()) 01081 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e); 01082 #endif 01083 01084 if ( timerRunning ) 01085 timer->start(freq); 01086 01087 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); 01088 } 01089 01090 bool KDirWatchPrivate::isNoisyFile( const char * filename ) 01091 { 01092 // $HOME/.X.err grows with debug output, so don't notify change 01093 if ( *filename == '.') { 01094 if (strncmp(filename, ".X.err", 6) == 0) return true; 01095 if (strncmp(filename, ".xsession-errors", 16) == 0) return true; 01096 // fontconfig updates the cache on every KDE app start 01097 // (inclusive kio_thumbnail slaves) 01098 if (strncmp(filename, ".fonts.cache", 12) == 0) return true; 01099 } 01100 01101 return false; 01102 } 01103 01104 #ifdef HAVE_FAM 01105 void KDirWatchPrivate::famEventReceived() 01106 { 01107 static FAMEvent fe; 01108 01109 delayRemove = true; 01110 01111 while(use_fam && FAMPending(&fc)) { 01112 if (FAMNextEvent(&fc, &fe) == -1) { 01113 kdWarning(7001) << "FAM connection problem, switching to polling." 01114 << endl; 01115 use_fam = false; 01116 delete sn; sn = 0; 01117 01118 // Replace all FAMMode entries with DNotify/Stat 01119 EntryMap::Iterator it; 01120 it = m_mapEntries.begin(); 01121 for( ; it != m_mapEntries.end(); ++it ) 01122 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) { 01123 #ifdef HAVE_DNOTIFY 01124 if (useDNotify( &(*it) )) continue; 01125 #endif 01126 useStat( &(*it) ); 01127 } 01128 } 01129 else 01130 checkFAMEvent(&fe); 01131 } 01132 01133 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); 01134 } 01135 01136 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe) 01137 { 01138 // Don't be too verbose ;-) 01139 if ((fe->code == FAMExists) || 01140 (fe->code == FAMEndExist) || 01141 (fe->code == FAMAcknowledge)) return; 01142 01143 if ( isNoisyFile( fe->filename ) ) 01144 return; 01145 01146 Entry* e = 0; 01147 EntryMap::Iterator it = m_mapEntries.begin(); 01148 for( ; it != m_mapEntries.end(); ++it ) 01149 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) == 01150 FAMREQUEST_GETREQNUM(&(fe->fr)) ) { 01151 e = &(*it); 01152 break; 01153 } 01154 01155 // Entry* e = static_cast<Entry*>(fe->userdata); 01156 01157 kdDebug(7001) << "Processing FAM event (" 01158 << ((fe->code == FAMChanged) ? "FAMChanged" : 01159 (fe->code == FAMDeleted) ? "FAMDeleted" : 01160 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" : 01161 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" : 01162 (fe->code == FAMCreated) ? "FAMCreated" : 01163 (fe->code == FAMMoved) ? "FAMMoved" : 01164 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" : 01165 (fe->code == FAMExists) ? "FAMExists" : 01166 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code") 01167 << ", " << fe->filename 01168 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) 01169 << ")" << endl; 01170 01171 if (!e) { 01172 // this happens e.g. for FAMAcknowledge after deleting a dir... 01173 // kdDebug(7001) << "No entry for FAM event ?!" << endl; 01174 return; 01175 } 01176 01177 if (e->m_status == NonExistent) { 01178 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl; 01179 return; 01180 } 01181 01182 // Delayed handling. This rechecks changes with own stat calls. 01183 e->dirty = true; 01184 if (!rescan_timer.isActive()) 01185 rescan_timer.start(m_PollInterval, true); 01186 01187 // needed FAM control actions on FAM events 01188 if (e->isDir) 01189 switch (fe->code) 01190 { 01191 case FAMDeleted: 01192 // file absolute: watched dir 01193 if (fe->filename[0] == '/') 01194 { 01195 // a watched directory was deleted 01196 01197 e->m_status = NonExistent; 01198 FAMCancelMonitor(&fc, &(e->fr) ); // needed ? 01199 kdDebug(7001) << "Cancelled FAMReq " 01200 << FAMREQUEST_GETREQNUM(&(e->fr)) 01201 << " for " << e->path << endl; 01202 // Scan parent for a new creation 01203 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); 01204 } 01205 break; 01206 01207 case FAMCreated: { 01208 // check for creation of a directory we have to watch 01209 Entry *sub_entry = e->m_entries.first(); 01210 for(;sub_entry; sub_entry = e->m_entries.next()) 01211 if (sub_entry->path == e->path + "/" + fe->filename) break; 01212 if (sub_entry && sub_entry->isDir) { 01213 QString path = e->path; 01214 removeEntry(0,e->path,sub_entry); // <e> can be invalid here!! 01215 sub_entry->m_status = Normal; 01216 if (!useFAM(sub_entry)) 01217 useStat(sub_entry); 01218 } 01219 break; 01220 } 01221 01222 default: 01223 break; 01224 } 01225 } 01226 #else 01227 void KDirWatchPrivate::famEventReceived() {} 01228 #endif 01229 01230 01231 void KDirWatchPrivate::statistics() 01232 { 01233 EntryMap::Iterator it; 01234 01235 kdDebug(7001) << "Entries watched:" << endl; 01236 if (m_mapEntries.count()==0) { 01237 kdDebug(7001) << " None." << endl; 01238 } 01239 else { 01240 it = m_mapEntries.begin(); 01241 for( ; it != m_mapEntries.end(); ++it ) { 01242 Entry* e = &(*it); 01243 kdDebug(7001) << " " << e->path << " (" 01244 << ((e->m_status==Normal)?"":"Nonexistent ") 01245 << (e->isDir ? "Dir":"File") << ", using " 01246 << ((e->m_mode == FAMMode) ? "FAM" : 01247 (e->m_mode == DNotifyMode) ? "DNotify" : 01248 (e->m_mode == StatMode) ? "Stat" : "Unknown Method") 01249 << ")" << endl; 01250 01251 Client* c = e->m_clients.first(); 01252 for(;c; c = e->m_clients.next()) { 01253 QString pending; 01254 if (c->watchingStopped) { 01255 if (c->pending & Deleted) pending += "deleted "; 01256 if (c->pending & Created) pending += "created "; 01257 if (c->pending & Changed) pending += "changed "; 01258 if (!pending.isEmpty()) pending = " (pending: " + pending + ")"; 01259 pending = ", stopped" + pending; 01260 } 01261 kdDebug(7001) << " by " << c->instance->name() 01262 << " (" << c->count << " times)" 01263 << pending << endl; 01264 } 01265 if (e->m_entries.count()>0) { 01266 kdDebug(7001) << " dependent entries:" << endl; 01267 Entry* d = e->m_entries.first(); 01268 for(;d; d = e->m_entries.next()) { 01269 kdDebug(7001) << " " << d->path << endl; 01270 } 01271 } 01272 } 01273 } 01274 } 01275 01276 01277 // 01278 // Class KDirWatch 01279 // 01280 01281 static KStaticDeleter<KDirWatch> sd_dw; 01282 KDirWatch* KDirWatch::s_pSelf = 0L; 01283 01284 KDirWatch* KDirWatch::self() 01285 { 01286 if ( !s_pSelf ) { 01287 sd_dw.setObject( s_pSelf, new KDirWatch ); 01288 } 01289 01290 return s_pSelf; 01291 } 01292 01293 bool KDirWatch::exists() 01294 { 01295 return s_pSelf != 0; 01296 } 01297 01298 KDirWatch::KDirWatch (QObject* parent, const char* name) 01299 : QObject(parent,name) 01300 { 01301 if (!name) { 01302 static int nameCounter = 0; 01303 01304 nameCounter++; 01305 setName(QString("KDirWatch-%1").arg(nameCounter).ascii()); 01306 } 01307 01308 if (!dwp_self) 01309 dwp_self = new KDirWatchPrivate; 01310 d = dwp_self; 01311 d->ref(); 01312 01313 _isStopped = false; 01314 } 01315 01316 KDirWatch::~KDirWatch() 01317 { 01318 if (d) d->removeEntries(this); 01319 if ( d->deref() ) 01320 { 01321 // delete it if it's the last one 01322 delete d; 01323 dwp_self = 0L; 01324 } 01325 } 01326 01327 01328 // TODO: add watchFiles/recursive support 01329 void KDirWatch::addDir( const QString& _path, 01330 bool watchFiles, bool recursive) 01331 { 01332 if (watchFiles || recursive) { 01333 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0" 01334 << endl; 01335 } 01336 if (d) d->addEntry(this, _path, 0, true); 01337 } 01338 01339 void KDirWatch::addFile( const QString& _path ) 01340 { 01341 if (d) d->addEntry(this, _path, 0, false); 01342 } 01343 01344 QDateTime KDirWatch::ctime( const QString &_path ) 01345 { 01346 KDirWatchPrivate::Entry* e = d->entry(_path); 01347 01348 if (!e) 01349 return QDateTime(); 01350 01351 QDateTime result; 01352 result.setTime_t(e->m_ctime); 01353 return result; 01354 } 01355 01356 void KDirWatch::removeDir( const QString& _path ) 01357 { 01358 if (d) d->removeEntry(this, _path, 0); 01359 } 01360 01361 void KDirWatch::removeFile( const QString& _path ) 01362 { 01363 if (d) d->removeEntry(this, _path, 0); 01364 } 01365 01366 bool KDirWatch::stopDirScan( const QString& _path ) 01367 { 01368 if (d) { 01369 KDirWatchPrivate::Entry *e = d->entry(_path); 01370 if (e && e->isDir) return d->stopEntryScan(this, e); 01371 } 01372 return false; 01373 } 01374 01375 bool KDirWatch::restartDirScan( const QString& _path ) 01376 { 01377 if (d) { 01378 KDirWatchPrivate::Entry *e = d->entry(_path); 01379 if (e && e->isDir) 01380 // restart without notifying pending events 01381 return d->restartEntryScan(this, e, false); 01382 } 01383 return false; 01384 } 01385 01386 void KDirWatch::stopScan() 01387 { 01388 if (d) d->stopScan(this); 01389 _isStopped = true; 01390 } 01391 01392 void KDirWatch::startScan( bool notify, bool skippedToo ) 01393 { 01394 _isStopped = false; 01395 if (d) d->startScan(this, notify, skippedToo); 01396 } 01397 01398 01399 bool KDirWatch::contains( const QString& _path ) const 01400 { 01401 KDirWatchPrivate::Entry* e = d->entry(_path); 01402 if (!e) 01403 return false; 01404 01405 KDirWatchPrivate::Client* c = e->m_clients.first(); 01406 for(;c;c=e->m_clients.next()) 01407 if (c->instance == this) return true; 01408 01409 return false; 01410 } 01411 01412 void KDirWatch::statistics() 01413 { 01414 if (!dwp_self) { 01415 kdDebug(7001) << "KDirWatch not used" << endl; 01416 return; 01417 } 01418 dwp_self->statistics(); 01419 } 01420 01421 01422 void KDirWatch::setCreated( const QString & _file ) 01423 { 01424 kdDebug(7001) << name() << " emitting created " << _file << endl; 01425 emit created( _file ); 01426 } 01427 01428 void KDirWatch::setDirty( const QString & _file ) 01429 { 01430 kdDebug(7001) << name() << " emitting dirty " << _file << endl; 01431 emit dirty( _file ); 01432 } 01433 01434 void KDirWatch::setDeleted( const QString & _file ) 01435 { 01436 kdDebug(7001) << name() << " emitting deleted " << _file << endl; 01437 emit deleted( _file ); 01438 } 01439 01440 KDirWatch::Method KDirWatch::internalMethod() 01441 { 01442 #ifdef HAVE_FAM 01443 if (d->use_fam) 01444 return KDirWatch::FAM; 01445 #endif 01446 #ifdef HAVE_DNOTIFY 01447 if (d->supports_dnotify) 01448 return KDirWatch::DNotify; 01449 #endif 01450 return KDirWatch::Stat; 01451 } 01452 01453 01454 #include "kdirwatch.moc" 01455 #include "kdirwatch_p.moc" 01456 01457 //sven 01458 01459 // vim: sw=2 ts=8 et
KDE Logo
This file is part of the documentation for kio Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:41:03 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003