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