• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

  • tdecore
ksimpledirwatch.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*/
18
19
20// KSimpleDirWatch is a basic copy of KDirWatch
21// but with the TDEIO linking requirement removed
22
23#include <config.h>
24#include <errno.h>
25
26#ifdef HAVE_DNOTIFY
27#include <unistd.h>
28#include <time.h>
29#include <fcntl.h>
30#include <signal.h>
31#include <errno.h>
32#endif
33
34
35#include <sys/stat.h>
36#include <assert.h>
37#include <tqdir.h>
38#include <tqfile.h>
39#include <tqintdict.h>
40#include <tqptrlist.h>
41#include <tqsocketnotifier.h>
42#include <tqstringlist.h>
43#include <tqtimer.h>
44
45#include <tdeapplication.h>
46#include <kdebug.h>
47#include <tdeconfig.h>
48#include <tdeglobal.h>
49#include <kstaticdeleter.h>
50#include <kde_file.h>
51
52// debug
53#include <sys/ioctl.h>
54
55#ifdef Q_OS_SOLARIS
56#include <sys/filio.h> /* FIONREAD is defined here */
57#endif /* solaris */
58
59#ifdef HAVE_INOTIFY
60#include <unistd.h>
61#include <fcntl.h>
62#include <sys/syscall.h>
63#ifdef Q_OS_LINUX
64#include <linux/types.h>
65#endif /* Linux */
66// Linux kernel headers are documented to not compile
67#define _S390_BITOPS_H
68#include <sys/inotify.h>
69
70#ifndef __NR_inotify_init
71#if defined(__i386__)
72#define __NR_inotify_init 291
73#define __NR_inotify_add_watch 292
74#define __NR_inotify_rm_watch 293
75#endif
76#if defined(__PPC__)
77#define __NR_inotify_init 275
78#define __NR_inotify_add_watch 276
79#define __NR_inotify_rm_watch 277
80#endif
81#if defined(__x86_64__)
82#define __NR_inotify_init 253
83#define __NR_inotify_add_watch 254
84#define __NR_inotify_rm_watch 255
85#endif
86#endif
87
88#ifndef IN_ONLYDIR
89#define IN_ONLYDIR 0x01000000
90#endif
91
92#ifndef IN_DONT_FOLLOW
93#define IN_DONT_FOLLOW 0x02000000
94#endif
95
96#ifndef IN_MOVE_SELF
97#define IN_MOVE_SELF 0x00000800
98#endif
99
100#endif
101
102#include <sys/utsname.h>
103
104#include "ksimpledirwatch.h"
105#include "ksimpledirwatch_p.h"
106
107#define NO_NOTIFY (time_t) 0
108
109static KSimpleDirWatchPrivate* dwp_self = 0;
110
111#ifdef HAVE_DNOTIFY
112
113static int dnotify_signal = 0;
114
115/* DNOTIFY signal handler
116 *
117 * As this is called asynchronously, only a flag is set and
118 * a rescan is requested.
119 * This is done by writing into a pipe to trigger a TQSocketNotifier
120 * watching on this pipe: a timer is started and after a timeout,
121 * the rescan is done.
122 */
123void KSimpleDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
124{
125 if (!dwp_self) return;
126
127 // write might change errno, we have to save it and restore it
128 // (Richard Stevens, Advanced programming in the Unix Environment)
129 int saved_errno = errno;
130
131 Entry* e = dwp_self->fd_Entry.find(si->si_fd);
132
133// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
134// << TQString(e ? e->path:"unknown") << endl;
135
136 if(e && e->dn_fd == si->si_fd)
137 e->dirty = true;
138
139 char c = 0;
140 write(dwp_self->mPipe[1], &c, 1);
141 errno = saved_errno;
142}
143
144static struct sigaction old_sigio_act;
145/* DNOTIFY SIGIO signal handler
146 *
147 * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
148 */
149void KSimpleDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
150{
151 if (dwp_self)
152 {
153 // write might change errno, we have to save it and restore it
154 // (Richard Stevens, Advanced programming in the Unix Environment)
155 int saved_errno = errno;
156
157 dwp_self->rescan_all = true;
158 char c = 0;
159 write(dwp_self->mPipe[1], &c, 1);
160
161 errno = saved_errno;
162 }
163
164 // Call previous signal handler
165 if (old_sigio_act.sa_flags & SA_SIGINFO)
166 {
167 if (old_sigio_act.sa_sigaction)
168 (*old_sigio_act.sa_sigaction)(sig, si, p);
169 }
170 else
171 {
172 if ((old_sigio_act.sa_handler != SIG_DFL) &&
173 (old_sigio_act.sa_handler != SIG_IGN))
174 (*old_sigio_act.sa_handler)(sig);
175 }
176}
177#endif
178
179
180//
181// Class KSimpleDirWatchPrivate (singleton)
182//
183
184/* All entries (files/directories) to be watched in the
185 * application (coming from multiple KSimpleDirWatch instances)
186 * are registered in a single KSimpleDirWatchPrivate instance.
187 *
188 * At the moment, the following methods for file watching
189 * are supported:
190 * - Polling: All files to be watched are polled regularly
191 * using stat (more precise: TQFileInfo.lastModified()).
192 * The polling frequency is determined from global tdeconfig
193 * settings, defaulting to 500 ms for local directories
194 * and 5000 ms for remote mounts
195 * - FAM (File Alternation Monitor): first used on IRIX, SGI
196 * has ported this method to LINUX. It uses a kernel part
197 * (IMON, sending change events to /dev/imon) and a user
198 * level damon (fam), to which applications connect for
199 * notification of file changes. For NFS, the fam damon
200 * on the NFS server machine is used; if IMON is not built
201 * into the kernel, fam uses polling for local files.
202 * - DNOTIFY: In late LINUX 2.3.x, directory notification was
203 * introduced. By opening a directory, you can request for
204 * UNIX signals to be sent to the process when a directory
205 * is changed.
206 * - INOTIFY: In LINUX 2.6.13, inode change notification was
207 * introduced. You're now able to watch arbitrary inode's
208 * for changes, and even get notification when they're
209 * unmounted.
210 */
211
212KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
213 : rescan_timer(0, "KSimpleDirWatchPrivate::rescan_timer")
214{
215 timer = new TQTimer(this, "KSimpleDirWatchPrivate::timer");
216 connect (timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotRescan()));
217 freq = 3600000; // 1 hour as upper bound
218 statEntries = 0;
219 delayRemove = false;
220 m_ref = 0;
221
222 TDEConfigGroup config(TDEGlobal::config(), TQCString("DirWatch"));
223 m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
224 m_PollInterval = config.readNumEntry("PollInterval", 500);
225
226 TQString available("Stat");
227
228 // used for FAM and DNOTIFY
229 rescan_all = false;
230 connect(&rescan_timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotRescan()));
231
232#ifdef HAVE_FAM
233 // It's possible that FAM server can't be started
234 if (FAMOpen(&fc) ==0) {
235 available += ", FAM";
236 use_fam=true;
237 sn = new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
238 TQSocketNotifier::Read, this);
239 connect( sn, TQ_SIGNAL(activated(int)),
240 this, TQ_SLOT(famEventReceived()) );
241 }
242 else {
243 kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
244 use_fam=false;
245 }
246#endif
247
248#ifdef HAVE_INOTIFY
249 supports_inotify = true;
250
251 m_inotify_fd = inotify_init();
252
253 if ( m_inotify_fd <= 0 ) {
254 kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl;
255 supports_inotify = false;
256 }
257
258 {
259 struct utsname uts;
260 int major, minor, patch;
261 if (uname(&uts) < 0)
262 supports_inotify = false; // *shrug*
263 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
264 supports_inotify = false; // *shrug*
265 else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
266 kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl;
267 supports_inotify = false;
268 }
269 }
270
271 if ( supports_inotify ) {
272 available += ", Inotify";
273 fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
274
275 mSn = new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read, this );
276 connect( mSn, TQ_SIGNAL(activated( int )), this, TQ_SLOT( slotActivated() ) );
277 }
278#endif
279
280#ifdef HAVE_DNOTIFY
281
282 // if we have inotify, disable dnotify.
283#ifdef HAVE_INOTIFY
284 supports_dnotify = !supports_inotify;
285#else
286 // otherwise, not guilty until proven guilty.
287 supports_dnotify = true;
288#endif
289
290 struct utsname uts;
291 int major, minor, patch;
292 if (uname(&uts) < 0)
293 supports_dnotify = false; // *shrug*
294 else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
295 supports_dnotify = false; // *shrug*
296 else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
297 kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
298 supports_dnotify = false;
299 }
300
301 if( supports_dnotify ) {
302 available += ", DNotify";
303
304 pipe(mPipe);
305 fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
306 fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
307 fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
308 fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
309 mSn = new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read, this);
310 connect(mSn, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotActivated()));
311 // Install the signal handler only once
312 if ( dnotify_signal == 0 )
313 {
314 dnotify_signal = SIGRTMIN + 8;
315
316 struct sigaction act;
317 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
318 sigemptyset(&act.sa_mask);
319 act.sa_flags = SA_SIGINFO;
320#ifdef SA_RESTART
321 act.sa_flags |= SA_RESTART;
322#endif
323 sigaction(dnotify_signal, &act, NULL);
324
325 act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
326 sigaction(SIGIO, &act, &old_sigio_act);
327 }
328 }
329 else
330 {
331 mPipe[0] = -1;
332 mPipe[1] = -1;
333 }
334#endif
335
336 kdDebug(7001) << "Available methods: " << available << endl;
337}
338
339/* This is called on app exit (KStaticDeleter) */
340KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
341{
342 timer->stop();
343
344 /* remove all entries being watched */
345 removeEntries(0);
346
347#ifdef HAVE_FAM
348 if (use_fam) {
349 FAMClose(&fc);
350 kdDebug(7001) << "KSimpleDirWatch deleted (FAM closed)" << endl;
351 }
352#endif
353#ifdef HAVE_INOTIFY
354 if ( supports_inotify )
355 ::close( m_inotify_fd );
356#endif
357#ifdef HAVE_DNOTIFY
358 close(mPipe[0]);
359 close(mPipe[1]);
360#endif
361}
362
363#include <stdlib.h>
364
365void KSimpleDirWatchPrivate::slotActivated()
366{
367#ifdef HAVE_DNOTIFY
368 if ( supports_dnotify )
369 {
370 char dummy_buf[4096];
371 read(mPipe[0], &dummy_buf, 4096);
372
373 if (!rescan_timer.isActive())
374 rescan_timer.start(m_PollInterval, true /* singleshot */);
375
376 return;
377 }
378#endif
379
380#ifdef HAVE_INOTIFY
381 if ( !supports_inotify )
382 return;
383
384 int pending = -1;
385 int offset = 0;
386 char buf[4096];
387 assert( m_inotify_fd > -1 );
388 ioctl( m_inotify_fd, FIONREAD, &pending );
389
390 while ( pending > 0 ) {
391
392 if ( pending > (int)sizeof( buf ) )
393 pending = sizeof( buf );
394
395 pending = read( m_inotify_fd, buf, pending);
396
397 while ( pending > 0 ) {
398 struct inotify_event *event = (struct inotify_event *) &buf[offset];
399 pending -= sizeof( struct inotify_event ) + event->len;
400 offset += sizeof( struct inotify_event ) + event->len;
401
402 TQString path;
403 if ( event->len )
404 path = TQFile::decodeName( TQCString( event->name, event->len ) );
405
406 if ( path.length() && isNoisyFile( path.latin1() ) )
407 continue;
408
409 kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl;
410
411 // now we're in deep trouble of finding the
412 // associated entries
413 // for now, we suck and iterate
414 for ( EntryMap::Iterator it = m_mapEntries.begin();
415 it != m_mapEntries.end(); ++it ) {
416 Entry* e = &( *it );
417 if ( e->wd == event->wd ) {
418 e->dirty = true;
419
420 if ( 1 || e->isDir) {
421 if( event->mask & IN_DELETE_SELF) {
422 kdDebug(7001) << "-->got deleteself signal for " << e->path << endl;
423 e->m_status = NonExistent;
424 if (e->isDir)
425 addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
426 else
427 addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
428 }
429 if ( event->mask & IN_IGNORED ) {
430 e->wd = 0;
431 }
432 if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
433 Entry *sub_entry = e->m_entries.first();
434 for(;sub_entry; sub_entry = e->m_entries.next())
435 if (sub_entry->path == e->path + "/" + path) break;
436
437 if (sub_entry /*&& sub_entry->isDir*/) {
438 removeEntry(0,e->path, sub_entry);
439 KDE_struct_stat stat_buf;
440 TQCString tpath = TQFile::encodeName(path);
441 KDE_stat(tpath, &stat_buf);
442
443 //sub_entry->isDir = S_ISDIR(stat_buf.st_mode);
444 //sub_entry->m_ctime = stat_buf.st_ctime;
445 //sub_entry->m_status = Normal;
446 //sub_entry->m_nlink = stat_buf.st_nlink;
447
448 if(!useINotify(sub_entry))
449 useStat(sub_entry);
450 sub_entry->dirty = true;
451 }
452 }
453 }
454
455 if (!rescan_timer.isActive())
456 rescan_timer.start(m_PollInterval, true /* singleshot */);
457
458 break; // there really should be only one matching wd
459 }
460 }
461
462 }
463 }
464#endif
465}
466
467/* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned.
468 * We first need to mark all yet nonexistent, but possible created
469 * entries as dirty...
470 */
471void KSimpleDirWatchPrivate::Entry::propagate_dirty()
472{
473 for (TQPtrListIterator<Entry> sub_entry (m_entries);
474 sub_entry.current(); ++sub_entry)
475 {
476 if (!sub_entry.current()->dirty)
477 {
478 sub_entry.current()->dirty = true;
479 sub_entry.current()->propagate_dirty();
480 }
481 }
482}
483
484
485/* A KSimpleDirWatch instance is interested in getting events for
486 * this file/Dir entry.
487 */
488void KSimpleDirWatchPrivate::Entry::addClient(KSimpleDirWatch* instance)
489{
490 Client* client = m_clients.first();
491 for(;client; client = m_clients.next())
492 if (client->instance == instance) break;
493
494 if (client) {
495 client->count++;
496 return;
497 }
498
499 client = new Client;
500 client->instance = instance;
501 client->count = 1;
502 client->watchingStopped = instance->isStopped();
503 client->pending = NoChange;
504
505 m_clients.append(client);
506}
507
508void KSimpleDirWatchPrivate::Entry::removeClient(KSimpleDirWatch* instance)
509{
510 Client* client = m_clients.first();
511 for(;client; client = m_clients.next())
512 if (client->instance == instance) break;
513
514 if (client) {
515 client->count--;
516 if (client->count == 0) {
517 m_clients.removeRef(client);
518 delete client;
519 }
520 }
521}
522
523/* get number of clients */
524int KSimpleDirWatchPrivate::Entry::clients()
525{
526 int clients = 0;
527 Client* client = m_clients.first();
528 for(;client; client = m_clients.next())
529 clients += client->count;
530
531 return clients;
532}
533
534
535KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(const TQString& _path)
536{
537// we only support absolute paths
538 if (TQDir::isRelativePath(_path)) {
539 return 0;
540 }
541
542 TQString path = _path;
543
544 if ( path.length() > 1 && path.right(1) == "/" )
545 path.truncate( path.length() - 1 );
546
547 EntryMap::Iterator it = m_mapEntries.find( path );
548 if ( it == m_mapEntries.end() )
549 return 0;
550 else
551 return &(*it);
552}
553
554// set polling frequency for a entry and adjust global freq if needed
555void KSimpleDirWatchPrivate::useFreq(Entry* e, int newFreq)
556{
557 e->freq = newFreq;
558
559 // a reasonable frequency for the global polling timer
560 if (e->freq < freq) {
561 freq = e->freq;
562 if (timer->isActive()) timer->changeInterval(freq);
563 kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
564 }
565}
566
567
568#ifdef HAVE_FAM
569// setup FAM notification, returns false if not possible
570bool KSimpleDirWatchPrivate::useFAM(Entry* e)
571{
572 if (!use_fam) return false;
573
574 // handle FAM events to avoid deadlock
575 // (FAM sends back all files in a directory when monitoring)
576 famEventReceived();
577
578 e->m_mode = FAMMode;
579 e->dirty = false;
580
581 if (e->isDir) {
582 if (e->m_status == NonExistent) {
583 // If the directory does not exist we watch the parent directory
584 addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
585 }
586 else {
587 int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
588 &(e->fr), e);
589 if (res<0) {
590 e->m_mode = UnknownMode;
591 use_fam=false;
592 return false;
593 }
594 kdDebug(7001) << " Setup FAM (Req "
595 << FAMREQUEST_GETREQNUM(&(e->fr))
596 << ") for " << e->path << endl;
597 }
598 }
599 else {
600 if (e->m_status == NonExistent) {
601 // If the file does not exist we watch the directory
602 addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
603 }
604 else {
605 int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
606 &(e->fr), e);
607 if (res<0) {
608 e->m_mode = UnknownMode;
609 use_fam=false;
610 return false;
611 }
612
613 kdDebug(7001) << " Setup FAM (Req "
614 << FAMREQUEST_GETREQNUM(&(e->fr))
615 << ") for " << e->path << endl;
616 }
617 }
618
619 // handle FAM events to avoid deadlock
620 // (FAM sends back all files in a directory when monitoring)
621 famEventReceived();
622
623 return true;
624}
625#endif
626
627
628#ifdef HAVE_DNOTIFY
629// setup DNotify notification, returns false if not possible
630bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
631{
632 e->dn_fd = 0;
633 e->dirty = false;
634 if (!supports_dnotify) return false;
635
636 e->m_mode = DNotifyMode;
637
638 if (e->isDir) {
639 if (e->m_status == Normal) {
640 int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
641 // Migrate fd to somewhere above 128. Some libraries have
642 // constructs like:
643 // fd = socket(...)
644 // if (fd > ARBITRARY_LIMIT)
645 // return error;
646 //
647 // Since programs might end up using a lot of KSimpleDirWatch objects
648 // for a rather long time the above braindamage could get
649 // triggered.
650 //
651 // By moving the ksimpledirwatch fd's to > 128, calls like socket() will keep
652 // returning fd's < ARBITRARY_LIMIT for a bit longer.
653 int fd2 = fcntl(fd, F_DUPFD, 128);
654 if (fd2 >= 0)
655 {
656 close(fd);
657 fd = fd2;
658 }
659 if (fd<0) {
660 e->m_mode = UnknownMode;
661 return false;
662 }
663
664 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
665 // if dependant is a file watch, we check for MODIFY & ATTRIB too
666 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
667 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
668
669 if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
670 fcntl(fd, F_NOTIFY, mask) < 0) {
671
672 kdDebug(7001) << "Not using Linux Directory Notifications."
673 << endl;
674 supports_dnotify = false;
675 ::close(fd);
676 e->m_mode = UnknownMode;
677 return false;
678 }
679
680 fd_Entry.replace(fd, e);
681 e->dn_fd = fd;
682
683 kdDebug(7001) << " Setup DNotify (fd " << fd
684 << ") for " << e->path << endl;
685 }
686 else { // NotExisting
687 addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
688 }
689 }
690 else { // File
691 // we always watch the directory (DNOTIFY can't watch files alone)
692 // this notifies us about changes of files therein
693 addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
694 }
695
696 return true;
697}
698#endif
699
700#ifdef HAVE_INOTIFY
701// setup INotify notification, returns false if not possible
702bool KSimpleDirWatchPrivate::useINotify( Entry* e )
703{
704 e->wd = 0;
705 e->dirty = false;
706 if (!supports_inotify) return false;
707
708 e->m_mode = INotifyMode;
709
710 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
711 if(!e->isDir)
712 mask |= IN_MODIFY|IN_ATTRIB;
713 else
714 mask |= IN_ONLYDIR;
715
716 // if dependant is a file watch, we check for MODIFY & ATTRIB too
717 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
718 if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
719 }
720
721 if ( ( e->wd = inotify_add_watch( m_inotify_fd,
722 TQFile::encodeName( e->path ), mask) ) > 0 )
723 return true;
724
725 if ( e->m_status == NonExistent ) {
726 if (e->isDir)
727 addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
728 else
729 addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
730 return true;
731 }
732
733 return false;
734}
735#endif
736
737bool KSimpleDirWatchPrivate::useStat(Entry* e)
738{
739 useFreq(e, m_PollInterval);
740
741 if (e->m_mode != StatMode) {
742 e->m_mode = StatMode;
743 statEntries++;
744
745 if ( statEntries == 1 ) {
746 // if this was first STAT entry (=timer was stopped)
747 timer->start(freq); // then start the timer
748 kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
749 }
750 }
751
752 kdDebug(7001) << " Setup Stat (freq " << e->freq
753 << ") for " << e->path << endl;
754
755 return true;
756}
757
758
759/* If <instance> !=0, this KSimpleDirWatch instance wants to watch at <_path>,
760 * providing in <isDir> the type of the entry to be watched.
761 * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
762 * this entry needs another entry to watch himself (when notExistent).
763 */
764void KSimpleDirWatchPrivate::addEntry(KSimpleDirWatch* instance, const TQString& _path,
765 Entry* sub_entry, bool isDir)
766{
767 TQString path = _path;
768 if (path.startsWith("/dev/") || (path == "/dev"))
769 return; // Don't even go there.
770
771 if ( path.length() > 1 && path.right(1) == "/" )
772 path.truncate( path.length() - 1 );
773
774 EntryMap::Iterator it = m_mapEntries.find( path );
775 if ( it != m_mapEntries.end() )
776 {
777 if (sub_entry) {
778 (*it).m_entries.append(sub_entry);
779 kdDebug(7001) << "Added already watched Entry " << path
780 << " (for " << sub_entry->path << ")" << endl;
781
782#ifdef HAVE_DNOTIFY
783 {
784 Entry* e = &(*it);
785 if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
786 int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
787 // if dependant is a file watch, we check for MODIFY & ATTRIB too
788 for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
789 if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
790 if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
791 ::close(e->dn_fd);
792 e->m_mode = UnknownMode;
793 fd_Entry.remove(e->dn_fd);
794 e->dn_fd = 0;
795 useStat( e );
796 }
797 }
798 }
799#endif
800
801#ifdef HAVE_INOTIFY
802 {
803 Entry* e = &(*it);
804 if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
805 int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
806 if(!e->isDir)
807 mask |= IN_MODIFY|IN_ATTRIB;
808 else
809 mask |= IN_ONLYDIR;
810
811 inotify_rm_watch (m_inotify_fd, e->wd);
812 e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
813 }
814 }
815#endif
816
817 }
818 else {
819 (*it).addClient(instance);
820 kdDebug(7001) << "Added already watched Entry " << path
821 << " (now " << (*it).clients() << " clients)"
822 << TQString(TQString(" [%1]").arg(instance->name())) << endl;
823 }
824 return;
825 }
826
827 // we have a new path to watch
828
829 KDE_struct_stat stat_buf;
830 TQCString tpath = TQFile::encodeName(path);
831 bool exists = (KDE_stat(tpath, &stat_buf) == 0);
832
833 Entry newEntry;
834 m_mapEntries.insert( path, newEntry );
835 // the insert does a copy, so we have to use <e> now
836 Entry* e = &(m_mapEntries[path]);
837
838 if (exists) {
839 e->isDir = S_ISDIR(stat_buf.st_mode);
840
841 if (e->isDir && !isDir)
842 kdWarning() << "KSimpleDirWatch: " << path << " is a directory. Use addDir!" << endl;
843 else if (!e->isDir && isDir)
844 kdWarning() << "KSimpleDirWatch: " << path << " is a file. Use addFile!" << endl;
845
846 e->m_ctime = stat_buf.st_ctime;
847 e->m_status = Normal;
848 e->m_nlink = stat_buf.st_nlink;
849 }
850 else {
851 e->isDir = isDir;
852 e->m_ctime = invalid_ctime;
853 e->m_status = NonExistent;
854 e->m_nlink = 0;
855 }
856
857 e->path = path;
858 if (sub_entry)
859 e->m_entries.append(sub_entry);
860 else
861 e->addClient(instance);
862
863 kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
864 << (e->m_status == NonExistent ? " NotExisting" : "")
865 << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
866 << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
867 << endl;
868
869
870 // now setup the notification method
871 e->m_mode = UnknownMode;
872 e->msecLeft = 0;
873
874 if ( isNoisyFile( tpath ) )
875 return;
876
877#ifdef HAVE_FAM
878 if (useFAM(e)) return;
879#endif
880
881#ifdef HAVE_INOTIFY
882 if (useINotify(e)) return;
883#endif
884
885#ifdef HAVE_DNOTIFY
886 if (useDNotify(e)) return;
887#endif
888
889 useStat(e);
890}
891
892
893void KSimpleDirWatchPrivate::removeEntry( KSimpleDirWatch* instance,
894 const TQString& _path, Entry* sub_entry )
895{
896 kdDebug(7001) << "KSimpleDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl;
897 Entry* e = entry(_path);
898 if (!e) {
899 kdDebug(7001) << "KSimpleDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl;
900 return;
901 }
902
903 if (sub_entry)
904 e->m_entries.removeRef(sub_entry);
905 else
906 e->removeClient(instance);
907
908 if (e->m_clients.count() || e->m_entries.count()) {
909 kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl;
910 return;
911 }
912
913 if (delayRemove) {
914 // removeList is allowed to contain any entry at most once
915 if (removeList.findRef(e)==-1)
916 removeList.append(e);
917 // now e->isValid() is false
918 return;
919 }
920
921#ifdef HAVE_FAM
922 if (e->m_mode == FAMMode) {
923 if ( e->m_status == Normal) {
924 FAMCancelMonitor(&fc, &(e->fr) );
925 kdDebug(7001) << "Cancelled FAM (Req "
926 << FAMREQUEST_GETREQNUM(&(e->fr))
927 << ") for " << e->path << endl;
928 }
929 else {
930 if (e->isDir)
931 removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
932 else
933 removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
934 }
935 }
936#endif
937
938#ifdef HAVE_INOTIFY
939 kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl;
940 if (e->m_mode == INotifyMode) {
941 if ( e->m_status == Normal ) {
942 (void) inotify_rm_watch( m_inotify_fd, e->wd );
943 kdDebug(7001) << "Cancelled INotify (fd " <<
944 m_inotify_fd << ", " << e->wd <<
945 ") for " << e->path << endl;
946 }
947 else {
948 if (e->isDir)
949 removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
950 else
951 removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
952 }
953 }
954#endif
955
956#ifdef HAVE_DNOTIFY
957 if (e->m_mode == DNotifyMode) {
958 if (!e->isDir) {
959 removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
960 }
961 else { // isDir
962 // must close the FD.
963 if ( e->m_status == Normal) {
964 if (e->dn_fd) {
965 ::close(e->dn_fd);
966 fd_Entry.remove(e->dn_fd);
967
968 kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
969 << ") for " << e->path << endl;
970 e->dn_fd = 0;
971
972 }
973 }
974 else {
975 removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
976 }
977 }
978 }
979#endif
980
981 if (e->m_mode == StatMode) {
982 statEntries--;
983 if ( statEntries == 0 ) {
984 timer->stop(); // stop timer if lists are empty
985 kdDebug(7001) << " Stopped Polling Timer" << endl;
986 }
987 }
988
989 kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
990 << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
991 << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
992 << endl;
993 m_mapEntries.remove( e->path ); // <e> not valid any more
994}
995
996
997/* Called from KSimpleDirWatch destructor:
998 * remove <instance> as client from all entries
999 */
1000void KSimpleDirWatchPrivate::removeEntries( KSimpleDirWatch* instance )
1001{
1002 TQPtrList<Entry> list;
1003 int minfreq = 3600000;
1004
1005 // put all entries where instance is a client in list
1006 EntryMap::Iterator it = m_mapEntries.begin();
1007 for( ; it != m_mapEntries.end(); ++it ) {
1008 Client* c = (*it).m_clients.first();
1009 for(;c;c=(*it).m_clients.next())
1010 if (c->instance == instance) break;
1011 if (c) {
1012 c->count = 1; // forces deletion of instance as client
1013 list.append(&(*it));
1014 }
1015 else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1016 minfreq = (*it).freq;
1017 }
1018
1019 for(Entry* e=list.first();e;e=list.next())
1020 removeEntry(instance, e->path, 0);
1021
1022 if (minfreq > freq) {
1023 // we can decrease the global polling frequency
1024 freq = minfreq;
1025 if (timer->isActive()) timer->changeInterval(freq);
1026 kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
1027 }
1028}
1029
1030// instance ==0: stop scanning for all instances
1031bool KSimpleDirWatchPrivate::stopEntryScan( KSimpleDirWatch* instance, Entry* e)
1032{
1033 int stillWatching = 0;
1034 Client* c = e->m_clients.first();
1035 for(;c;c=e->m_clients.next()) {
1036 if (!instance || instance == c->instance)
1037 c->watchingStopped = true;
1038 else if (!c->watchingStopped)
1039 stillWatching += c->count;
1040 }
1041
1042 kdDebug(7001) << instance->name() << " stopped scanning " << e->path
1043 << " (now " << stillWatching << " watchers)" << endl;
1044
1045 if (stillWatching == 0) {
1046 // if nobody is interested, we don't watch
1047 e->m_ctime = invalid_ctime; // invalid
1048 e->m_status = NonExistent;
1049 // e->m_status = Normal;
1050 }
1051 return true;
1052}
1053
1054// instance ==0: start scanning for all instances
1055bool KSimpleDirWatchPrivate::restartEntryScan( KSimpleDirWatch* instance, Entry* e,
1056 bool notify)
1057{
1058 int wasWatching = 0, newWatching = 0;
1059 Client* c = e->m_clients.first();
1060 for(;c;c=e->m_clients.next()) {
1061 if (!c->watchingStopped)
1062 wasWatching += c->count;
1063 else if (!instance || instance == c->instance) {
1064 c->watchingStopped = false;
1065 newWatching += c->count;
1066 }
1067 }
1068 if (newWatching == 0)
1069 return false;
1070
1071 kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path
1072 << " (now " << wasWatching+newWatching << " watchers)" << endl;
1073
1074 // restart watching and emit pending events
1075
1076 int ev = NoChange;
1077 if (wasWatching == 0) {
1078 if (!notify) {
1079 KDE_struct_stat stat_buf;
1080 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1081 if (exists) {
1082 e->m_ctime = stat_buf.st_ctime;
1083 e->m_status = Normal;
1084 e->m_nlink = stat_buf.st_nlink;
1085 }
1086 else {
1087 e->m_ctime = invalid_ctime;
1088 e->m_status = NonExistent;
1089 e->m_nlink = 0;
1090 }
1091 }
1092 e->msecLeft = 0;
1093 ev = scanEntry(e);
1094 }
1095 emitEvent(e,ev);
1096
1097 return true;
1098}
1099
1100// instance ==0: stop scanning for all instances
1101void KSimpleDirWatchPrivate::stopScan(KSimpleDirWatch* instance)
1102{
1103 EntryMap::Iterator it = m_mapEntries.begin();
1104 for( ; it != m_mapEntries.end(); ++it )
1105 stopEntryScan(instance, &(*it));
1106}
1107
1108
1109void KSimpleDirWatchPrivate::startScan(KSimpleDirWatch* instance,
1110 bool notify, bool skippedToo )
1111{
1112 if (!notify)
1113 resetList(instance,skippedToo);
1114
1115 EntryMap::Iterator it = m_mapEntries.begin();
1116 for( ; it != m_mapEntries.end(); ++it )
1117 restartEntryScan(instance, &(*it), notify);
1118
1119 // timer should still be running when in polling mode
1120}
1121
1122
1123// clear all pending events, also from stopped
1124void KSimpleDirWatchPrivate::resetList( KSimpleDirWatch* /*instance*/,
1125 bool skippedToo )
1126{
1127 EntryMap::Iterator it = m_mapEntries.begin();
1128 for( ; it != m_mapEntries.end(); ++it ) {
1129
1130 Client* c = (*it).m_clients.first();
1131 for(;c;c=(*it).m_clients.next())
1132 if (!c->watchingStopped || skippedToo)
1133 c->pending = NoChange;
1134 }
1135}
1136
1137// Return event happened on <e>
1138//
1139int KSimpleDirWatchPrivate::scanEntry(Entry* e)
1140{
1141#ifdef HAVE_FAM
1142 if (e->m_mode == FAMMode) {
1143 // we know nothing has changed, no need to stat
1144 if(!e->dirty) return NoChange;
1145 e->dirty = false;
1146 }
1147#endif
1148
1149 // Shouldn't happen: Ignore "unknown" notification method
1150 if (e->m_mode == UnknownMode) return NoChange;
1151
1152#if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
1153 if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
1154 // we know nothing has changed, no need to stat
1155 if(!e->dirty) return NoChange;
1156 kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl;
1157 e->dirty = false;
1158 }
1159#endif
1160
1161 if (e->m_mode == StatMode) {
1162 // only scan if timeout on entry timer happens;
1163 // e.g. when using 500msec global timer, a entry
1164 // with freq=5000 is only watched every 10th time
1165
1166 e->msecLeft -= freq;
1167 if (e->msecLeft>0) return NoChange;
1168 e->msecLeft += e->freq;
1169 }
1170
1171 KDE_struct_stat stat_buf;
1172 bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
1173 if (exists) {
1174
1175 if (e->m_status == NonExistent) {
1176 e->m_ctime = stat_buf.st_ctime;
1177 e->m_status = Normal;
1178 e->m_nlink = stat_buf.st_nlink;
1179 return Created;
1180 }
1181
1182 if ( (e->m_ctime != invalid_ctime) &&
1183 ((stat_buf.st_ctime != e->m_ctime) ||
1184 (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
1185 e->m_ctime = stat_buf.st_ctime;
1186 e->m_nlink = stat_buf.st_nlink;
1187 return Changed;
1188 }
1189
1190 return NoChange;
1191 }
1192
1193 // dir/file doesn't exist
1194
1195 if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
1196 e->m_nlink = 0;
1197 e->m_status = NonExistent;
1198 return NoChange;
1199 }
1200
1201 e->m_ctime = invalid_ctime;
1202 e->m_nlink = 0;
1203 e->m_status = NonExistent;
1204
1205 return Deleted;
1206}
1207
1208/* Notify all interested KSimpleDirWatch instances about a given event on an entry
1209 * and stored pending events. When watching is stopped, the event is
1210 * added to the pending events.
1211 */
1212void KSimpleDirWatchPrivate::emitEvent(Entry* e, int event, const TQString &fileName)
1213{
1214 TQString path = e->path;
1215 if (!fileName.isEmpty()) {
1216 if (!TQDir::isRelativePath(fileName))
1217 path = fileName;
1218 else
1219#ifdef Q_OS_UNIX
1220 path += "/" + fileName;
1221#elif defined(TQ_WS_WIN)
1222 //current drive is passed instead of /
1223 path += TQDir::currentDirPath().left(2) + "/" + fileName;
1224#endif
1225 }
1226
1227 TQPtrListIterator<Client> cit( e->m_clients );
1228 for ( ; cit.current(); ++cit )
1229 {
1230 Client* c = cit.current();
1231
1232 if (c->instance==0 || c->count==0) continue;
1233
1234 if (c->watchingStopped) {
1235 // add event to pending...
1236 if (event == Changed)
1237 c->pending |= event;
1238 else if (event == Created || event == Deleted)
1239 c->pending = event;
1240 continue;
1241 }
1242 // not stopped
1243 if (event == NoChange || event == Changed)
1244 event |= c->pending;
1245 c->pending = NoChange;
1246 if (event == NoChange) continue;
1247
1248 if (event & Deleted) {
1249 c->instance->setDeleted(path);
1250 // emit only Deleted event...
1251 continue;
1252 }
1253
1254 if (event & Created) {
1255 c->instance->setCreated(path);
1256 // possible emit Change event after creation
1257 }
1258
1259 if (event & Changed)
1260 c->instance->setDirty(path);
1261 }
1262}
1263
1264// Remove entries which were marked to be removed
1265void KSimpleDirWatchPrivate::slotRemoveDelayed()
1266{
1267 Entry* e;
1268 delayRemove = false;
1269 for(e=removeList.first();e;e=removeList.next())
1270 removeEntry(0, e->path, 0);
1271 removeList.clear();
1272}
1273
1274/* Scan all entries to be watched for changes. This is done regularly
1275 * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
1276 */
1277void KSimpleDirWatchPrivate::slotRescan()
1278{
1279 EntryMap::Iterator it;
1280
1281 // People can do very long things in the slot connected to dirty(),
1282 // like showing a message box. We don't want to keep polling during
1283 // that time, otherwise the value of 'delayRemove' will be reset.
1284 bool timerRunning = timer->isActive();
1285 if ( timerRunning )
1286 timer->stop();
1287
1288 // We delay deletions of entries this way.
1289 // removeDir(), when called in slotDirty(), can cause a crash otherwise
1290 delayRemove = true;
1291
1292#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1293 TQPtrList<Entry> dList, cList;
1294#endif
1295
1296 if (rescan_all)
1297 {
1298 // mark all as dirty
1299 it = m_mapEntries.begin();
1300 for( ; it != m_mapEntries.end(); ++it )
1301 (*it).dirty = true;
1302 rescan_all = false;
1303 }
1304 else
1305 {
1306 // progate dirty flag to dependant entries (e.g. file watches)
1307 it = m_mapEntries.begin();
1308 for( ; it != m_mapEntries.end(); ++it )
1309 if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
1310 (*it).propagate_dirty();
1311 }
1312
1313 it = m_mapEntries.begin();
1314 for( ; it != m_mapEntries.end(); ++it ) {
1315 // we don't check invalid entries (i.e. remove delayed)
1316 if (!(*it).isValid()) continue;
1317
1318 int ev = scanEntry( &(*it) );
1319
1320
1321#ifdef HAVE_INOTIFY
1322 if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
1323 cList.append( &(*it) );
1324 if (! useINotify( &(*it) )) {
1325 useStat( &(*it) );
1326 }
1327 }
1328#endif
1329
1330#ifdef HAVE_DNOTIFY
1331 if ((*it).m_mode == DNotifyMode) {
1332 if ((*it).isDir && (ev == Deleted)) {
1333 dList.append( &(*it) );
1334
1335 // must close the FD.
1336 if ((*it).dn_fd) {
1337 ::close((*it).dn_fd);
1338 fd_Entry.remove((*it).dn_fd);
1339 (*it).dn_fd = 0;
1340 }
1341 }
1342
1343 else if ((*it).isDir && (ev == Created)) {
1344 // For created, but yet without DNOTIFYing ...
1345 if ( (*it).dn_fd == 0) {
1346 cList.append( &(*it) );
1347 if (! useDNotify( &(*it) )) {
1348 // if DNotify setup fails...
1349 useStat( &(*it) );
1350 }
1351 }
1352 }
1353 }
1354#endif
1355
1356 if ( ev != NoChange )
1357 emitEvent( &(*it), ev);
1358 }
1359
1360
1361#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
1362 // Scan parent of deleted directories for new creation
1363 Entry* e;
1364 for(e=dList.first();e;e=dList.next())
1365 addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
1366
1367 // Remove watch of parent of new created directories
1368 for(e=cList.first();e;e=cList.next())
1369 removeEntry(0, TQDir::cleanDirPath( e->path+"/.."), e);
1370#endif
1371
1372 if ( timerRunning )
1373 timer->start(freq);
1374
1375 TQTimer::singleShot(0, this, TQ_SLOT(slotRemoveDelayed()));
1376}
1377
1378bool KSimpleDirWatchPrivate::isNoisyFile( const char * filename )
1379{
1380 // $HOME/.X.err grows with debug output, so don't notify change
1381 if ( *filename == '.') {
1382 if (strncmp(filename, ".X.err", 6) == 0) return true;
1383 if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
1384 // fontconfig updates the cache on every KDE app start
1385 // (inclusive tdeio_thumbnail slaves)
1386 if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
1387 }
1388
1389 return false;
1390}
1391
1392#ifdef HAVE_FAM
1393void KSimpleDirWatchPrivate::famEventReceived()
1394{
1395 static FAMEvent fe;
1396
1397 delayRemove = true;
1398
1399 while(use_fam && FAMPending(&fc)) {
1400 if (FAMNextEvent(&fc, &fe) == -1) {
1401 kdWarning(7001) << "FAM connection problem, switching to polling."
1402 << endl;
1403 use_fam = false;
1404 delete sn; sn = 0;
1405
1406 // Replace all FAMMode entries with DNotify/Stat
1407 EntryMap::Iterator it;
1408 it = m_mapEntries.begin();
1409 for( ; it != m_mapEntries.end(); ++it )
1410 if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1411#ifdef HAVE_INOTIFY
1412 if (useINotify( &(*it) )) continue;
1413#endif
1414#ifdef HAVE_DNOTIFY
1415 if (useDNotify( &(*it) )) continue;
1416#endif
1417 useStat( &(*it) );
1418 }
1419 }
1420 else
1421 checkFAMEvent(&fe);
1422 }
1423
1424 TQTimer::singleShot(0, this, TQ_SLOT(slotRemoveDelayed()));
1425}
1426
1427void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1428{
1429 // Don't be too verbose ;-)
1430 if ((fe->code == FAMExists) ||
1431 (fe->code == FAMEndExist) ||
1432 (fe->code == FAMAcknowledge)) return;
1433
1434 if ( isNoisyFile( fe->filename ) )
1435 return;
1436
1437 Entry* e = 0;
1438 EntryMap::Iterator it = m_mapEntries.begin();
1439 for( ; it != m_mapEntries.end(); ++it )
1440 if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1441 FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1442 e = &(*it);
1443 break;
1444 }
1445
1446 // Entry* e = static_cast<Entry*>(fe->userdata);
1447
1448#if 0 // #88538
1449 kdDebug(7001) << "Processing FAM event ("
1450 << ((fe->code == FAMChanged) ? "FAMChanged" :
1451 (fe->code == FAMDeleted) ? "FAMDeleted" :
1452 (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
1453 (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
1454 (fe->code == FAMCreated) ? "FAMCreated" :
1455 (fe->code == FAMMoved) ? "FAMMoved" :
1456 (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
1457 (fe->code == FAMExists) ? "FAMExists" :
1458 (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
1459 << ", " << fe->filename
1460 << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
1461 << ")" << endl;
1462#endif
1463
1464 if (!e) {
1465 // this happens e.g. for FAMAcknowledge after deleting a dir...
1466 // kdDebug(7001) << "No entry for FAM event ?!" << endl;
1467 return;
1468 }
1469
1470 if (e->m_status == NonExistent) {
1471 kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
1472 return;
1473 }
1474
1475 // Delayed handling. This rechecks changes with own stat calls.
1476 e->dirty = true;
1477 if (!rescan_timer.isActive())
1478 rescan_timer.start(m_PollInterval, true);
1479
1480 // needed FAM control actions on FAM events
1481 if (e->isDir)
1482 switch (fe->code)
1483 {
1484 case FAMDeleted:
1485 // file absolute: watched dir
1486 if (!TQDir::isRelativePath(fe->filename))
1487 {
1488 // a watched directory was deleted
1489
1490 e->m_status = NonExistent;
1491 FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
1492 kdDebug(7001) << "Cancelled FAMReq "
1493 << FAMREQUEST_GETREQNUM(&(e->fr))
1494 << " for " << e->path << endl;
1495 // Scan parent for a new creation
1496 addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
1497 }
1498 break;
1499
1500 case FAMCreated: {
1501 // check for creation of a directory we have to watch
1502 Entry *sub_entry = e->m_entries.first();
1503 for(;sub_entry; sub_entry = e->m_entries.next())
1504 if (sub_entry->path == e->path + "/" + fe->filename) break;
1505 if (sub_entry && sub_entry->isDir) {
1506 TQString path = e->path;
1507 removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
1508 sub_entry->m_status = Normal;
1509 if (!useFAM(sub_entry))
1510 {
1511#ifdef HAVE_INOTIFY
1512 if (!useINotify(sub_entry ))
1513#endif
1514 {
1515 useStat(sub_entry);
1516 }
1517 }
1518 }
1519 break;
1520 }
1521
1522 default:
1523 break;
1524 }
1525}
1526#else
1527void KSimpleDirWatchPrivate::famEventReceived() {}
1528#endif
1529
1530
1531void KSimpleDirWatchPrivate::statistics()
1532{
1533 EntryMap::Iterator it;
1534
1535 kdDebug(7001) << "Entries watched:" << endl;
1536 if (m_mapEntries.count()==0) {
1537 kdDebug(7001) << " None." << endl;
1538 }
1539 else {
1540 it = m_mapEntries.begin();
1541 for( ; it != m_mapEntries.end(); ++it ) {
1542 Entry* e = &(*it);
1543 kdDebug(7001) << " " << e->path << " ("
1544 << ((e->m_status==Normal)?"":"Nonexistent ")
1545 << (e->isDir ? "Dir":"File") << ", using "
1546 << ((e->m_mode == FAMMode) ? "FAM" :
1547 (e->m_mode == INotifyMode) ? "INotify" :
1548 (e->m_mode == DNotifyMode) ? "DNotify" :
1549 (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
1550 << ")" << endl;
1551
1552 Client* c = e->m_clients.first();
1553 for(;c; c = e->m_clients.next()) {
1554 TQString pending;
1555 if (c->watchingStopped) {
1556 if (c->pending & Deleted) pending += "deleted ";
1557 if (c->pending & Created) pending += "created ";
1558 if (c->pending & Changed) pending += "changed ";
1559 if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
1560 pending = ", stopped" + pending;
1561 }
1562 kdDebug(7001) << " by " << c->instance->name()
1563 << " (" << c->count << " times)"
1564 << pending << endl;
1565 }
1566 if (e->m_entries.count()>0) {
1567 kdDebug(7001) << " dependent entries:" << endl;
1568 Entry* d = e->m_entries.first();
1569 for(;d; d = e->m_entries.next()) {
1570 kdDebug(7001) << " " << d << endl;
1571 kdDebug(7001) << " " << d->path << " (" << d << ") " << endl;
1572 }
1573 }
1574 }
1575 }
1576}
1577
1578
1579//
1580// Class KSimpleDirWatch
1581//
1582
1583static KStaticDeleter<KSimpleDirWatch> sd_dw;
1584KSimpleDirWatch* KSimpleDirWatch::s_pSelf = 0L;
1585
1586KSimpleDirWatch* KSimpleDirWatch::self()
1587{
1588 if ( !s_pSelf ) {
1589 sd_dw.setObject( s_pSelf, new KSimpleDirWatch );
1590 }
1591
1592 return s_pSelf;
1593}
1594
1595bool KSimpleDirWatch::exists()
1596{
1597 return s_pSelf != 0;
1598}
1599
1600KSimpleDirWatch::KSimpleDirWatch (TQObject* parent, const char* name)
1601 : TQObject(parent,name)
1602{
1603 if (!name) {
1604 static int nameCounter = 0;
1605
1606 nameCounter++;
1607 setName(TQString(TQString("KSimpleDirWatch-%1").arg(nameCounter)).ascii());
1608 }
1609
1610 if (!dwp_self)
1611 dwp_self = new KSimpleDirWatchPrivate;
1612 d = dwp_self;
1613 d->ref();
1614
1615 _isStopped = false;
1616}
1617
1618KSimpleDirWatch::~KSimpleDirWatch()
1619{
1620 d->removeEntries(this);
1621 if ( d->deref() )
1622 {
1623 // delete it if it's the last one
1624 delete d;
1625 dwp_self = 0L;
1626 }
1627}
1628
1629
1630// TODO: add watchFiles/recursive support
1631void KSimpleDirWatch::addDir( const TQString& _path,
1632 bool watchFiles, bool recursive)
1633{
1634 if (watchFiles || recursive) {
1635 kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
1636 }
1637 if (d) d->addEntry(this, _path, 0, true);
1638}
1639
1640void KSimpleDirWatch::addFile( const TQString& _path )
1641{
1642 if (d) d->addEntry(this, _path, 0, false);
1643}
1644
1645TQDateTime KSimpleDirWatch::ctime( const TQString &_path )
1646{
1647 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1648
1649 if (!e)
1650 return TQDateTime();
1651
1652 TQDateTime result;
1653 result.setTime_t(e->m_ctime);
1654 return result;
1655}
1656
1657void KSimpleDirWatch::removeDir( const TQString& _path )
1658{
1659 if (d) d->removeEntry(this, _path, 0);
1660}
1661
1662void KSimpleDirWatch::removeFile( const TQString& _path )
1663{
1664 if (d) d->removeEntry(this, _path, 0);
1665}
1666
1667bool KSimpleDirWatch::stopDirScan( const TQString& _path )
1668{
1669 if (d) {
1670 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1671 if (e && e->isDir) return d->stopEntryScan(this, e);
1672 }
1673 return false;
1674}
1675
1676bool KSimpleDirWatch::restartDirScan( const TQString& _path )
1677{
1678 if (d) {
1679 KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
1680 if (e && e->isDir)
1681 // restart without notifying pending events
1682 return d->restartEntryScan(this, e, false);
1683 }
1684 return false;
1685}
1686
1687void KSimpleDirWatch::stopScan()
1688{
1689 if (d) d->stopScan(this);
1690 _isStopped = true;
1691}
1692
1693void KSimpleDirWatch::startScan( bool notify, bool skippedToo )
1694{
1695 _isStopped = false;
1696 if (d) d->startScan(this, notify, skippedToo);
1697}
1698
1699
1700bool KSimpleDirWatch::contains( const TQString& _path ) const
1701{
1702 KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
1703 if (!e)
1704 return false;
1705
1706 KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
1707 for(;c;c=e->m_clients.next())
1708 if (c->instance == this) return true;
1709
1710 return false;
1711}
1712
1713void KSimpleDirWatch::statistics()
1714{
1715 if (!dwp_self) {
1716 kdDebug(7001) << "KSimpleDirWatch not used" << endl;
1717 return;
1718 }
1719 dwp_self->statistics();
1720}
1721
1722
1723void KSimpleDirWatch::setCreated( const TQString & _file )
1724{
1725 kdDebug(7001) << name() << " emitting created " << _file << endl;
1726 emit created( _file );
1727}
1728
1729void KSimpleDirWatch::setDirty( const TQString & _file )
1730{
1731 kdDebug(7001) << name() << " emitting dirty " << _file << endl;
1732 emit dirty( _file );
1733}
1734
1735void KSimpleDirWatch::setDeleted( const TQString & _file )
1736{
1737 kdDebug(7001) << name() << " emitting deleted " << _file << endl;
1738 emit deleted( _file );
1739}
1740
1741KSimpleDirWatch::Method KSimpleDirWatch::internalMethod()
1742{
1743#ifdef HAVE_FAM
1744 if (d->use_fam)
1745 return KSimpleDirWatch::FAM;
1746#endif
1747#ifdef HAVE_INOTIFY
1748 if (d->supports_inotify)
1749 return KSimpleDirWatch::INotify;
1750#endif
1751#ifdef HAVE_DNOTIFY
1752 if (d->supports_dnotify)
1753 return KSimpleDirWatch::DNotify;
1754#endif
1755 return KSimpleDirWatch::Stat;
1756}
1757
1758
1759#include "ksimpledirwatch.moc"
1760#include "ksimpledirwatch_p.moc"
KSimpleDirWatch
KSimpleDirWatch is a basic copy of KDirWatch but with the TDEIO linking requirement removed.
Definition: ksimpledirwatch.h:67
KSimpleDirWatch::ctime
TQDateTime ctime(const TQString &path)
Returns the time the directory/file was last changed.
Definition: ksimpledirwatch.cpp:1645
KSimpleDirWatch::addFile
void addFile(const TQString &file)
Adds a file to be watched.
Definition: ksimpledirwatch.cpp:1640
KSimpleDirWatch::removeDir
void removeDir(const TQString &path)
Removes a directory from the list of scanned directories.
Definition: ksimpledirwatch.cpp:1657
KSimpleDirWatch::restartDirScan
bool restartDirScan(const TQString &path)
Restarts scanning for specified path.
Definition: ksimpledirwatch.cpp:1676
KSimpleDirWatch::setDeleted
void setDeleted(const TQString &path)
Emits deleted().
Definition: ksimpledirwatch.cpp:1735
KSimpleDirWatch::created
void created(const TQString &path)
Emitted when a file or directory is created.
KSimpleDirWatch::setCreated
void setCreated(const TQString &path)
Emits created().
Definition: ksimpledirwatch.cpp:1723
KSimpleDirWatch::deleted
void deleted(const TQString &path)
Emitted when a file or directory is deleted.
KSimpleDirWatch::statistics
static void statistics()
Dump statistic information about all KSimpleDirWatch instances.
Definition: ksimpledirwatch.cpp:1713
KSimpleDirWatch::KSimpleDirWatch
KSimpleDirWatch(TQObject *parent=0, const char *name=0)
Constructor.
Definition: ksimpledirwatch.cpp:1600
KSimpleDirWatch::dirty
void dirty(const TQString &path)
Emitted when a watched object is changed.
KSimpleDirWatch::stopDirScan
bool stopDirScan(const TQString &path)
Stops scanning the specified path.
Definition: ksimpledirwatch.cpp:1667
KSimpleDirWatch::contains
bool contains(const TQString &path) const
Check if a directory is being watched by this KSimpleDirWatch instance.
Definition: ksimpledirwatch.cpp:1700
KSimpleDirWatch::setDirty
void setDirty(const TQString &path)
Emits dirty().
Definition: ksimpledirwatch.cpp:1729
KSimpleDirWatch::addDir
void addDir(const TQString &path, bool watchFiles=false, bool recursive=false)
Adds a directory to be watched.
Definition: ksimpledirwatch.cpp:1631
KSimpleDirWatch::removeFile
void removeFile(const TQString &file)
Removes a file from the list of watched files.
Definition: ksimpledirwatch.cpp:1662
KSimpleDirWatch::exists
static bool exists()
Returns true if there is an instance of KSimpleDirWatch.
Definition: ksimpledirwatch.cpp:1595
KSimpleDirWatch::stopScan
void stopScan()
Stops scanning of all directories in internal list.
Definition: ksimpledirwatch.cpp:1687
KSimpleDirWatch::self
static KSimpleDirWatch * self()
The KSimpleDirWatch instance usually globally used in an application.
Definition: ksimpledirwatch.cpp:1586
KSimpleDirWatch::~KSimpleDirWatch
~KSimpleDirWatch()
Destructor.
Definition: ksimpledirwatch.cpp:1618
KSimpleDirWatch::internalMethod
Method internalMethod()
Returns the preferred internal method to watch for changes.
Definition: ksimpledirwatch.cpp:1741
KSimpleDirWatch::startScan
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
Definition: ksimpledirwatch.cpp:1693
KStaticDeleter
Little helper class to clean up static objects that are held as pointer.
Definition: kstaticdeleter.h:74
TDEConfigGroup
A TDEConfigBase derived class for one specific group in a TDEConfig object.
Definition: tdeconfigbase.h:2127
TDEGlobal::config
static TDEConfig * config()
Returns the general config object.
Definition: tdeglobal.cpp:65
TDEGlobal::kdWarning
kdbgstream kdWarning(int area=0)
Returns a warning stream.
Definition: kdebug.cpp:376
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
TDEGlobal::kdDebug
kdbgstream kdDebug(int area=0)
Returns a debug stream.
Definition: kdebug.cpp:371
TDEGlobal::endl
kdbgstream & endl(kdbgstream &s)
Prints an "\n".
Definition: kdebug.h:430
KNotifyClient::event
int event(const TQString &message, const TQString &text=TQString::null) TDE_DEPRECATED
Definition: knotifyclient.cpp:125
KNotifyClient::instance
TDEInstance * instance()
Shortcut to KNotifyClient::Instance::current() :)
Definition: knotifyclient.cpp:280
KStdAction::close
TDEAction * close(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
KStdAction::name
const char * name(StdAction id)

tdecore

Skip menu "tdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.9.4
This website is maintained by Timothy Pearson.