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