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