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