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->dn_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 #ifdef HAVE_FAM
00185
00186 if (FAMOpen(&fc) ==0) {
00187 available += ", FAM";
00188 use_fam=true;
00189 sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00190 QSocketNotifier::Read, this);
00191 connect( sn, SIGNAL(activated(int)),
00192 this, SLOT(famEventReceived()) );
00193 }
00194 else {
00195 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
00196 use_fam=false;
00197 }
00198 #endif
00199
00200 #ifdef HAVE_DNOTIFY
00201 supports_dnotify = true;
00202 rescan_all = false;
00203 struct utsname uts;
00204 int major, minor, patch;
00205 if (uname(&uts) < 0)
00206 supports_dnotify = false;
00207 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00208 supports_dnotify = false;
00209 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) {
00210 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
00211 supports_dnotify = false;
00212 }
00213
00214 if( supports_dnotify ) {
00215 available += ", DNotify";
00216
00217 pipe(mPipe);
00218 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00219 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00220 mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
00221 connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
00222 connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00223
00224 if ( dnotify_signal == 0 )
00225 {
00226 dnotify_signal = SIGRTMIN + 8;
00227
00228 struct sigaction act;
00229 act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00230 sigemptyset(&act.sa_mask);
00231 act.sa_flags = SA_SIGINFO;
00232 #ifdef SA_RESTART
00233 act.sa_flags |= SA_RESTART;
00234 #endif
00235 sigaction(dnotify_signal, &act, NULL);
00236
00237 act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00238 sigaction(SIGIO, &act, &old_sigio_act);
00239 }
00240 }
00241 else
00242 {
00243 mPipe[0] = -1;
00244 mPipe[1] = -1;
00245 }
00246 #endif
00247
00248 kdDebug(7001) << "Available methods: " << available << endl;
00249 }
00250
00251
00252 KDirWatchPrivate::~KDirWatchPrivate()
00253 {
00254 timer->stop();
00255
00256
00257 removeEntries(0);
00258
00259 #ifdef HAVE_FAM
00260 if (use_fam) {
00261 FAMClose(&fc);
00262 kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
00263 }
00264 #endif
00265 #ifdef HAVE_DNOTIFY
00266 close(mPipe[0]);
00267 close(mPipe[1]);
00268 #endif
00269 }
00270
00271 #ifdef HAVE_DNOTIFY
00272 void KDirWatchPrivate::slotActivated()
00273 {
00274 char dummy_buf[100];
00275 read(mPipe[0], &dummy_buf, 100);
00276
00277 if (!mTimer.isActive())
00278 mTimer.start(200, true);
00279 }
00280
00281
00282
00283
00284
00285 void KDirWatchPrivate::Entry::propagate_dirty()
00286 {
00287 Entry* sub_entry;
00288 for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
00289 {
00290 if (!sub_entry->dn_dirty)
00291 {
00292 sub_entry->dn_dirty = true;
00293 sub_entry->propagate_dirty();
00294 }
00295 }
00296 }
00297
00298 #else // !HAVE_DNOTIFY
00299
00300 void KDirWatchPrivate::slotActivated() {}
00301 #endif
00302
00303
00304
00305
00306 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
00307 {
00308 Client* client = m_clients.first();
00309 for(;client; client = m_clients.next())
00310 if (client->instance == instance) break;
00311
00312 if (client) {
00313 client->count++;
00314 return;
00315 }
00316
00317 client = new Client;
00318 client->instance = instance;
00319 client->count = 1;
00320 client->watchingStopped = instance->isStopped();
00321 client->pending = NoChange;
00322
00323 m_clients.append(client);
00324 }
00325
00326 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00327 {
00328 Client* client = m_clients.first();
00329 for(;client; client = m_clients.next())
00330 if (client->instance == instance) break;
00331
00332 if (client) {
00333 client->count--;
00334 if (client->count == 0) {
00335 m_clients.removeRef(client);
00336 delete client;
00337 }
00338 }
00339 }
00340
00341
00342 int KDirWatchPrivate::Entry::clients()
00343 {
00344 int clients = 0;
00345 Client* client = m_clients.first();
00346 for(;client; client = m_clients.next())
00347 clients += client->count;
00348
00349 return clients;
00350 }
00351
00352
00353 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00354 {
00355
00356 if (_path.left(1) != "/") {
00357 return 0;
00358 }
00359
00360 QString path = _path;
00361
00362 if ( path.length() > 1 && path.right(1) == "/" )
00363 path.truncate( path.length() - 1 );
00364
00365 EntryMap::Iterator it = m_mapEntries.find( path );
00366 if ( it == m_mapEntries.end() )
00367 return 0;
00368 else
00369 return &(*it);
00370 }
00371
00372
00373 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00374 {
00375 e->freq = newFreq;
00376
00377
00378 if (e->freq < freq) {
00379 freq = e->freq;
00380 if (timer->isActive()) timer->changeInterval(freq);
00381 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
00382 }
00383 }
00384
00385
00386 #if defined(HAVE_FAM)
00387
00388 bool KDirWatchPrivate::useFAM(Entry* e)
00389 {
00390 if (!use_fam) return false;
00391
00392 e->m_mode = FAMMode;
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->dn_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 ( e->path.startsWith("/mnt/") || (e->path == "/mnt")
00517 || (KIO::probably_slow_mounted(e->path)) )
00518 useFreq(e, m_nfsPollInterval);
00519 else
00520 useFreq(e, m_PollInterval);
00521
00522 if (e->m_mode != StatMode) {
00523 e->m_mode = StatMode;
00524 statEntries++;
00525
00526 if ( statEntries == 1 ) {
00527
00528 timer->start(freq);
00529 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
00530 }
00531 }
00532
00533 kdDebug(7001) << " Setup Stat (freq " << e->freq
00534 << ") for " << e->path << endl;
00535
00536 return true;
00537 }
00538
00539
00540
00541
00542
00543
00544
00545 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00546 Entry* sub_entry, bool isDir)
00547 {
00548 QString path = _path;
00549 if (path.startsWith("/dev/") || (path == "/dev"))
00550 return;
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 {
00558 if (sub_entry) {
00559 (*it).m_entries.append(sub_entry);
00560 kdDebug(7001) << "Added already watched Entry " << path
00561 << " (for " << sub_entry->path << ")" << endl;
00562 #ifdef HAVE_DNOTIFY
00563 Entry* e = &(*it);
00564 if( e->dn_fd > 0 ) {
00565 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00566
00567 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00568 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00569 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) {
00570 ::close(e->dn_fd);
00571 e->m_mode = UnknownMode;
00572 fd_Entry.remove(e->dn_fd);
00573 e->dn_fd = 0;
00574 useStat( e );
00575 }
00576 }
00577 #endif
00578 }
00579 else {
00580 (*it).addClient(instance);
00581 kdDebug(7001) << "Added already watched Entry " << path
00582 << " (now " << (*it).clients() << " clients)"
00583 << QString(" [%1]").arg(instance->name()) << endl;
00584 }
00585 return;
00586 }
00587
00588
00589
00590 struct stat stat_buf;
00591 bool exists = (stat(QFile::encodeName(path), &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 defined(HAVE_FAM)
00635 if ( ! (path.startsWith("/mnt/") || (path == "/mnt")) )
00636 if (useFAM(e)) return;
00637 #endif
00638
00639 #ifdef HAVE_DNOTIFY
00640 if ( ! (path.startsWith("/mnt/") || (path == "/mnt")) )
00641 if (useDNotify(e)) return;
00642 #endif
00643
00644 useStat(e);
00645 }
00646
00647
00648 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
00649 const QString& _path, Entry* sub_entry )
00650 {
00651 Entry* e = entry(_path);
00652 if (!e) {
00653 kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
00654 return;
00655 }
00656
00657 if (sub_entry)
00658 e->m_entries.removeRef(sub_entry);
00659 else
00660 e->removeClient(instance);
00661
00662 if (e->m_clients.count() || e->m_entries.count())
00663 return;
00664
00665 if (delayRemove) {
00666
00667 if (removeList.findRef(e)==-1)
00668 removeList.append(e);
00669
00670 return;
00671 }
00672
00673 #ifdef HAVE_FAM
00674 if (e->m_mode == FAMMode) {
00675 if ( e->m_status == Normal) {
00676 FAMCancelMonitor(&fc, &(e->fr) );
00677 kdDebug(7001) << "Cancelled FAM (Req "
00678 << FAMREQUEST_GETREQNUM(&(e->fr))
00679 << ") for " << e->path << endl;
00680 }
00681 else {
00682 if (e->isDir)
00683 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00684 else
00685 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00686 }
00687 }
00688 #endif
00689
00690 #ifdef HAVE_DNOTIFY
00691 if (e->m_mode == DNotifyMode) {
00692 if (!e->isDir) {
00693 removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00694 }
00695 else {
00696
00697 if ( e->m_status == Normal) {
00698 if (e->dn_fd) {
00699 ::close(e->dn_fd);
00700 fd_Entry.remove(e->dn_fd);
00701
00702 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
00703 << ") for " << e->path << endl;
00704 e->dn_fd = 0;
00705
00706 }
00707 }
00708 else {
00709 removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00710 }
00711 }
00712 }
00713 #endif
00714
00715 if (e->m_mode == StatMode) {
00716 statEntries--;
00717 if ( statEntries == 0 ) {
00718 timer->stop();
00719 kdDebug(7001) << " Stopped Polling Timer" << endl;
00720 }
00721 }
00722
00723 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00724 << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00725 << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00726 << endl;
00727 m_mapEntries.remove( e->path );
00728 }
00729
00730
00731
00732
00733
00734 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00735 {
00736 QPtrList<Entry> list;
00737 int minfreq = 3600000;
00738
00739
00740 EntryMap::Iterator it = m_mapEntries.begin();
00741 for( ; it != m_mapEntries.end(); ++it ) {
00742 Client* c = (*it).m_clients.first();
00743 for(;c;c=(*it).m_clients.next())
00744 if (c->instance == instance) break;
00745 if (c) {
00746 c->count = 1;
00747 list.append(&(*it));
00748 }
00749 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00750 minfreq = (*it).freq;
00751 }
00752
00753 for(Entry* e=list.first();e;e=list.next())
00754 removeEntry(instance, e->path, 0);
00755
00756 if (minfreq > freq) {
00757
00758 freq = minfreq;
00759 if (timer->isActive()) timer->changeInterval(freq);
00760 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
00761 }
00762 }
00763
00764
00765 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00766 {
00767 int stillWatching = 0;
00768 Client* c = e->m_clients.first();
00769 for(;c;c=e->m_clients.next()) {
00770 if (!instance || instance == c->instance)
00771 c->watchingStopped = true;
00772 else if (!c->watchingStopped)
00773 stillWatching += c->count;
00774 }
00775
00776 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
00777 << " (now " << stillWatching << " watchers)" << endl;
00778
00779 if (stillWatching == 0) {
00780
00781 e->m_ctime = invalid_ctime;
00782
00783 }
00784 return true;
00785 }
00786
00787
00788 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
00789 bool notify)
00790 {
00791 int wasWatching = 0, newWatching = 0;
00792 Client* c = e->m_clients.first();
00793 for(;c;c=e->m_clients.next()) {
00794 if (!c->watchingStopped)
00795 wasWatching += c->count;
00796 else if (!instance || instance == c->instance) {
00797 c->watchingStopped = false;
00798 newWatching += c->count;
00799 }
00800 }
00801 if (newWatching == 0)
00802 return false;
00803
00804 kdDebug(7001) << instance->name() << " restarted scanning " << e->path
00805 << " (now " << wasWatching+newWatching << " watchers)" << endl;
00806
00807
00808
00809 int ev = NoChange;
00810 if (wasWatching == 0) {
00811 if (!notify) {
00812 struct stat stat_buf;
00813 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00814 if (exists) {
00815 e->m_ctime = stat_buf.st_ctime;
00816 e->m_status = Normal;
00817 e->m_nlink = stat_buf.st_nlink;
00818 }
00819 else {
00820 e->m_ctime = invalid_ctime;
00821 e->m_status = NonExistent;
00822 e->m_nlink = 0;
00823 }
00824 }
00825 e->msecLeft = 0;
00826 ev = scanEntry(e);
00827 }
00828 emitEvent(e,ev);
00829
00830 return true;
00831 }
00832
00833
00834 void KDirWatchPrivate::stopScan(KDirWatch* instance)
00835 {
00836 EntryMap::Iterator it = m_mapEntries.begin();
00837 for( ; it != m_mapEntries.end(); ++it )
00838 stopEntryScan(instance, &(*it));
00839 }
00840
00841
00842 void KDirWatchPrivate::startScan(KDirWatch* instance,
00843 bool notify, bool skippedToo )
00844 {
00845 if (!notify)
00846 resetList(instance,skippedToo);
00847
00848 EntryMap::Iterator it = m_mapEntries.begin();
00849 for( ; it != m_mapEntries.end(); ++it )
00850 restartEntryScan(instance, &(*it), notify);
00851
00852
00853 }
00854
00855
00856
00857 void KDirWatchPrivate::resetList( KDirWatch* ,
00858 bool skippedToo )
00859 {
00860 EntryMap::Iterator it = m_mapEntries.begin();
00861 for( ; it != m_mapEntries.end(); ++it ) {
00862
00863 Client* c = (*it).m_clients.first();
00864 for(;c;c=(*it).m_clients.next())
00865 if (!c->watchingStopped || skippedToo)
00866 c->pending = NoChange;
00867 }
00868 }
00869
00870
00871
00872 int KDirWatchPrivate::scanEntry(Entry* e)
00873 {
00874 #ifdef HAVE_FAM
00875
00876 if (e->m_mode == FAMMode) return NoChange;
00877 #endif
00878
00879
00880 if (e->m_mode == UnknownMode) return NoChange;
00881
00882 #ifdef HAVE_DNOTIFY
00883 if (e->m_mode == DNotifyMode) {
00884
00885 if(!e->dn_dirty) return NoChange;
00886 e->dn_dirty = false;
00887 }
00888 #endif
00889
00890 if (e->m_mode == StatMode) {
00891
00892
00893
00894
00895 e->msecLeft -= freq;
00896 if (e->msecLeft>0) return NoChange;
00897 e->msecLeft += e->freq;
00898 }
00899
00900 struct stat stat_buf;
00901 bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00902 if (exists) {
00903
00904 if (e->m_status == NonExistent) {
00905 e->m_ctime = stat_buf.st_ctime;
00906 e->m_status = Normal;
00907 e->m_nlink = stat_buf.st_nlink;
00908 return Created;
00909 }
00910
00911 if ( (e->m_ctime != invalid_ctime) &&
00912 ((stat_buf.st_ctime != e->m_ctime) ||
00913 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
00914 e->m_ctime = stat_buf.st_ctime;
00915 e->m_nlink = stat_buf.st_nlink;
00916 return Changed;
00917 }
00918
00919 return NoChange;
00920 }
00921
00922
00923
00924 if (e->m_ctime == invalid_ctime)
00925 return NoChange;
00926
00927 e->m_ctime = invalid_ctime;
00928 e->m_nlink = 0;
00929 e->m_status = NonExistent;
00930
00931 return Deleted;
00932 }
00933
00934
00935
00936
00937
00938 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
00939 {
00940 QString path = e->path;
00941 if (!fileName.isEmpty()) {
00942 if (fileName[0] == '/')
00943 path = fileName;
00944 else
00945 path += "/" + fileName;
00946 }
00947
00948 Client* c = e->m_clients.first();
00949 for(;c;c=e->m_clients.next()) {
00950 if (c->instance==0 || c->count==0) continue;
00951
00952 if (c->watchingStopped) {
00953
00954 if (event == Changed)
00955 c->pending |= event;
00956 else if (event == Created || event == Deleted)
00957 c->pending = event;
00958 continue;
00959 }
00960
00961 if (event == NoChange || event == Changed)
00962 event |= c->pending;
00963 c->pending = NoChange;
00964 if (event == NoChange) continue;
00965
00966 if (event & Deleted) {
00967 c->instance->setDeleted(path);
00968
00969 continue;
00970 }
00971
00972 if (event & Created) {
00973 c->instance->setCreated(path);
00974
00975 }
00976
00977 if (event & Changed)
00978 c->instance->setDirty(path);
00979 }
00980 }
00981
00982
00983 void KDirWatchPrivate::slotRemoveDelayed()
00984 {
00985 Entry* e;
00986 delayRemove = false;
00987 for(e=removeList.first();e;e=removeList.next())
00988 removeEntry(0, e->path, 0);
00989 removeList.clear();
00990 }
00991
00992
00993
00994
00995 void KDirWatchPrivate::slotRescan()
00996 {
00997 EntryMap::Iterator it;
00998
00999
01000
01001
01002 bool timerRunning = timer->isActive();
01003 if ( timerRunning )
01004 timer->stop();
01005
01006
01007
01008 delayRemove = true;
01009
01010 #ifdef HAVE_DNOTIFY
01011 QPtrList<Entry> dList, cList;
01012
01013
01014 if (rescan_all)
01015 {
01016
01017 it = m_mapEntries.begin();
01018 for( ; it != m_mapEntries.end(); ++it )
01019 (*it).dn_dirty = true;
01020 rescan_all = false;
01021 }
01022 else
01023 {
01024
01025 it = m_mapEntries.begin();
01026 for( ; it != m_mapEntries.end(); ++it )
01027 if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
01028 (*it).propagate_dirty();
01029 }
01030
01031 #endif
01032
01033 it = m_mapEntries.begin();
01034 for( ; it != m_mapEntries.end(); ++it ) {
01035
01036 if (!(*it).isValid()) continue;
01037
01038 int ev = scanEntry( &(*it) );
01039
01040 #ifdef HAVE_DNOTIFY
01041 if ((*it).m_mode == DNotifyMode) {
01042 if ((*it).isDir && (ev == Deleted)) {
01043 dList.append( &(*it) );
01044
01045
01046 if ((*it).dn_fd) {
01047 ::close((*it).dn_fd);
01048 fd_Entry.remove((*it).dn_fd);
01049 (*it).dn_fd = 0;
01050 }
01051 }
01052
01053 else if ((*it).isDir && (ev == Created)) {
01054
01055 if ( (*it).dn_fd == 0) {
01056 cList.append( &(*it) );
01057 if (! useDNotify( &(*it) )) {
01058
01059 useStat( &(*it) );
01060 }
01061 }
01062 }
01063 }
01064 #endif
01065
01066 if ( ev != NoChange )
01067 emitEvent( &(*it), ev);
01068 }
01069
01070
01071 #ifdef HAVE_DNOTIFY
01072
01073 Entry* e;
01074 for(e=dList.first();e;e=dList.next())
01075 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01076
01077
01078 for(e=cList.first();e;e=cList.next())
01079 removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
01080 #endif
01081
01082 if ( timerRunning )
01083 timer->start(freq);
01084
01085 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01086 }
01087
01088 #ifdef HAVE_FAM
01089 void KDirWatchPrivate::famEventReceived()
01090 {
01091 static FAMEvent fe;
01092
01093 delayRemove = true;
01094
01095 while(use_fam && FAMPending(&fc)) {
01096 if (FAMNextEvent(&fc, &fe) == -1) {
01097 kdWarning(7001) << "FAM connection problem, switching to polling."
01098 << endl;
01099 use_fam = false;
01100 delete sn; sn = 0;
01101
01102
01103 EntryMap::Iterator it;
01104 it = m_mapEntries.begin();
01105 for( ; it != m_mapEntries.end(); ++it )
01106 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01107 #ifdef HAVE_DNOTIFY
01108 if (useDNotify( &(*it) )) continue;
01109 #endif
01110 useStat( &(*it) );
01111 }
01112 }
01113 else
01114 checkFAMEvent(&fe);
01115 }
01116
01117 QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01118 }
01119
01120 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01121 {
01122
01123 if ((fe->code == FAMExists) ||
01124 (fe->code == FAMEndExist) ||
01125 (fe->code == FAMAcknowledge)) return;
01126
01127
01128 if ( *(fe->filename) == '.') {
01129 if (strncmp(fe->filename, ".X.err", 6) == 0) return;
01130 if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return;
01131
01132
01133 if (strncmp(fe->filename, ".fonts.cache", 12) == 0) return;
01134 }
01135
01136 Entry* e = 0;
01137 EntryMap::Iterator it = m_mapEntries.begin();
01138 for( ; it != m_mapEntries.end(); ++it )
01139 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01140 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01141 e = &(*it);
01142 break;
01143 }
01144
01145
01146
01147 kdDebug(7001) << "Processing FAM event ("
01148 << ((fe->code == FAMChanged) ? "FAMChanged" :
01149 (fe->code == FAMDeleted) ? "FAMDeleted" :
01150 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01151 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01152 (fe->code == FAMCreated) ? "FAMCreated" :
01153 (fe->code == FAMMoved) ? "FAMMoved" :
01154 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01155 (fe->code == FAMExists) ? "FAMExists" :
01156 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01157 << ", " << fe->filename
01158 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01159 << ")" << endl;
01160
01161 if (!e) {
01162
01163
01164 return;
01165 }
01166
01167 if (e->m_status == NonExistent) {
01168 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
01169 return;
01170 }
01171
01172 if (e->isDir)
01173 switch (fe->code)
01174 {
01175 case FAMDeleted:
01176
01177 if (fe->filename[0] == '/')
01178 {
01179
01180
01181 e->m_status = NonExistent;
01182 FAMCancelMonitor(&fc, &(e->fr) );
01183 kdDebug(7001) << "Cancelled FAMReq "
01184 << FAMREQUEST_GETREQNUM(&(e->fr))
01185 << " for " << e->path << endl;
01186
01187 addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01188 }
01189 emitEvent(e, Deleted, QFile::decodeName(fe->filename));
01190 break;
01191
01192 case FAMCreated: {
01193
01194 Entry *sub_entry = e->m_entries.first();
01195 for(;sub_entry; sub_entry = e->m_entries.next())
01196 if (sub_entry->path == e->path + "/" + fe->filename) break;
01197 if (sub_entry && sub_entry->isDir) {
01198 QString path = e->path;
01199 removeEntry(0,e->path,sub_entry);
01200 sub_entry->m_status = Normal;
01201 if (!useFAM(sub_entry))
01202 useStat(sub_entry);
01203
01204 emitEvent(sub_entry, Created);
01205 }
01206 else emitEvent(e, Created, QFile::decodeName(fe->filename));
01207 break;
01208 }
01209
01210 case FAMChanged:
01211 emitEvent(e, Changed, QFile::decodeName(fe->filename));
01212
01213 default:
01214 break;
01215 }
01216 else switch (fe->code)
01217 {
01218 case FAMCreated: emitEvent(e, Created);
01219 break;
01220 case FAMDeleted: emitEvent(e, Deleted);
01221 break;
01222 case FAMChanged: emitEvent(e, Changed);
01223 break;
01224 default: break;
01225 }
01226 }
01227 #else
01228 void KDirWatchPrivate::famEventReceived() {}
01229 #endif
01230
01231
01232 void KDirWatchPrivate::statistics()
01233 {
01234 EntryMap::Iterator it;
01235
01236 kdDebug(7001) << "Entries watched:" << endl;
01237 if (m_mapEntries.count()==0) {
01238 kdDebug(7001) << " None." << endl;
01239 }
01240 else {
01241 it = m_mapEntries.begin();
01242 for( ; it != m_mapEntries.end(); ++it ) {
01243 Entry* e = &(*it);
01244 kdDebug(7001) << " " << e->path << " ("
01245 << ((e->m_status==Normal)?"":"Nonexistent ")
01246 << (e->isDir ? "Dir":"File") << ", using "
01247 << ((e->m_mode == FAMMode) ? "FAM" :
01248 (e->m_mode == DNotifyMode) ? "DNotify" :
01249 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01250 << ")" << endl;
01251
01252 Client* c = e->m_clients.first();
01253 for(;c; c = e->m_clients.next()) {
01254 QString pending;
01255 if (c->watchingStopped) {
01256 if (c->pending & Deleted) pending += "deleted ";
01257 if (c->pending & Created) pending += "created ";
01258 if (c->pending & Changed) pending += "changed ";
01259 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
01260 pending = ", stopped" + pending;
01261 }
01262 kdDebug(7001) << " by " << c->instance->name()
01263 << " (" << c->count << " times)"
01264 << pending << endl;
01265 }
01266 if (e->m_entries.count()>0) {
01267 kdDebug(7001) << " dependent entries:" << endl;
01268 Entry* d = e->m_entries.first();
01269 for(;d; d = e->m_entries.next()) {
01270 kdDebug(7001) << " " << d->path << endl;
01271 }
01272 }
01273 }
01274 }
01275 }
01276
01277
01278
01279
01280
01281
01282 static KStaticDeleter<KDirWatch> sd_dw;
01283 KDirWatch* KDirWatch::s_pSelf = 0L;
01284
01285 KDirWatch* KDirWatch::self()
01286 {
01287 if ( !s_pSelf ) {
01288 sd_dw.setObject( s_pSelf, new KDirWatch );
01289 }
01290
01291 return s_pSelf;
01292 }
01293
01294 bool KDirWatch::exists()
01295 {
01296 return s_pSelf != 0;
01297 }
01298
01299 KDirWatch::KDirWatch (QObject* parent, const char* name)
01300 : QObject(parent,name)
01301 {
01302 if (!name) {
01303 static int nameCounter = 0;
01304
01305 nameCounter++;
01306 setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
01307 }
01308
01309 if (!dwp_self)
01310 dwp_self = new KDirWatchPrivate;
01311 d = dwp_self;
01312 d->ref();
01313
01314 _isStopped = false;
01315 }
01316
01317 KDirWatch::~KDirWatch()
01318 {
01319 if (d) d->removeEntries(this);
01320 if ( d->deref() )
01321 {
01322
01323 delete d;
01324 dwp_self = 0L;
01325 }
01326 }
01327
01328
01329
01330 void KDirWatch::addDir( const QString& _path,
01331 bool watchFiles, bool recursive)
01332 {
01333 if (watchFiles || recursive) {
01334 kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
01335 << endl;
01336 }
01337 if (d) d->addEntry(this, _path, 0, true);
01338 }
01339
01340 void KDirWatch::addFile( const QString& _path )
01341 {
01342 if (d) d->addEntry(this, _path, 0, false);
01343 }
01344
01345 QDateTime KDirWatch::ctime( const QString &_path )
01346 {
01347 KDirWatchPrivate::Entry* e = d->entry(_path);
01348
01349 if (!e)
01350 return QDateTime();
01351
01352 QDateTime result;
01353 result.setTime_t(e->m_ctime);
01354 return result;
01355 }
01356
01357 void KDirWatch::removeDir( const QString& _path )
01358 {
01359 if (d) d->removeEntry(this, _path, 0);
01360 }
01361
01362 void KDirWatch::removeFile( const QString& _path )
01363 {
01364 if (d) d->removeEntry(this, _path, 0);
01365 }
01366
01367 bool KDirWatch::stopDirScan( const QString& _path )
01368 {
01369 if (d) {
01370 KDirWatchPrivate::Entry *e = d->entry(_path);
01371 if (e && e->isDir) return d->stopEntryScan(this, e);
01372 }
01373 return false;
01374 }
01375
01376 bool KDirWatch::restartDirScan( const QString& _path )
01377 {
01378 if (d) {
01379 KDirWatchPrivate::Entry *e = d->entry(_path);
01380 if (e && e->isDir)
01381
01382 return d->restartEntryScan(this, e, false);
01383 }
01384 return false;
01385 }
01386
01387 void KDirWatch::stopScan()
01388 {
01389 if (d) d->stopScan(this);
01390 _isStopped = true;
01391 }
01392
01393 void KDirWatch::startScan( bool notify, bool skippedToo )
01394 {
01395 _isStopped = false;
01396 if (d) d->startScan(this, notify, skippedToo);
01397 }
01398
01399
01400 bool KDirWatch::contains( const QString& _path ) const
01401 {
01402 KDirWatchPrivate::Entry* e = d->entry(_path);
01403 if (!e)
01404 return false;
01405
01406 KDirWatchPrivate::Client* c = e->m_clients.first();
01407 for(;c;c=e->m_clients.next())
01408 if (c->instance == this) return true;
01409
01410 return false;
01411 }
01412
01413 void KDirWatch::statistics()
01414 {
01415 if (!dwp_self) {
01416 kdDebug(7001) << "KDirWatch not used" << endl;
01417 return;
01418 }
01419 dwp_self->statistics();
01420 }
01421
01422
01423 void KDirWatch::setCreated( const QString & _file )
01424 {
01425 kdDebug(7001) << name() << " emitting created " << _file << endl;
01426 emit created( _file );
01427 }
01428
01429 void KDirWatch::setDirty( const QString & _file )
01430 {
01431 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
01432 emit dirty( _file );
01433 }
01434
01435 void KDirWatch::setDeleted( const QString & _file )
01436 {
01437 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
01438 emit deleted( _file );
01439 }
01440
01441 KDirWatch::Method KDirWatch::internalMethod()
01442 {
01443 #ifdef HAVE_FAM
01444 if (d->use_fam)
01445 return KDirWatch::FAM;
01446 #endif
01447 #ifdef HAVE_DNOTIFY
01448 if (d->supports_dnotify)
01449 return KDirWatch::DNotify;
01450 #endif
01451 return KDirWatch::Stat;
01452 }
01453
01454
01455 #include "kdirwatch.moc"
01456 #include "kdirwatch_p.moc"
01457
01458
01459
01460