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 #include <config.h>
00035
00036 #ifdef HAVE_DNOTIFY
00037 #include <unistd.h>
00038 #include <time.h>
00039 #include <fcntl.h>
00040 #include <signal.h>
00041 #include <errno.h>
00042 #endif
00043
00044 #include <sys/stat.h>
00045 #include <assert.h>
00046 #include <qdir.h>
00047 #include <qfile.h>
00048 #include <qintdict.h>
00049 #include <qptrlist.h>
00050 #include <qsocketnotifier.h>
00051 #include <qstringlist.h>
00052 #include <qtimer.h>
00053
00054 #include <kapplication.h>
00055 #include <kdebug.h>
00056 #include <kconfig.h>
00057 #include <kglobal.h>
00058 #include <kstaticdeleter.h>
00059
00060 #include "kdirwatch.h"
00061 #include "kdirwatch_p.h"
00062 #include "global.h"
00063
00064 #define NO_NOTIFY (time_t) 0
00065
00066 static KDirWatchPrivate* dwp_self = 0;
00067
00068 #ifdef HAVE_DNOTIFY
00069
00070 #include <sys/utsname.h>
00071
00072 static int dnotify_signal = 0;
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
00083 {
00084 if (!dwp_self) return;
00085
00086
00087
00088 int saved_errno = errno;
00089
00090 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
00091
00092
00093
00094
00095 if(!e || e->dn_fd != si->si_fd) {
00096 qDebug("fatal error in KDirWatch");
00097 } else
00098 e->dirty = true;
00099
00100 char c = 0;
00101 write(dwp_self->mPipe[1], &c, 1);
00102 errno = saved_errno;
00103 }
00104
00105 static struct sigaction old_sigio_act;
00106
00107
00108
00109
00110 void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
00111 {
00112 if (dwp_self)
00113 {
00114
00115
00116 int saved_errno = errno;
00117
00118 dwp_self->rescan_all = true;
00119 char c = 0;
00120 write(dwp_self->mPipe[1], &c, 1);
00121
00122 errno = saved_errno;
00123 }
00124
00125
00126 if (old_sigio_act.sa_flags & SA_SIGINFO)
00127 {
00128 if (old_sigio_act.sa_sigaction)
00129 (*old_sigio_act.sa_sigaction)(sig, si, p);
00130 }
00131 else
00132 {
00133 if ((old_sigio_act.sa_handler != SIG_DFL) &&
00134 (old_sigio_act.sa_handler != SIG_IGN))
00135 (*old_sigio_act.sa_handler)(sig);
00136 }
00137 }
00138 #endif
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 KDirWatchPrivate::KDirWatchPrivate()
00170 {
00171 timer = new QTimer(this);
00172 connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00173 freq = 3600000;
00174 statEntries = 0;
00175 delayRemove = false;
00176 m_ref = 0;
00177
00178 KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
00179 m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
00180 m_PollInterval = config.readNumEntry("PollInterval", 500);
00181
00182 QString available("Stat");
00183
00184
00185 rescan_all = false;
00186 connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00187
00188 #ifdef HAVE_FAM
00189
00190 if (FAMOpen(&fc) ==0) {
00191 available += ", FAM";
00192 use_fam=true;
00193 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00194 QSocketNotifier::Read, this);
00195 connect( sn, SIGNAL(activated(int)),
00196 this, SLOT(famEventReceived()) );
00197 }
00198 else {
00199 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
00200 use_fam=false;
00201 }
00202 #endif
00203
00204 #ifdef HAVE_DNOTIFY
00205 supports_dnotify = true;
00206 struct utsname uts;
00207 int major, minor, patch;
00208 if (uname(&uts) < 0)
00209 supports_dnotify = false;
00210 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00211 supports_dnotify = false;
00212 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
00213 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
00214 supports_dnotify = false;
00215 }
00216
00217 if( supports_dnotify ) {
00218 available += ", DNotify";
00219
00220 pipe(mPipe);
00221 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00222 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00223 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
00224 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
00225
00226 if ( dnotify_signal == 0 )
00227 {
00228 dnotify_signal = SIGRTMIN + 8;
00229
00230 struct sigaction act;
00231 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00232 sigemptyset(&act.sa_mask);
00233 act.sa_flags = SA_SIGINFO;
00234 #ifdef SA_RESTART
00235 act.sa_flags |= SA_RESTART;
00236 #endif
00237 sigaction(dnotify_signal, &act, NULL);
00238
00239 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00240 sigaction(SIGIO, &act, &old_sigio_act);
00241 }
00242 }
00243 else
00244 {
00245 mPipe[0] = -1;
00246 mPipe[1] = -1;
00247 }
00248 #endif
00249
00250 kdDebug(7001) << "Available methods: " << available << endl;
00251 }
00252
00253
00254 KDirWatchPrivate::~KDirWatchPrivate()
00255 {
00256 timer->stop();
00257
00258
00259 removeEntries(0);
00260
00261 #ifdef HAVE_FAM
00262 if (use_fam) {
00263 FAMClose(&fc);
00264 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
00265 }
00266 #endif
00267 #ifdef HAVE_DNOTIFY
00268 close(mPipe[0]);
00269 close(mPipe[1]);
00270 #endif
00271 }
00272
00273 void KDirWatchPrivate::slotActivated()
00274 {
00275 #ifdef HAVE_DNOTIFY
00276 char dummy_buf[100];
00277 read(mPipe[0], &dummy_buf, 100);
00278
00279 if (!rescan_timer.isActive())
00280 rescan_timer.start(m_PollInterval, true);
00281 #endif
00282 }
00283
00284
00285
00286
00287
00288 void KDirWatchPrivate::Entry::propagate_dirty()
00289 {
00290 Entry* sub_entry;
00291 for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
00292 {
00293 if (!sub_entry->dirty)
00294 {
00295 sub_entry->dirty = true;
00296 sub_entry->propagate_dirty();
00297 }
00298 }
00299 }
00300
00301
00302
00303
00304
00305 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
00306 {
00307 Client* client = m_clients.first();
00308 for(;client; client = m_clients.next())
00309 if (client->instance == instance) break;
00310
00311 if (client) {
00312 client->count++;
00313 return;
00314 }
00315
00316 client = new Client;
00317 client->instance = instance;
00318 client->count = 1;
00319 client->watchingStopped = instance->isStopped();
00320 client->pending = NoChange;
00321
00322 m_clients.append(client);
00323 }
00324
00325 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00326 {
00327 Client* client = m_clients.first();
00328 for(;client; client = m_clients.next())
00329 if (client->instance == instance) break;
00330
00331 if (client) {
00332 client->count--;
00333 if (client->count == 0) {
00334 m_clients.removeRef(client);
00335 delete client;
00336 }
00337 }
00338 }
00339
00340
00341 int KDirWatchPrivate::Entry::clients()
00342 {
00343 int clients = 0;
00344 Client* client = m_clients.first();
00345 for(;client; client = m_clients.next())
00346 clients += client->count;
00347
00348 return clients;
00349 }
00350
00351
00352 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00353 {
00354
00355 if (_path.left(1) != "/") {
00356 return 0;
00357 }
00358
00359 QString path = _path;
00360
00361 if ( path.length() > 1 && path.right(1) == "/" )
00362 path.truncate( path.length() - 1 );
00363
00364 EntryMap::Iterator it = m_mapEntries.find( path );
00365 if ( it == m_mapEntries.end() )
00366 return 0;
00367 else
00368 return &(*it);
00369 }
00370
00371
00372 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00373 {
00374 e->freq = newFreq;
00375
00376
00377 if (e->freq < freq) {
00378 freq = e->freq;
00379 if (timer->isActive()) timer->changeInterval(freq);
00380 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
00381 }
00382 }
00383
00384
00385 #if defined(HAVE_FAM)
00386
00387 bool KDirWatchPrivate::useFAM(Entry* e)
00388 {
00389 if (!use_fam) return false;
00390
00391 e->m_mode = FAMMode;
00392 e->dirty = false;
00393
00394 if (e->isDir) {
00395 if (e->m_status == NonExistent) {
00396
00397 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00398 }
00399 else {
00400 int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00401 &(e->fr), e);
00402 if (res<0) {
00403 e->m_mode = UnknownMode;
00404 use_fam=false;
00405 return false;
00406 }
00407 kdDebug(7001) << " Setup FAM (Req "
00408 << FAMREQUEST_GETREQNUM(&(e->fr))
00409 << ") for " << e->path << endl;
00410 }
00411 }
00412 else {
00413 if (e->m_status == NonExistent) {
00414
00415 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00416 }
00417 else {
00418 int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00419 &(e->fr), e);
00420 if (res<0) {
00421 e->m_mode = UnknownMode;
00422 use_fam=false;
00423 return false;
00424 }
00425
00426 kdDebug(7001) << " Setup FAM (Req "
00427 << FAMREQUEST_GETREQNUM(&(e->fr))
00428 << ") for " << e->path << endl;
00429 }
00430 }
00431
00432
00433
00434 famEventReceived();
00435
00436 return true;
00437 }
00438 #endif
00439
00440
00441 #ifdef HAVE_DNOTIFY
00442
00443 bool KDirWatchPrivate::useDNotify(Entry* e)
00444 {
00445 e->dn_fd = 0;
00446 if (!supports_dnotify) return false;
00447
00448 e->m_mode = DNotifyMode;
00449
00450 if (e->isDir) {
00451 e->dirty = false;
00452 if (e->m_status == Normal) {
00453 int fd = open(QFile::encodeName(e->path).data(), O_RDONLY);
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466 int fd2 = fcntl(fd, F_DUPFD, 128);
00467 if (fd2 >= 0)
00468 {
00469 close(fd);
00470 fd = fd2;
00471 }
00472 if (fd<0) {
00473 e->m_mode = UnknownMode;
00474 return false;
00475 }
00476
00477 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00478
00479 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00480 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00481
00482 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
00483 fcntl(fd, F_NOTIFY, mask) < 0) {
00484
00485 kdDebug(7001) << "Not using Linux Directory Notifications."
00486 << endl;
00487 supports_dnotify = false;
00488 ::close(fd);
00489 e->m_mode = UnknownMode;
00490 return false;
00491 }
00492
00493 fd_Entry.replace(fd, e);
00494 e->dn_fd = fd;
00495
00496 kdDebug(7001) << " Setup DNotify (fd " << fd
00497 << ") for " << e->path << endl;
00498 }
00499 else {
00500 addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00501 }
00502 }
00503 else {
00504
00505
00506 addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00507 }
00508
00509 return true;
00510 }
00511 #endif
00512
00513
00514 bool KDirWatchPrivate::useStat(Entry* e)
00515 {
00516 if (KIO::probably_slow_mounted(e->path))
00517 useFreq(e, m_nfsPollInterval);
00518 else
00519 useFreq(e, m_PollInterval);
00520
00521 if (e->m_mode != StatMode) {
00522 e->m_mode = StatMode;
00523 statEntries++;
00524
00525 if ( statEntries == 1 ) {
00526
00527 timer->start(freq);
00528 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
00529 }
00530 }
00531
00532 kdDebug(7001) << " Setup Stat (freq " << e->freq
00533 << ") for " << e->path << endl;
00534
00535 return true;
00536 }
00537
00538
00539
00540
00541
00542
00543
00544 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00545 Entry* sub_entry, bool isDir)
00546 {
00547 QString path = _path;
00548 if (path.startsWith("/dev/") || (path == "/dev"))
00549 return;
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 {
00557 if (sub_entry) {
00558 (*it).m_entries.append(sub_entry);
00559 kdDebug(7001) << "Added already watched Entry " << path
00560 << " (for " << sub_entry->path << ")" << endl;
00561 #ifdef HAVE_DNOTIFY
00562 Entry* e = &(*it);
00563 if( e->dn_fd > 0 ) {
00564 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00565
00566 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00567 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00568 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
00569 ::close(e->dn_fd);
00570 e->m_mode = UnknownMode;
00571 fd_Entry.remove(e->dn_fd);
00572 e->dn_fd = 0;
00573 useStat( e );
00574 }
00575 }
00576 #endif
00577 }
00578 else {
00579 (*it).addClient(instance);
00580 kdDebug(7001) << "Added already watched Entry " << path
00581 << " (now " << (*it).clients() << " clients)"
00582 << QString(" [%1]").arg(instance->name()) << endl;
00583 }
00584 return;
00585 }
00586
00587
00588
00589 struct stat stat_buf;
00590 QCString tpath = QFile::encodeName(path);
00591 bool exists = (stat(tpath, &stat_buf) == 0);
00592
00593 Entry newEntry;
00594 m_mapEntries.insert( path, newEntry );
00595
00596 Entry* e = &(m_mapEntries[path]);
00597
00598 if (exists) {
00599 e->isDir = S_ISDIR(stat_buf.st_mode);
00600
00601 if (e->isDir && !isDir)
00602 qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
00603 else if (!e->isDir && isDir)
00604 qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
00605
00606 e->m_ctime = stat_buf.st_ctime;
00607 e->m_status = Normal;
00608 e->m_nlink = stat_buf.st_nlink;
00609 }
00610 else {
00611 e->isDir = isDir;
00612 e->m_ctime = invalid_ctime;
00613 e->m_status = NonExistent;
00614 e->m_nlink = 0;
00615 }
00616
00617 e->path = path;
00618 if (sub_entry)
00619 e->m_entries.append(sub_entry);
00620 else
00621 e->addClient(instance);
00622
00623 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
00624 << (e->m_status == NonExistent ? " NotExisting" : "")
00625 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00626 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00627 << endl;
00628
00629
00630
00631 e->m_mode = UnknownMode;
00632 e->msecLeft = 0;
00633
00634 if ( isNoisyFile( tpath ) )
00635 return;
00636
00637 #if defined(HAVE_FAM)
00638 if (useFAM(e)) return;
00639 #endif
00640
00641 #ifdef HAVE_DNOTIFY
00642 if (useDNotify(e)) return;
00643 #endif
00644
00645 useStat(e);
00646 }
00647
00648
00649 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
00650 const QString& _path, Entry* sub_entry )
00651 {
00652 Entry* e = entry(_path);
00653 if (!e) {
00654 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
00655 return;
00656 }
00657
00658 if (sub_entry)
00659 e->m_entries.removeRef(sub_entry);
00660 else
00661 e->removeClient(instance);
00662
00663 if (e->m_clients.count() || e->m_entries.count())
00664 return;
00665
00666 if (delayRemove) {
00667
00668 if (removeList.findRef(e)==-1)
00669 removeList.append(e);
00670
00671 return;
00672 }
00673
00674 #ifdef HAVE_FAM
00675 if (e->m_mode == FAMMode) {
00676 if ( e->m_status == Normal) {
00677 FAMCancelMonitor(&fc, &(e->fr) );
00678 kdDebug(7001) << "Cancelled FAM (Req "
00679 << FAMREQUEST_GETREQNUM(&(e->fr))
00680 << ") for " << e->path << endl;
00681 }
00682 else {
00683 if (e->isDir)
00684 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00685 else
00686 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00687 }
00688 }
00689 #endif
00690
00691 #ifdef HAVE_DNOTIFY
00692 if (e->m_mode == DNotifyMode) {
00693 if (!e->isDir) {
00694 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00695 }
00696 else {
00697
00698 if ( e->m_status == Normal) {
00699 if (e->dn_fd) {
00700 ::close(e->dn_fd);
00701 fd_Entry.remove(e->dn_fd);
00702
00703 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
00704 << ") for " << e->path << endl;
00705 e->dn_fd = 0;
00706
00707 }
00708 }
00709 else {
00710 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00711 }
00712 }
00713 }
00714 #endif
00715
00716 if (e->m_mode == StatMode) {
00717 statEntries--;
00718 if ( statEntries == 0 ) {
00719 timer->stop();
00720 kdDebug(7001) << " Stopped Polling Timer" << endl;
00721 }
00722 }
00723
00724 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00725 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00726 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00727 << endl;
00728 m_mapEntries.remove( e->path );
00729 }
00730
00731
00732
00733
00734
00735 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00736 {
00737 QPtrList<Entry> list;
00738 int minfreq = 3600000;
00739
00740
00741 EntryMap::Iterator it = m_mapEntries.begin();
00742 for( ; it != m_mapEntries.end(); ++it ) {
00743 Client* c = (*it).m_clients.first();
00744 for(;c;c=(*it).m_clients.next())
00745 if (c->instance == instance) break;
00746 if (c) {
00747 c->count = 1;
00748 list.append(&(*it));
00749 }
00750 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00751 minfreq = (*it).freq;
00752 }
00753
00754 for(Entry* e=list.first();e;e=list.next())
00755 removeEntry(instance, e->path, 0);
00756
00757 if (minfreq > freq) {
00758
00759 freq = minfreq;
00760 if (timer->isActive()) timer->changeInterval(freq);
00761 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
00762 }
00763 }
00764
00765
00766 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00767 {
00768 int stillWatching = 0;
00769 Client* c = e->m_clients.first();
00770 for(;c;c=e->m_clients.next()) {
00771 if (!instance || instance == c->instance)
00772 c->watchingStopped = true;
00773 else if (!c->watchingStopped)
00774 stillWatching += c->count;
00775 }
00776
00777 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
00778 << " (now " << stillWatching << " watchers)" << endl;
00779
00780 if (stillWatching == 0) {
00781
00782 e->m_ctime = invalid_ctime;
00783
00784 }
00785 return true;
00786 }
00787
00788
00789 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
00790 bool notify)
00791 {
00792 int wasWatching = 0, newWatching = 0;
00793 Client* c = e->m_clients.first();
00794 for(;c;c=e->m_clients.next()) {
00795 if (!c->watchingStopped)
00796 wasWatching += c->count;
00797 else if (!instance || instance == c->instance) {
00798 c->watchingStopped = false;
00799 newWatching += c->count;
00800 }
00801 }
00802 if (newWatching == 0)
00803 return false;
00804
00805 kdDebug(7001) << instance->name() << " restarted scanning " << e->path
00806 << " (now " << wasWatching+newWatching << " watchers)" << endl;
00807
00808
00809
00810 int ev = NoChange;
00811 if (wasWatching == 0) {
00812 if (!notify) {
00813 struct stat stat_buf;
00814 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00815 if (exists) {
00816 e->m_ctime = stat_buf.st_ctime;
00817 e->m_status = Normal;
00818 e->m_nlink = stat_buf.st_nlink;
00819 }
00820 else {
00821 e->m_ctime = invalid_ctime;
00822 e->m_status = NonExistent;
00823 e->m_nlink = 0;
00824 }
00825 }
00826 e->msecLeft = 0;
00827 ev = scanEntry(e);
00828 }
00829 emitEvent(e,ev);
00830
00831 return true;
00832 }
00833
00834
00835 void KDirWatchPrivate::stopScan(KDirWatch* instance)
00836 {
00837 EntryMap::Iterator it = m_mapEntries.begin();
00838 for( ; it != m_mapEntries.end(); ++it )
00839 stopEntryScan(instance, &(*it));
00840 }
00841
00842
00843 void KDirWatchPrivate::startScan(KDirWatch* instance,
00844 bool notify, bool skippedToo )
00845 {
00846 if (!notify)
00847 resetList(instance,skippedToo);
00848
00849 EntryMap::Iterator it = m_mapEntries.begin();
00850 for( ; it != m_mapEntries.end(); ++it )
00851 restartEntryScan(instance, &(*it), notify);
00852
00853
00854 }
00855
00856
00857
00858 void KDirWatchPrivate::resetList( KDirWatch* ,
00859 bool skippedToo )
00860 {
00861 EntryMap::Iterator it = m_mapEntries.begin();
00862 for( ; it != m_mapEntries.end(); ++it ) {
00863
00864 Client* c = (*it).m_clients.first();
00865 for(;c;c=(*it).m_clients.next())
00866 if (!c->watchingStopped || skippedToo)
00867 c->pending = NoChange;
00868 }
00869 }
00870
00871
00872
00873 int KDirWatchPrivate::scanEntry(Entry* e)
00874 {
00875 #ifdef HAVE_FAM
00876 if (e->m_mode == FAMMode) {
00877
00878 if(!e->dirty) return NoChange;
00879 e->dirty = false;
00880 }
00881 #endif
00882
00883
00884 if (e->m_mode == UnknownMode) return NoChange;
00885
00886 #ifdef HAVE_DNOTIFY
00887 if (e->m_mode == DNotifyMode) {
00888
00889 if(!e->dirty) return NoChange;
00890 e->dirty = false;
00891 }
00892 #endif
00893
00894 if (e->m_mode == StatMode) {
00895
00896
00897
00898
00899 e->msecLeft -= freq;
00900 if (e->msecLeft>0) return NoChange;
00901 e->msecLeft += e->freq;
00902 }
00903
00904 struct stat stat_buf;
00905 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00906 if (exists) {
00907
00908 if (e->m_status == NonExistent) {
00909 e->m_ctime = stat_buf.st_ctime;
00910 e->m_status = Normal;
00911 e->m_nlink = stat_buf.st_nlink;
00912 return Created;
00913 }
00914
00915 if ( (e->m_ctime != invalid_ctime) &&
00916 ((stat_buf.st_ctime != e->m_ctime) ||
00917 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
00918 e->m_ctime = stat_buf.st_ctime;
00919 e->m_nlink = stat_buf.st_nlink;
00920 return Changed;
00921 }
00922
00923 return NoChange;
00924 }
00925
00926
00927
00928 if (e->m_ctime == invalid_ctime)
00929 return NoChange;
00930
00931 e->m_ctime = invalid_ctime;
00932 e->m_nlink = 0;
00933 e->m_status = NonExistent;
00934
00935 return Deleted;
00936 }
00937
00938
00939
00940
00941
00942 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
00943 {
00944 QString path = e->path;
00945 if (!fileName.isEmpty()) {
00946 if (fileName[0] == '/')
00947 path = fileName;
00948 else
00949 path += "/" + fileName;
00950 }
00951
00952 Client* c = e->m_clients.first();
00953 for(;c;c=e->m_clients.next()) {
00954 if (c->instance==0 || c->count==0) continue;
00955
00956 if (c->watchingStopped) {
00957
00958 if (event == Changed)
00959 c->pending |= event;
00960 else if (event == Created || event == Deleted)
00961 c->pending = event;
00962 continue;
00963 }
00964
00965 if (event == NoChange || event == Changed)
00966 event |= c->pending;
00967 c->pending = NoChange;
00968 if (event == NoChange) continue;
00969
00970 if (event & Deleted) {
00971 c->instance->setDeleted(path);
00972
00973 continue;
00974 }
00975
00976 if (event & Created) {
00977 c->instance->setCreated(path);
00978
00979 }
00980
00981 if (event & Changed)
00982 c->instance->setDirty(path);
00983 }
00984 }
00985
00986
00987 void KDirWatchPrivate::slotRemoveDelayed()
00988 {
00989 Entry* e;
00990 delayRemove = false;
00991 for(e=removeList.first();e;e=removeList.next())
00992 removeEntry(0, e->path, 0);
00993 removeList.clear();
00994 }
00995
00996
00997
00998
00999 void KDirWatchPrivate::slotRescan()
01000 {
01001 EntryMap::Iterator it;
01002
01003
01004
01005
01006 bool timerRunning = timer->isActive();
01007 if ( timerRunning )
01008 timer->stop();
01009
01010
01011
01012 delayRemove = true;
01013
01014 #ifdef HAVE_DNOTIFY
01015 QPtrList<Entry> dList, cList;
01016 #endif
01017
01018 if (rescan_all)
01019 {
01020
01021 it = m_mapEntries.begin();
01022 for( ; it != m_mapEntries.end(); ++it )
01023 (*it).dirty = true;
01024 rescan_all = false;
01025 }
01026 else
01027 {
01028
01029 it = m_mapEntries.begin();
01030 for( ; it != m_mapEntries.end(); ++it )
01031 if ( ((*it).m_mode == DNotifyMode) && (*it).dirty )
01032 (*it).propagate_dirty();
01033 }
01034
01035 it = m_mapEntries.begin();
01036 for( ; it != m_mapEntries.end(); ++it ) {
01037
01038 if (!(*it).isValid()) continue;
01039
01040 int ev = scanEntry( &(*it) );
01041
01042 #ifdef HAVE_DNOTIFY
01043 if ((*it).m_mode == DNotifyMode) {
01044 if ((*it).isDir && (ev == Deleted)) {
01045 dList.append( &(*it) );
01046
01047
01048 if ((*it).dn_fd) {
01049 ::close((*it).dn_fd);
01050 fd_Entry.remove((*it).dn_fd);
01051 (*it).dn_fd = 0;
01052 }
01053 }
01054
01055 else if ((*it).isDir && (ev == Created)) {
01056
01057 if ( (*it).dn_fd == 0) {
01058 cList.append( &(*it) );
01059 if (! useDNotify( &(*it) )) {
01060
01061 useStat( &(*it) );
01062 }
01063 }
01064 }
01065 }
01066 #endif
01067
01068 if ( ev != NoChange )
01069 emitEvent( &(*it), ev);
01070 }
01071
01072
01073 #ifdef HAVE_DNOTIFY
01074
01075 Entry* e;
01076 for(e=dList.first();e;e=dList.next())
01077 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01078
01079
01080 for(e=cList.first();e;e=cList.next())
01081 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
01082 #endif
01083
01084 if ( timerRunning )
01085 timer->start(freq);
01086
01087 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01088 }
01089
01090 bool KDirWatchPrivate::isNoisyFile( const char * filename )
01091 {
01092
01093 if ( *filename == '.') {
01094 if (strncmp(filename, ".X.err", 6) == 0) return true;
01095 if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
01096
01097
01098 if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
01099 }
01100
01101 return false;
01102 }
01103
01104 #ifdef HAVE_FAM
01105 void KDirWatchPrivate::famEventReceived()
01106 {
01107 static FAMEvent fe;
01108
01109 delayRemove = true;
01110
01111 while(use_fam && FAMPending(&fc)) {
01112 if (FAMNextEvent(&fc, &fe) == -1) {
01113 kdWarning(7001) << "FAM connection problem, switching to polling."
01114 << endl;
01115 use_fam = false;
01116 delete sn; sn = 0;
01117
01118
01119 EntryMap::Iterator it;
01120 it = m_mapEntries.begin();
01121 for( ; it != m_mapEntries.end(); ++it )
01122 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01123 #ifdef HAVE_DNOTIFY
01124 if (useDNotify( &(*it) )) continue;
01125 #endif
01126 useStat( &(*it) );
01127 }
01128 }
01129 else
01130 checkFAMEvent(&fe);
01131 }
01132
01133 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01134 }
01135
01136 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01137 {
01138
01139 if ((fe->code == FAMExists) ||
01140 (fe->code == FAMEndExist) ||
01141 (fe->code == FAMAcknowledge)) return;
01142
01143 if ( isNoisyFile( fe->filename ) )
01144 return;
01145
01146 Entry* e = 0;
01147 EntryMap::Iterator it = m_mapEntries.begin();
01148 for( ; it != m_mapEntries.end(); ++it )
01149 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01150 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01151 e = &(*it);
01152 break;
01153 }
01154
01155
01156
01157 kdDebug(7001) << "Processing FAM event ("
01158 << ((fe->code == FAMChanged) ? "FAMChanged" :
01159 (fe->code == FAMDeleted) ? "FAMDeleted" :
01160 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01161 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01162 (fe->code == FAMCreated) ? "FAMCreated" :
01163 (fe->code == FAMMoved) ? "FAMMoved" :
01164 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01165 (fe->code == FAMExists) ? "FAMExists" :
01166 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01167 << ", " << fe->filename
01168 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01169 << ")" << endl;
01170
01171 if (!e) {
01172
01173
01174 return;
01175 }
01176
01177 if (e->m_status == NonExistent) {
01178 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
01179 return;
01180 }
01181
01182
01183 e->dirty = true;
01184 if (!rescan_timer.isActive())
01185 rescan_timer.start(m_PollInterval, true);
01186
01187
01188 if (e->isDir)
01189 switch (fe->code)
01190 {
01191 case FAMDeleted:
01192
01193 if (fe->filename[0] == '/')
01194 {
01195
01196
01197 e->m_status = NonExistent;
01198 FAMCancelMonitor(&fc, &(e->fr) );
01199 kdDebug(7001) << "Cancelled FAMReq "
01200 << FAMREQUEST_GETREQNUM(&(e->fr))
01201 << " for " << e->path << endl;
01202
01203 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01204 }
01205 break;
01206
01207 case FAMCreated: {
01208
01209 Entry *sub_entry = e->m_entries.first();
01210 for(;sub_entry; sub_entry = e->m_entries.next())
01211 if (sub_entry->path == e->path + "/" + fe->filename) break;
01212 if (sub_entry && sub_entry->isDir) {
01213 QString path = e->path;
01214 removeEntry(0,e->path,sub_entry);
01215 sub_entry->m_status = Normal;
01216 if (!useFAM(sub_entry))
01217 useStat(sub_entry);
01218 }
01219 break;
01220 }
01221
01222 default:
01223 break;
01224 }
01225 }
01226 #else
01227 void KDirWatchPrivate::famEventReceived() {}
01228 #endif
01229
01230
01231 void KDirWatchPrivate::statistics()
01232 {
01233 EntryMap::Iterator it;
01234
01235 kdDebug(7001) << "Entries watched:" << endl;
01236 if (m_mapEntries.count()==0) {
01237 kdDebug(7001) << " None." << endl;
01238 }
01239 else {
01240 it = m_mapEntries.begin();
01241 for( ; it != m_mapEntries.end(); ++it ) {
01242 Entry* e = &(*it);
01243 kdDebug(7001) << " " << e->path << " ("
01244 << ((e->m_status==Normal)?"":"Nonexistent ")
01245 << (e->isDir ? "Dir":"File") << ", using "
01246 << ((e->m_mode == FAMMode) ? "FAM" :
01247 (e->m_mode == DNotifyMode) ? "DNotify" :
01248 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01249 << ")" << endl;
01250
01251 Client* c = e->m_clients.first();
01252 for(;c; c = e->m_clients.next()) {
01253 QString pending;
01254 if (c->watchingStopped) {
01255 if (c->pending & Deleted) pending += "deleted ";
01256 if (c->pending & Created) pending += "created ";
01257 if (c->pending & Changed) pending += "changed ";
01258 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
01259 pending = ", stopped" + pending;
01260 }
01261 kdDebug(7001) << " by " << c->instance->name()
01262 << " (" << c->count << " times)"
01263 << pending << endl;
01264 }
01265 if (e->m_entries.count()>0) {
01266 kdDebug(7001) << " dependent entries:" << endl;
01267 Entry* d = e->m_entries.first();
01268 for(;d; d = e->m_entries.next()) {
01269 kdDebug(7001) << " " << d->path << endl;
01270 }
01271 }
01272 }
01273 }
01274 }
01275
01276
01277
01278
01279
01280
01281 static KStaticDeleter<KDirWatch> sd_dw;
01282 KDirWatch* KDirWatch::s_pSelf = 0L;
01283
01284 KDirWatch* KDirWatch::self()
01285 {
01286 if ( !s_pSelf ) {
01287 sd_dw.setObject( s_pSelf, new KDirWatch );
01288 }
01289
01290 return s_pSelf;
01291 }
01292
01293 bool KDirWatch::exists()
01294 {
01295 return s_pSelf != 0;
01296 }
01297
01298 KDirWatch::KDirWatch (QObject* parent, const char* name)
01299 : QObject(parent,name)
01300 {
01301 if (!name) {
01302 static int nameCounter = 0;
01303
01304 nameCounter++;
01305 setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
01306 }
01307
01308 if (!dwp_self)
01309 dwp_self = new KDirWatchPrivate;
01310 d = dwp_self;
01311 d->ref();
01312
01313 _isStopped = false;
01314 }
01315
01316 KDirWatch::~KDirWatch()
01317 {
01318 if (d) d->removeEntries(this);
01319 if ( d->deref() )
01320 {
01321
01322 delete d;
01323 dwp_self = 0L;
01324 }
01325 }
01326
01327
01328
01329 void KDirWatch::addDir( const QString& _path,
01330 bool watchFiles, bool recursive)
01331 {
01332 if (watchFiles || recursive) {
01333 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
01334 << endl;
01335 }
01336 if (d) d->addEntry(this, _path, 0, true);
01337 }
01338
01339 void KDirWatch::addFile( const QString& _path )
01340 {
01341 if (d) d->addEntry(this, _path, 0, false);
01342 }
01343
01344 QDateTime KDirWatch::ctime( const QString &_path )
01345 {
01346 KDirWatchPrivate::Entry* e = d->entry(_path);
01347
01348 if (!e)
01349 return QDateTime();
01350
01351 QDateTime result;
01352 result.setTime_t(e->m_ctime);
01353 return result;
01354 }
01355
01356 void KDirWatch::removeDir( const QString& _path )
01357 {
01358 if (d) d->removeEntry(this, _path, 0);
01359 }
01360
01361 void KDirWatch::removeFile( const QString& _path )
01362 {
01363 if (d) d->removeEntry(this, _path, 0);
01364 }
01365
01366 bool KDirWatch::stopDirScan( const QString& _path )
01367 {
01368 if (d) {
01369 KDirWatchPrivate::Entry *e = d->entry(_path);
01370 if (e && e->isDir) return d->stopEntryScan(this, e);
01371 }
01372 return false;
01373 }
01374
01375 bool KDirWatch::restartDirScan( const QString& _path )
01376 {
01377 if (d) {
01378 KDirWatchPrivate::Entry *e = d->entry(_path);
01379 if (e && e->isDir)
01380
01381 return d->restartEntryScan(this, e, false);
01382 }
01383 return false;
01384 }
01385
01386 void KDirWatch::stopScan()
01387 {
01388 if (d) d->stopScan(this);
01389 _isStopped = true;
01390 }
01391
01392 void KDirWatch::startScan( bool notify, bool skippedToo )
01393 {
01394 _isStopped = false;
01395 if (d) d->startScan(this, notify, skippedToo);
01396 }
01397
01398
01399 bool KDirWatch::contains( const QString& _path ) const
01400 {
01401 KDirWatchPrivate::Entry* e = d->entry(_path);
01402 if (!e)
01403 return false;
01404
01405 KDirWatchPrivate::Client* c = e->m_clients.first();
01406 for(;c;c=e->m_clients.next())
01407 if (c->instance == this) return true;
01408
01409 return false;
01410 }
01411
01412 void KDirWatch::statistics()
01413 {
01414 if (!dwp_self) {
01415 kdDebug(7001) << "KDirWatch not used" << endl;
01416 return;
01417 }
01418 dwp_self->statistics();
01419 }
01420
01421
01422 void KDirWatch::setCreated( const QString & _file )
01423 {
01424 kdDebug(7001) << name() << " emitting created " << _file << endl;
01425 emit created( _file );
01426 }
01427
01428 void KDirWatch::setDirty( const QString & _file )
01429 {
01430 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
01431 emit dirty( _file );
01432 }
01433
01434 void KDirWatch::setDeleted( const QString & _file )
01435 {
01436 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
01437 emit deleted( _file );
01438 }
01439
01440 KDirWatch::Method KDirWatch::internalMethod()
01441 {
01442 #ifdef HAVE_FAM
01443 if (d->use_fam)
01444 return KDirWatch::FAM;
01445 #endif
01446 #ifdef HAVE_DNOTIFY
01447 if (d->supports_dnotify)
01448 return KDirWatch::DNotify;
01449 #endif
01450 return KDirWatch::Stat;
01451 }
01452
01453 #include <stdio.h>
01454 #include <mntent.h>
01455 #include <string.h>
01456 bool KDirWatchPrivate::dir_isRO(const QString & _path )
01457 {
01458 int s = 0;
01459 FILE *mnt = setmntent(_PATH_MOUNTED, "r");
01460 struct mntent *ent;
01461 int ret = -1;
01462
01463 if (mnt != NULL) {
01464 ent = getmntent(mnt);
01465 while (ent) {
01466 int l = strlen(ent->mnt_dir);
01467
01468 if (!strncmp(ent->mnt_dir, _path.latin1(), l)) {
01469 if (l > s) {
01470 s = l;
01471 ret = (hasmntopt(ent, MNTOPT_RO) != NULL);
01472 kdDebug()<<" ent->mnt_dir :"<<ent->mnt_dir<<" l "<<l<<endl;
01473 }
01474 }
01475 ent = getmntent(mnt);
01476 }
01477 endmntent(mnt);
01478 }
01479 return ret;
01480 }
01481
01482
01483
01484 #include "kdirwatch.moc"
01485 #include "kdirwatch_p.moc"
01486
01487
01488
01489