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