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

