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 ( e->path.startsWith("/media/") || (e->path == "/media")
00744 || (KIO::probably_slow_mounted(e->path)) )
00745 useFreq(e, m_nfsPollInterval);
00746 else
00747 useFreq(e, m_PollInterval);
00748
00749 if (e->m_mode != StatMode) {
00750 e->m_mode = StatMode;
00751 statEntries++;
00752
00753 if ( statEntries == 1 ) {
00754
00755 timer->start(freq);
00756 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
00757 }
00758 }
00759
00760 kdDebug(7001) << " Setup Stat (freq " << e->freq
00761 << ") for " << e->path << endl;
00762
00763 return true;
00764 }
00765
00766
00767
00768
00769
00770
00771
00772 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00773 Entry* sub_entry, bool isDir)
00774 {
00775 QString path = _path;
00776 if (path.startsWith("/dev/") || (path == "/dev"))
00777 return;
00778
00779 if ( path.length() > 1 && path.right(1) == "/" )
00780 path.truncate( path.length() - 1 );
00781
00782 EntryMap::Iterator it = m_mapEntries.find( path );
00783 if ( it != m_mapEntries.end() )
00784 {
00785 if (sub_entry) {
00786 (*it).m_entries.append(sub_entry);
00787 kdDebug(7001) << "Added already watched Entry " << path
00788 << " (for " << sub_entry->path << ")" << endl;
00789
00790 #ifdef HAVE_DNOTIFY
00791 {
00792 Entry* e = &(*it);
00793 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
00794 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00795
00796 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00797 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00798 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
00799 ::close(e->dn_fd);
00800 e->m_mode = UnknownMode;
00801 fd_Entry.remove(e->dn_fd);
00802 e->dn_fd = 0;
00803 useStat( e );
00804 }
00805 }
00806 }
00807 #endif
00808
00809 #ifdef HAVE_INOTIFY
00810 {
00811 Entry* e = &(*it);
00812 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
00813 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
00814 if(!e->isDir)
00815 mask |= IN_MODIFY|IN_ATTRIB;
00816 else
00817 mask |= IN_ONLYDIR;
00818
00819 inotify_rm_watch (m_inotify_fd, e->wd);
00820 e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ), mask);
00821 }
00822 }
00823 #endif
00824
00825 }
00826 else {
00827 (*it).addClient(instance);
00828 kdDebug(7001) << "Added already watched Entry " << path
00829 << " (now " << (*it).clients() << " clients)"
00830 << QString(" [%1]").arg(instance->name()) << endl;
00831 }
00832 return;
00833 }
00834
00835
00836
00837 KDE_struct_stat stat_buf;
00838 QCString tpath = QFile::encodeName(path);
00839 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
00840
00841 Entry newEntry;
00842 m_mapEntries.insert( path, newEntry );
00843
00844 Entry* e = &(m_mapEntries[path]);
00845
00846 if (exists) {
00847 e->isDir = S_ISDIR(stat_buf.st_mode);
00848
00849 if (e->isDir && !isDir)
00850 kdWarning() << "KDirWatch: " << path << " is a directory. Use addDir!" << endl;
00851 else if (!e->isDir && isDir)
00852 kdWarning() << "KDirWatch: " << path << " is a file. Use addFile!" << endl;
00853
00854 e->m_ctime = stat_buf.st_ctime;
00855 e->m_status = Normal;
00856 e->m_nlink = stat_buf.st_nlink;
00857 }
00858 else {
00859 e->isDir = isDir;
00860 e->m_ctime = invalid_ctime;
00861 e->m_status = NonExistent;
00862 e->m_nlink = 0;
00863 }
00864
00865 e->path = path;
00866 if (sub_entry)
00867 e->m_entries.append(sub_entry);
00868 else
00869 e->addClient(instance);
00870
00871 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
00872 << (e->m_status == NonExistent ? " NotExisting" : "")
00873 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00874 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00875 << endl;
00876
00877
00878
00879 e->m_mode = UnknownMode;
00880 e->msecLeft = 0;
00881
00882 if ( isNoisyFile( tpath ) )
00883 return;
00884
00885 #ifdef HAVE_FAM
00886 if (useFAM(e)) return;
00887 #endif
00888
00889 #ifdef HAVE_INOTIFY
00890 if (useINotify(e)) return;
00891 #endif
00892
00893 #ifdef HAVE_DNOTIFY
00894 if (useDNotify(e)) return;
00895 #endif
00896
00897 useStat(e);
00898 }
00899
00900
00901 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
00902 const QString& _path, Entry* sub_entry )
00903 {
00904 kdDebug(7001) << "KDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl;
00905 Entry* e = entry(_path);
00906 if (!e) {
00907 kdDebug(7001) << "KDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl;
00908 return;
00909 }
00910
00911 if (sub_entry)
00912 e->m_entries.removeRef(sub_entry);
00913 else
00914 e->removeClient(instance);
00915
00916 if (e->m_clients.count() || e->m_entries.count()) {
00917 kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl;
00918 return;
00919 }
00920
00921 if (delayRemove) {
00922
00923 if (removeList.findRef(e)==-1)
00924 removeList.append(e);
00925
00926 return;
00927 }
00928
00929 #ifdef HAVE_FAM
00930 if (e->m_mode == FAMMode) {
00931 if ( e->m_status == Normal) {
00932 FAMCancelMonitor(&fc, &(e->fr) );
00933 kdDebug(7001) << "Cancelled FAM (Req "
00934 << FAMREQUEST_GETREQNUM(&(e->fr))
00935 << ") for " << e->path << endl;
00936 }
00937 else {
00938 if (e->isDir)
00939 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00940 else
00941 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00942 }
00943 }
00944 #endif
00945
00946 #ifdef HAVE_INOTIFY
00947 kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl;
00948 if (e->m_mode == INotifyMode) {
00949 if ( e->m_status == Normal ) {
00950 (void) inotify_rm_watch( m_inotify_fd, e->wd );
00951 kdDebug(7001) << "Cancelled INotify (fd " <<
00952 m_inotify_fd << ", " << e->wd <<
00953 ") for " << e->path << endl;
00954 }
00955 else {
00956 if (e->isDir)
00957 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00958 else
00959 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00960 }
00961 }
00962 #endif
00963
00964 #ifdef HAVE_DNOTIFY
00965 if (e->m_mode == DNotifyMode) {
00966 if (!e->isDir) {
00967 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00968 }
00969 else {
00970
00971 if ( e->m_status == Normal) {
00972 if (e->dn_fd) {
00973 ::close(e->dn_fd);
00974 fd_Entry.remove(e->dn_fd);
00975
00976 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
00977 << ") for " << e->path << endl;
00978 e->dn_fd = 0;
00979
00980 }
00981 }
00982 else {
00983 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00984 }
00985 }
00986 }
00987 #endif
00988
00989 if (e->m_mode == StatMode) {
00990 statEntries--;
00991 if ( statEntries == 0 ) {
00992 timer->stop();
00993 kdDebug(7001) << " Stopped Polling Timer" << endl;
00994 }
00995 }
00996
00997 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00998 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00999 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
01000 << endl;
01001 m_mapEntries.remove( e->path );
01002 }
01003
01004
01005
01006
01007
01008 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
01009 {
01010 QPtrList<Entry> list;
01011 int minfreq = 3600000;
01012
01013
01014 EntryMap::Iterator it = m_mapEntries.begin();
01015 for( ; it != m_mapEntries.end(); ++it ) {
01016 Client* c = (*it).m_clients.first();
01017 for(;c;c=(*it).m_clients.next())
01018 if (c->instance == instance) break;
01019 if (c) {
01020 c->count = 1;
01021 list.append(&(*it));
01022 }
01023 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
01024 minfreq = (*it).freq;
01025 }
01026
01027 for(Entry* e=list.first();e;e=list.next())
01028 removeEntry(instance, e->path, 0);
01029
01030 if (minfreq > freq) {
01031
01032 freq = minfreq;
01033 if (timer->isActive()) timer->changeInterval(freq);
01034 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
01035 }
01036 }
01037
01038
01039 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
01040 {
01041 int stillWatching = 0;
01042 Client* c = e->m_clients.first();
01043 for(;c;c=e->m_clients.next()) {
01044 if (!instance || instance == c->instance)
01045 c->watchingStopped = true;
01046 else if (!c->watchingStopped)
01047 stillWatching += c->count;
01048 }
01049
01050 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
01051 << " (now " << stillWatching << " watchers)" << endl;
01052
01053 if (stillWatching == 0) {
01054
01055 e->m_ctime = invalid_ctime;
01056 e->m_status = NonExistent;
01057
01058 }
01059 return true;
01060 }
01061
01062
01063 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
01064 bool notify)
01065 {
01066 int wasWatching = 0, newWatching = 0;
01067 Client* c = e->m_clients.first();
01068 for(;c;c=e->m_clients.next()) {
01069 if (!c->watchingStopped)
01070 wasWatching += c->count;
01071 else if (!instance || instance == c->instance) {
01072 c->watchingStopped = false;
01073 newWatching += c->count;
01074 }
01075 }
01076 if (newWatching == 0)
01077 return false;
01078
01079 kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path
01080 << " (now " << wasWatching+newWatching << " watchers)" << endl;
01081
01082
01083
01084 int ev = NoChange;
01085 if (wasWatching == 0) {
01086 if (!notify) {
01087 KDE_struct_stat stat_buf;
01088 bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
01089 if (exists) {
01090 e->m_ctime = stat_buf.st_ctime;
01091 e->m_status = Normal;
01092 e->m_nlink = stat_buf.st_nlink;
01093 }
01094 else {
01095 e->m_ctime = invalid_ctime;
01096 e->m_status = NonExistent;
01097 e->m_nlink = 0;
01098 }
01099 }
01100 e->msecLeft = 0;
01101 ev = scanEntry(e);
01102 }
01103 emitEvent(e,ev);
01104
01105 return true;
01106 }
01107
01108
01109 void KDirWatchPrivate::stopScan(KDirWatch* instance)
01110 {
01111 EntryMap::Iterator it = m_mapEntries.begin();
01112 for( ; it != m_mapEntries.end(); ++it )
01113 stopEntryScan(instance, &(*it));
01114 }
01115
01116
01117 void KDirWatchPrivate::startScan(KDirWatch* instance,
01118 bool notify, bool skippedToo )
01119 {
01120 if (!notify)
01121 resetList(instance,skippedToo);
01122
01123 EntryMap::Iterator it = m_mapEntries.begin();
01124 for( ; it != m_mapEntries.end(); ++it )
01125 restartEntryScan(instance, &(*it), notify);
01126
01127
01128 }
01129
01130
01131
01132 void KDirWatchPrivate::resetList( KDirWatch* ,
01133 bool skippedToo )
01134 {
01135 EntryMap::Iterator it = m_mapEntries.begin();
01136 for( ; it != m_mapEntries.end(); ++it ) {
01137
01138 Client* c = (*it).m_clients.first();
01139 for(;c;c=(*it).m_clients.next())
01140 if (!c->watchingStopped || skippedToo)
01141 c->pending = NoChange;
01142 }
01143 }
01144
01145
01146
01147 int KDirWatchPrivate::scanEntry(Entry* e)
01148 {
01149 #ifdef HAVE_FAM
01150 if (e->m_mode == FAMMode) {
01151
01152 if(!e->dirty) return NoChange;
01153 e->dirty = false;
01154 }
01155 #endif
01156
01157
01158 if (e->m_mode == UnknownMode) return NoChange;
01159
01160 #if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
01161 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
01162
01163 if(!e->dirty) return NoChange;
01164 kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl;
01165 e->dirty = false;
01166 }
01167 #endif
01168
01169 if (e->m_mode == StatMode) {
01170
01171
01172
01173
01174 e->msecLeft -= freq;
01175 if (e->msecLeft>0) return NoChange;
01176 e->msecLeft += e->freq;
01177 }
01178
01179 KDE_struct_stat stat_buf;
01180 bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0);
01181 if (exists) {
01182
01183 if (e->m_status == NonExistent) {
01184 e->m_ctime = stat_buf.st_ctime;
01185 e->m_status = Normal;
01186 e->m_nlink = stat_buf.st_nlink;
01187 return Created;
01188 }
01189
01190 if ( (e->m_ctime != invalid_ctime) &&
01191 ((stat_buf.st_ctime != e->m_ctime) ||
01192 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
01193 e->m_ctime = stat_buf.st_ctime;
01194 e->m_nlink = stat_buf.st_nlink;
01195 return Changed;
01196 }
01197
01198 return NoChange;
01199 }
01200
01201
01202
01203 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
01204 e->m_nlink = 0;
01205 e->m_status = NonExistent;
01206 return NoChange;
01207 }
01208
01209 e->m_ctime = invalid_ctime;
01210 e->m_nlink = 0;
01211 e->m_status = NonExistent;
01212
01213 return Deleted;
01214 }
01215
01216
01217
01218
01219
01220 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
01221 {
01222 QString path = e->path;
01223 if (!fileName.isEmpty()) {
01224 if (!QDir::isRelativePath(fileName))
01225 path = fileName;
01226 else
01227 #ifdef Q_OS_UNIX
01228 path += "/" + fileName;
01229 #elif defined(Q_WS_WIN)
01230
01231 path += QDir::currentDirPath().left(2) + "/" + fileName;
01232 #endif
01233 }
01234
01235 QPtrListIterator<Client> cit( e->m_clients );
01236 for ( ; cit.current(); ++cit )
01237 {
01238 Client* c = cit.current();
01239
01240 if (c->instance==0 || c->count==0) continue;
01241
01242 if (c->watchingStopped) {
01243
01244 if (event == Changed)
01245 c->pending |= event;
01246 else if (event == Created || event == Deleted)
01247 c->pending = event;
01248 continue;
01249 }
01250
01251 if (event == NoChange || event == Changed)
01252 event |= c->pending;
01253 c->pending = NoChange;
01254 if (event == NoChange) continue;
01255
01256 if (event & Deleted) {
01257 c->instance->setDeleted(path);
01258
01259 continue;
01260 }
01261
01262 if (event & Created) {
01263 c->instance->setCreated(path);
01264
01265 }
01266
01267 if (event & Changed)
01268 c->instance->setDirty(path);
01269 }
01270 }
01271
01272
01273 void KDirWatchPrivate::slotRemoveDelayed()
01274 {
01275 Entry* e;
01276 delayRemove = false;
01277 for(e=removeList.first();e;e=removeList.next())
01278 removeEntry(0, e->path, 0);
01279 removeList.clear();
01280 }
01281
01282
01283
01284
01285 void KDirWatchPrivate::slotRescan()
01286 {
01287 EntryMap::Iterator it;
01288
01289
01290
01291
01292 bool timerRunning = timer->isActive();
01293 if ( timerRunning )
01294 timer->stop();
01295
01296
01297
01298 delayRemove = true;
01299
01300 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
01301 QPtrList<Entry> dList, cList;
01302 #endif
01303
01304 if (rescan_all)
01305 {
01306
01307 it = m_mapEntries.begin();
01308 for( ; it != m_mapEntries.end(); ++it )
01309 (*it).dirty = true;
01310 rescan_all = false;
01311 }
01312 else
01313 {
01314
01315 it = m_mapEntries.begin();
01316 for( ; it != m_mapEntries.end(); ++it )
01317 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
01318 (*it).propagate_dirty();
01319 }
01320
01321 it = m_mapEntries.begin();
01322 for( ; it != m_mapEntries.end(); ++it ) {
01323
01324 if (!(*it).isValid()) continue;
01325
01326 int ev = scanEntry( &(*it) );
01327
01328
01329 #ifdef HAVE_INOTIFY
01330 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
01331 cList.append( &(*it) );
01332 if (! useINotify( &(*it) )) {
01333 useStat( &(*it) );
01334 }
01335 }
01336 #endif
01337
01338 #ifdef HAVE_DNOTIFY
01339 if ((*it).m_mode == DNotifyMode) {
01340 if ((*it).isDir && (ev == Deleted)) {
01341 dList.append( &(*it) );
01342
01343
01344 if ((*it).dn_fd) {
01345 ::close((*it).dn_fd);
01346 fd_Entry.remove((*it).dn_fd);
01347 (*it).dn_fd = 0;
01348 }
01349 }
01350
01351 else if ((*it).isDir && (ev == Created)) {
01352
01353 if ( (*it).dn_fd == 0) {
01354 cList.append( &(*it) );
01355 if (! useDNotify( &(*it) )) {
01356
01357 useStat( &(*it) );
01358 }
01359 }
01360 }
01361 }
01362 #endif
01363
01364 if ( ev != NoChange )
01365 emitEvent( &(*it), ev);
01366 }
01367
01368
01369 #if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
01370
01371 Entry* e;
01372 for(e=dList.first();e;e=dList.next())
01373 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01374
01375
01376 for(e=cList.first();e;e=cList.next())
01377 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
01378 #endif
01379
01380 if ( timerRunning )
01381 timer->start(freq);
01382
01383 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01384 }
01385
01386 bool KDirWatchPrivate::isNoisyFile( const char * filename )
01387 {
01388
01389 if ( *filename == '.') {
01390 if (strncmp(filename, ".X.err", 6) == 0) return true;
01391 if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
01392
01393
01394 if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
01395 }
01396
01397 return false;
01398 }
01399
01400 #ifdef HAVE_FAM
01401 void KDirWatchPrivate::famEventReceived()
01402 {
01403 static FAMEvent fe;
01404
01405 delayRemove = true;
01406
01407 while(use_fam && FAMPending(&fc)) {
01408 if (FAMNextEvent(&fc, &fe) == -1) {
01409 kdWarning(7001) << "FAM connection problem, switching to polling."
01410 << endl;
01411 use_fam = false;
01412 delete sn; sn = 0;
01413
01414
01415 EntryMap::Iterator it;
01416 it = m_mapEntries.begin();
01417 for( ; it != m_mapEntries.end(); ++it )
01418 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01419 #ifdef HAVE_INOTIFY
01420 if (useINotify( &(*it) )) continue;
01421 #endif
01422 #ifdef HAVE_DNOTIFY
01423 if (useDNotify( &(*it) )) continue;
01424 #endif
01425 useStat( &(*it) );
01426 }
01427 }
01428 else
01429 checkFAMEvent(&fe);
01430 }
01431
01432 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01433 }
01434
01435 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01436 {
01437
01438 if ((fe->code == FAMExists) ||
01439 (fe->code == FAMEndExist) ||
01440 (fe->code == FAMAcknowledge)) return;
01441
01442 if ( isNoisyFile( fe->filename ) )
01443 return;
01444
01445 Entry* e = 0;
01446 EntryMap::Iterator it = m_mapEntries.begin();
01447 for( ; it != m_mapEntries.end(); ++it )
01448 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01449 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01450 e = &(*it);
01451 break;
01452 }
01453
01454
01455
01456 #if 0 // #88538
01457 kdDebug(7001) << "Processing FAM event ("
01458 << ((fe->code == FAMChanged) ? "FAMChanged" :
01459 (fe->code == FAMDeleted) ? "FAMDeleted" :
01460 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01461 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01462 (fe->code == FAMCreated) ? "FAMCreated" :
01463 (fe->code == FAMMoved) ? "FAMMoved" :
01464 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01465 (fe->code == FAMExists) ? "FAMExists" :
01466 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01467 << ", " << fe->filename
01468 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01469 << ")" << endl;
01470 #endif
01471
01472 if (!e) {
01473
01474
01475 return;
01476 }
01477
01478 if (e->m_status == NonExistent) {
01479 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
01480 return;
01481 }
01482
01483
01484 e->dirty = true;
01485 if (!rescan_timer.isActive())
01486 rescan_timer.start(m_PollInterval, true);
01487
01488
01489 if (e->isDir)
01490 switch (fe->code)
01491 {
01492 case FAMDeleted:
01493
01494 if (!QDir::isRelativePath(fe->filename))
01495 {
01496
01497
01498 e->m_status = NonExistent;
01499 FAMCancelMonitor(&fc, &(e->fr) );
01500 kdDebug(7001) << "Cancelled FAMReq "
01501 << FAMREQUEST_GETREQNUM(&(e->fr))
01502 << " for " << e->path << endl;
01503
01504 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01505 }
01506 break;
01507
01508 case FAMCreated: {
01509
01510 Entry *sub_entry = e->m_entries.first();
01511 for(;sub_entry; sub_entry = e->m_entries.next())
01512 if (sub_entry->path == e->path + "/" + fe->filename) break;
01513 if (sub_entry && sub_entry->isDir) {
01514 QString path = e->path;
01515 removeEntry(0,e->path,sub_entry);
01516 sub_entry->m_status = Normal;
01517 if (!useFAM(sub_entry))
01518 #ifdef HAVE_INOTIFY
01519 if (!useINotify(sub_entry ))
01520 #endif
01521 useStat(sub_entry);
01522 }
01523 break;
01524 }
01525
01526 default:
01527 break;
01528 }
01529 }
01530 #else
01531 void KDirWatchPrivate::famEventReceived() {}
01532 #endif
01533
01534
01535 void KDirWatchPrivate::statistics()
01536 {
01537 EntryMap::Iterator it;
01538
01539 kdDebug(7001) << "Entries watched:" << endl;
01540 if (m_mapEntries.count()==0) {
01541 kdDebug(7001) << " None." << endl;
01542 }
01543 else {
01544 it = m_mapEntries.begin();
01545 for( ; it != m_mapEntries.end(); ++it ) {
01546 Entry* e = &(*it);
01547 kdDebug(7001) << " " << e->path << " ("
01548 << ((e->m_status==Normal)?"":"Nonexistent ")
01549 << (e->isDir ? "Dir":"File") << ", using "
01550 << ((e->m_mode == FAMMode) ? "FAM" :
01551 (e->m_mode == INotifyMode) ? "INotify" :
01552 (e->m_mode == DNotifyMode) ? "DNotify" :
01553 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01554 << ")" << endl;
01555
01556 Client* c = e->m_clients.first();
01557 for(;c; c = e->m_clients.next()) {
01558 QString pending;
01559 if (c->watchingStopped) {
01560 if (c->pending & Deleted) pending += "deleted ";
01561 if (c->pending & Created) pending += "created ";
01562 if (c->pending & Changed) pending += "changed ";
01563 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
01564 pending = ", stopped" + pending;
01565 }
01566 kdDebug(7001) << " by " << c->instance->name()
01567 << " (" << c->count << " times)"
01568 << pending << endl;
01569 }
01570 if (e->m_entries.count()>0) {
01571 kdDebug(7001) << " dependent entries:" << endl;
01572 Entry* d = e->m_entries.first();
01573 for(;d; d = e->m_entries.next()) {
01574 kdDebug(7001) << " " << d << endl;
01575 kdDebug(7001) << " " << d->path << " (" << d << ") " << endl;
01576 }
01577 }
01578 }
01579 }
01580 }
01581
01582
01583
01584
01585
01586
01587 static KStaticDeleter<KDirWatch> sd_dw;
01588 KDirWatch* KDirWatch::s_pSelf = 0L;
01589
01590 KDirWatch* KDirWatch::self()
01591 {
01592 if ( !s_pSelf ) {
01593 sd_dw.setObject( s_pSelf, new KDirWatch );
01594 }
01595
01596 return s_pSelf;
01597 }
01598
01599 bool KDirWatch::exists()
01600 {
01601 return s_pSelf != 0;
01602 }
01603
01604 KDirWatch::KDirWatch (QObject* parent, const char* name)
01605 : QObject(parent,name)
01606 {
01607 if (!name) {
01608 static int nameCounter = 0;
01609
01610 nameCounter++;
01611 setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
01612 }
01613
01614 if (!dwp_self)
01615 dwp_self = new KDirWatchPrivate;
01616 d = dwp_self;
01617 d->ref();
01618
01619 _isStopped = false;
01620 }
01621
01622 KDirWatch::~KDirWatch()
01623 {
01624 d->removeEntries(this);
01625 if ( d->deref() )
01626 {
01627
01628 delete d;
01629 dwp_self = 0L;
01630 }
01631 }
01632
01633
01634
01635 void KDirWatch::addDir( const QString& _path,
01636 bool watchFiles, bool recursive)
01637 {
01638 if (watchFiles || recursive) {
01639 kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
01640 }
01641 if (d) d->addEntry(this, _path, 0, true);
01642 }
01643
01644 void KDirWatch::addFile( const QString& _path )
01645 {
01646 if (d) d->addEntry(this, _path, 0, false);
01647 }
01648
01649 QDateTime KDirWatch::ctime( const QString &_path )
01650 {
01651 KDirWatchPrivate::Entry* e = d->entry(_path);
01652
01653 if (!e)
01654 return QDateTime();
01655
01656 QDateTime result;
01657 result.setTime_t(e->m_ctime);
01658 return result;
01659 }
01660
01661 void KDirWatch::removeDir( const QString& _path )
01662 {
01663 if (d) d->removeEntry(this, _path, 0);
01664 }
01665
01666 void KDirWatch::removeFile( const QString& _path )
01667 {
01668 if (d) d->removeEntry(this, _path, 0);
01669 }
01670
01671 bool KDirWatch::stopDirScan( const QString& _path )
01672 {
01673 if (d) {
01674 KDirWatchPrivate::Entry *e = d->entry(_path);
01675 if (e && e->isDir) return d->stopEntryScan(this, e);
01676 }
01677 return false;
01678 }
01679
01680 bool KDirWatch::restartDirScan( const QString& _path )
01681 {
01682 if (d) {
01683 KDirWatchPrivate::Entry *e = d->entry(_path);
01684 if (e && e->isDir)
01685
01686 return d->restartEntryScan(this, e, false);
01687 }
01688 return false;
01689 }
01690
01691 void KDirWatch::stopScan()
01692 {
01693 if (d) d->stopScan(this);
01694 _isStopped = true;
01695 }
01696
01697 void KDirWatch::startScan( bool notify, bool skippedToo )
01698 {
01699 _isStopped = false;
01700 if (d) d->startScan(this, notify, skippedToo);
01701 }
01702
01703
01704 bool KDirWatch::contains( const QString& _path ) const
01705 {
01706 KDirWatchPrivate::Entry* e = d->entry(_path);
01707 if (!e)
01708 return false;
01709
01710 KDirWatchPrivate::Client* c = e->m_clients.first();
01711 for(;c;c=e->m_clients.next())
01712 if (c->instance == this) return true;
01713
01714 return false;
01715 }
01716
01717 void KDirWatch::statistics()
01718 {
01719 if (!dwp_self) {
01720 kdDebug(7001) << "KDirWatch not used" << endl;
01721 return;
01722 }
01723 dwp_self->statistics();
01724 }
01725
01726
01727 void KDirWatch::setCreated( const QString & _file )
01728 {
01729 kdDebug(7001) << name() << " emitting created " << _file << endl;
01730 emit created( _file );
01731 }
01732
01733 void KDirWatch::setDirty( const QString & _file )
01734 {
01735 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
01736 emit dirty( _file );
01737 }
01738
01739 void KDirWatch::setDeleted( const QString & _file )
01740 {
01741 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
01742 emit deleted( _file );
01743 }
01744
01745 KDirWatch::Method KDirWatch::internalMethod()
01746 {
01747 #ifdef HAVE_FAM
01748 if (d->use_fam)
01749 return KDirWatch::FAM;
01750 #endif
01751 #ifdef HAVE_INOTIFY
01752 if (d->supports_inotify)
01753 return KDirWatch::INotify;
01754 #endif
01755 #ifdef HAVE_DNOTIFY
01756 if (d->supports_dnotify)
01757 return KDirWatch::DNotify;
01758 #endif
01759 return KDirWatch::Stat;
01760 }
01761
01762
01763 #include "kdirwatch.moc"
01764 #include "kdirwatch_p.moc"
01765
01766
01767
01768