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