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