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 #define flock linux_flock
00075 #define flock64 linux_flock64
00076 #include <linux/inotify.h>
00077 #undef flock
00078 #undef flock64
00079
00080 static inline int inotify_init (void)
00081 {
00082 return syscall (__NR_inotify_init);
00083 }
00084
00085 static inline int inotify_add_watch (int fd, const char *name, __u32 mask)
00086 {
00087 return syscall (__NR_inotify_add_watch, fd, name, mask);
00088 }
00089
00090 static inline int inotify_rm_watch (int fd, __u32 wd)
00091 {
00092 return syscall (__NR_inotify_rm_watch, fd, wd);
00093 }
00094
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
00107 #endif
00108
00109 #include <sys/utsname.h>
00110
00111 #include "kdirwatch.h"
00112 #include "kdirwatch_p.h"
00113 #include "global.h"
00114
00115 #define NO_NOTIFY (time_t) 0
00116
00117 static KDirWatchPrivate* dwp_self = 0;
00118
00119 #ifdef HAVE_DNOTIFY
00120
00121 static int dnotify_signal = 0;
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
00132 {
00133 if (!dwp_self) return;
00134
00135
00136
00137 int saved_errno = errno;
00138
00139 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
00140
00141
00142
00143
00144 if(e && e->dn_fd == si->si_fd)
00145 e->dirty = true;
00146
00147 char c = 0;
00148 write(dwp_self->mPipe[1], &c, 1);
00149 errno = saved_errno;
00150 }
00151
00152 static struct sigaction old_sigio_act;
00153
00154
00155
00156
00157 void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
00158 {
00159 if (dwp_self)
00160 {
00161
00162
00163 int saved_errno = errno;
00164
00165 dwp_self->rescan_all = true;
00166 char c = 0;
00167 write(dwp_self->mPipe[1], &c, 1);
00168
00169 errno = saved_errno;
00170 }
00171
00172
00173 if (old_sigio_act.sa_flags & SA_SIGINFO)
00174 {
00175 if (old_sigio_act.sa_sigaction)
00176 (*old_sigio_act.sa_sigaction)(sig, si, p);
00177 }
00178 else
00179 {
00180 if ((old_sigio_act.sa_handler != SIG_DFL) &&
00181 (old_sigio_act.sa_handler != SIG_IGN))
00182 (*old_sigio_act.sa_handler)(sig);
00183 }
00184 }
00185 #endif
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
00220 KDirWatchPrivate::KDirWatchPrivate()
00221 : rescan_timer(0, "KDirWatchPrivate::rescan_timer")
00222 {
00223 timer = new QTimer(this, "KDirWatchPrivate::timer");
00224 connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00225 freq = 3600000;
00226 statEntries = 0;
00227 delayRemove = false;
00228 m_ref = 0;
00229
00230 KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
00231 m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
00232 m_PollInterval = config.readNumEntry("PollInterval", 500);
00233
00234 QString available("Stat");
00235
00236
00237 rescan_all = false;
00238 connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00239
00240 #ifdef HAVE_FAM
00241
00242 if (FAMOpen(&fc) ==0) {
00243 available += ", FAM";
00244 use_fam=true;
00245 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00246 QSocketNotifier::Read, this);
00247 connect( sn, SIGNAL(activated(int)),
00248 this, SLOT(famEventReceived()) );
00249 }
00250 else {
00251 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
00252 use_fam=false;
00253 }
00254 #endif
00255
00256 #ifdef HAVE_INOTIFY
00257 supports_inotify = true;
00258
00259 m_inotify_fd = inotify_init();
00260
00261 if ( m_inotify_fd <= 0 ) {
00262 kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl;
00263 supports_inotify = false;
00264 }
00265
00266 {
00267 struct utsname uts;
00268 int major, minor, patch;
00269 if (uname(&uts) < 0)
00270 supports_inotify = false;
00271 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00272 supports_inotify = false;
00273 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) {
00274 kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl;
00275 supports_inotify = false;
00276 }
00277 }
00278
00279 if ( supports_inotify ) {
00280 available += ", Inotify";
00281 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
00282
00283 mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
00284 connect( mSn, SIGNAL(activated( int )), this, SLOT( slotActivated() ) );
00285 }
00286 #endif
00287
00288 #ifdef HAVE_DNOTIFY
00289
00290
00291 #ifdef HAVE_INOTIFY
00292 supports_dnotify = !supports_inotify;
00293 #else
00294
00295 supports_dnotify = true;
00296 #endif
00297
00298 struct utsname uts;
00299 int major, minor, patch;
00300 if (uname(&uts) < 0)
00301 supports_dnotify = false;
00302 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00303 supports_dnotify = false;
00304 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
00305 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
00306 supports_dnotify = false;
00307 }
00308
00309 if( supports_dnotify ) {
00310 available += ", DNotify";
00311
00312 pipe(mPipe);
00313 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00314 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00315 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
00316 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
00317 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
00318 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
00319
00320 if ( dnotify_signal == 0 )
00321 {
00322 dnotify_signal = SIGRTMIN + 8;
00323
00324 struct sigaction act;
00325 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00326 sigemptyset(&act.sa_mask);
00327 act.sa_flags = SA_SIGINFO;
00328 #ifdef SA_RESTART
00329 act.sa_flags |= SA_RESTART;
00330 #endif
00331 sigaction(dnotify_signal, &act, NULL);
00332
00333 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00334 sigaction(SIGIO, &act, &old_sigio_act);
00335 }
00336 }
00337 else
00338 {
00339 mPipe[0] = -1;
00340 mPipe[1] = -1;
00341 }
00342 #endif
00343
00344 kdDebug(7001) << "Available methods: " << available << endl;
00345 }
00346
00347
00348 KDirWatchPrivate::~KDirWatchPrivate()
00349 {
00350 timer->stop();
00351
00352
00353 removeEntries(0);
00354
00355 #ifdef HAVE_FAM
00356 if (use_fam) {
00357 FAMClose(&fc);
00358 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
00359 }
00360 #endif
00361 #ifdef HAVE_INOTIFY
00362 if ( supports_inotify )
00363 ::close( m_inotify_fd );
00364 #endif
00365 #ifdef HAVE_DNOTIFY
00366 close(mPipe[0]);
00367 close(mPipe[1]);
00368 #endif
00369 }
00370
00371 #include <stdlib.h>
00372
00373 void KDirWatchPrivate::slotActivated()
00374 {
00375 #ifdef HAVE_DNOTIFY
00376 if ( supports_dnotify )
00377 {
00378 char dummy_buf[4096];
00379 read(mPipe[0], &dummy_buf, 4096);
00380
00381 if (!rescan_timer.isActive())
00382 rescan_timer.start(m_PollInterval, true );
00383
00384 return;
00385 }
00386 #endif
00387
00388 #ifdef HAVE_INOTIFY
00389 if ( !supports_inotify )
00390 return;
00391
00392 int pending = -1;
00393 int offset = 0;
00394 char buf[4096];
00395 assert( m_inotify_fd > -1 );
00396 ioctl( m_inotify_fd, FIONREAD, &pending );
00397
00398 while ( pending > 0 ) {
00399
00400 if ( pending > (int)sizeof( buf ) )
00401 pending = sizeof( buf );
00402
00403 pending = read( m_inotify_fd, buf, pending);
00404
00405 while ( pending > 0 ) {
00406 struct inotify_event *event = (struct inotify_event *) &buf[offset];
00407 pending -= sizeof( struct inotify_event ) + event->len;
00408 offset += sizeof( struct inotify_event ) + event->len;
00409
00410 QString path;
00411 if ( event->len )
00412 path = QFile::decodeName( QCString( event->name, event->len ) );
00413
00414 if ( path.length() && isNoisyFile( path.latin1() ) )
00415 continue;
00416
00417 kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl;
00418
00419
00420
00421
00422 for ( EntryMap::Iterator it = m_mapEntries.begin();
00423 it != m_mapEntries.end(); ++it ) {
00424 Entry* e = &( *it );
00425 if ( e->wd == event->wd ) {
00426 e->dirty = true;
00427
00428 if ( 1 || e->isDir) {
00429 if( event->mask & IN_DELETE_SELF) {
00430 kdDebug(7001) << "-->got deleteself signal for " << e->path << endl;
00431 e->m_status = NonExistent;
00432 if (e->isDir)
00433 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00434 else
00435 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00436 }
00437 if ( event->mask & IN_IGNORED ) {
00438 e->wd = 0;
00439 }
00440 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
00441 Entry *sub_entry = e->m_entries.first();
00442 for(;sub_entry; sub_entry = e->m_entries.next())
00443 if (sub_entry->path == e->path + "/" + path) break;
00444
00445 if (sub_entry ) {
00446 removeEntry(0,e->path, sub_entry);
00447 KDE_struct_stat stat_buf;
00448 QCString tpath = QFile::encodeName(path);
00449 KDE_stat(tpath, &stat_buf);
00450
00451
00452
00453
00454
00455
00456 if(!useINotify(sub_entry))
00457 useStat(sub_entry);
00458 sub_entry->dirty = true;
00459 }
00460 }
00461 }
00462
00463 if (!rescan_timer.isActive())
00464 rescan_timer.start(m_PollInterval, true );
00465
00466 break;
00467 }
00468 }
00469
00470 }
00471 }
00472 #endif
00473 }
00474
00475
00476
00477
00478
00479 void KDirWatchPrivate::Entry::propagate_dirty()
00480 {
00481 for (QPtrListIterator<Entry> sub_entry (m_entries);
00482 sub_entry.current(); ++sub_entry)
00483 {
00484 if (!sub_entry.current()->dirty)
00485 {
00486 sub_entry.current()->dirty = true;
00487 sub_entry.current()->propagate_dirty();
00488 }
00489 }
00490 }
00491
00492
00493
00494
00495
00496 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
00497 {
00498 Client* client = m_clients.first();
00499 for(;client; client = m_clients.next())
00500 if (client->instance == instance) break;
00501
00502 if (client) {
00503 client->count++;
00504 return;
00505 }
00506
00507 client = new Client;
00508 client->instance = instance;
00509 client->count = 1;
00510 client->watchingStopped = instance->isStopped();
00511 client->pending = NoChange;
00512
00513 m_clients.append(client);
00514 }
00515
00516 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00517 {
00518 Client* client = m_clients.first();
00519 for(;client; client = m_clients.next())
00520 if (client->instance == instance) break;
00521
00522 if (client) {
00523 client->count--;
00524 if (client->count == 0) {
00525 m_clients.removeRef(client);
00526 delete client;
00527 }
00528 }
00529 }
00530
00531
00532 int KDirWatchPrivate::Entry::clients()
00533 {
00534 int clients = 0;
00535 Client* client = m_clients.first();
00536 for(;client; client = m_clients.next())
00537 clients += client->count;
00538
00539 return clients;
00540 }
00541
00542
00543 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00544 {
00545
00546 if (QDir::isRelativePath(_path)) {
00547 return 0;
00548 }
00549
00550 QString path = _path;
00551
00552 if ( path.length() > 1 && path.right(1) == "/" )
00553 path.truncate( path.length() - 1 );
00554
00555 EntryMap::Iterator it = m_mapEntries.find( path );
00556 if ( it == m_mapEntries.end() )
00557 return 0;
00558 else
00559 return &(*it);
00560 }
00561
00562
00563 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00564 {
00565 e->freq = newFreq;
00566
00567
00568 if (e->freq < freq) {
00569 freq = e->freq;
00570 if (timer->isActive()) timer->changeInterval(freq);
00571 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
00572 }
00573 }
00574
00575
00576 #ifdef HAVE_FAM
00577
00578 bool KDirWatchPrivate::useFAM(Entry* e)
00579 {
00580 if (!use_fam) return false;
00581
00582
00583
00584 famEventReceived();
00585
00586 e->m_mode = FAMMode;
00587 e->dirty = false;
00588
00589 if (e->isDir) {
00590 if (e->m_status == NonExistent) {
00591
00592 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00593 }
00594 else {
00595 int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00596 &(e->fr), e);
00597 if (res<0) {
00598 e->m_mode = UnknownMode;
00599 use_fam=false;
00600 return false;
00601 }
00602 kdDebug(7001) << " Setup FAM (Req "
00603 << FAMREQUEST_GETREQNUM(&(e->fr))
00604 << ") for " << e->path << endl;
00605 }
00606 }
00607 else {
00608 if (e->m_status == NonExistent) {
00609
00610 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00611 }
00612 else {
00613 int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00614 &(e->fr), e);
00615 if (res<0) {
00616 e->m_mode = UnknownMode;
00617 use_fam=false;
00618 return false;
00619 }
00620
00621 kdDebug(7001) << " Setup FAM (Req "
00622 << FAMREQUEST_GETREQNUM(&(e->fr))
00623 << ") for " << e->path << endl;
00624 }
00625 }
00626
00627
00628
00629 famEventReceived();
00630
00631 return true;
00632 }
00633 #endif
00634
00635
00636 #ifdef HAVE_DNOTIFY
00637
00638 bool KDirWatchPrivate::useDNotify(Entry* e)
00639 {
00640 e->dn_fd = 0;
00641 e->dirty = false;
00642 if (!supports_dnotify) return false;
00643
00644 e->m_mode = DNotifyMode;
00645
00646 if (e->isDir) {
00647 if (e->m_status == Normal) {
00648 int fd = KDE_open(QFile::encodeName(e->path).data(), O_RDONLY);
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 int fd2 = fcntl(fd, F_DUPFD, 128);
00662 if (fd2 >= 0)
00663 {
00664 close(fd);
00665 fd = fd2;
00666 }
00667 if (fd<0) {
00668 e->m_mode = UnknownMode;
00669 return false;
00670 }
00671
00672 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00673
00674 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00675 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00676
00677 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
00678 fcntl(fd, F_NOTIFY, mask) < 0) {
00679
00680 kdDebug(7001) << "Not using Linux Directory Notifications."
00681 << endl;
00682 supports_dnotify = false;
00683 ::close(fd);
00684 e->m_mode = UnknownMode;
00685 return false;
00686 }
00687
00688 fd_Entry.replace(fd, e);
00689 e->dn_fd = fd;
00690
00691 kdDebug(7001) << " Setup DNotify (fd " << fd
00692 << ") for " << e->path << endl;
00693 }
00694 else {
00695 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00696 }
00697 }
00698 else {
00699
00700
00701 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00702 }
00703
00704 return true;
00705 }
00706 #endif
00707
00708 #ifdef HAVE_INOTIFY
00709
00710 bool KDirWatchPrivate::useINotify( Entry* e )
00711 {
00712 e->wd = 0;
00713 e->dirty = false;
00714 if (!supports_inotify) return false;
00715
00716 e->m_mode = INotifyMode;
00717
00718 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
00719 if(!e->isDir)
00720 mask |= IN_MODIFY|IN_ATTRIB;
00721 else
00722 mask |= IN_ONLYDIR;
00723
00724
00725 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
00726 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
00727 }
00728
00729 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
00730 QFile::encodeName( e->path ), mask) ) > 0 )
00731 return true;
00732
00733 if ( e->m_status == NonExistent ) {
00734 if (e->isDir)
00735 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00736 else
00737 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00738 return true;
00739 }
00740
00741 return false;
00742 }
00743 #endif
00744
00745 bool KDirWatchPrivate::useStat(Entry* e)
00746 {
00747 if (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