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