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