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