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