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