• 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 
109 static KSimpleDirWatchPrivate* dwp_self = 0;
110 
111 #ifdef HAVE_DNOTIFY
112 
113 static 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  */
123 void 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 
144 static 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  */
149 void 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 
212 KSimpleDirWatchPrivate::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) */
340 KSimpleDirWatchPrivate::~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 
365 void 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  */
471 void 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  */
488 void 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 
508 void 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 */
524 int 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 
535 KSimpleDirWatchPrivate::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
555 void 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
570 bool 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
630 bool 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
702 bool 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 
737 bool 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  */
764 void 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 
893 void 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  */
1000 void 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
1031 bool 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
1055 bool 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
1101 void 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 
1109 void 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
1124 void 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 //
1139 int 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  */
1212 void 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
1265 void 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  */
1277 void 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 
1378 bool 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
1393 void 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 
1427 void 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
1527 void KSimpleDirWatchPrivate::famEventReceived() {}
1528 #endif
1529 
1530 
1531 void 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 
1583 static KStaticDeleter<KSimpleDirWatch> sd_dw;
1584 KSimpleDirWatch* KSimpleDirWatch::s_pSelf = 0L;
1585 
1586 KSimpleDirWatch* KSimpleDirWatch::self()
1587 {
1588  if ( !s_pSelf ) {
1589  sd_dw.setObject( s_pSelf, new KSimpleDirWatch );
1590  }
1591 
1592  return s_pSelf;
1593 }
1594 
1595 bool KSimpleDirWatch::exists()
1596 {
1597  return s_pSelf != 0;
1598 }
1599 
1600 KSimpleDirWatch::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 
1618 KSimpleDirWatch::~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
1631 void 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 
1640 void KSimpleDirWatch::addFile( const TQString& _path )
1641 {
1642  if (d) d->addEntry(this, _path, 0, false);
1643 }
1644 
1645 TQDateTime 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 
1657 void KSimpleDirWatch::removeDir( const TQString& _path )
1658 {
1659  if (d) d->removeEntry(this, _path, 0);
1660 }
1661 
1662 void KSimpleDirWatch::removeFile( const TQString& _path )
1663 {
1664  if (d) d->removeEntry(this, _path, 0);
1665 }
1666 
1667 bool 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 
1676 bool 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 
1687 void KSimpleDirWatch::stopScan()
1688 {
1689  if (d) d->stopScan(this);
1690  _isStopped = true;
1691 }
1692 
1693 void KSimpleDirWatch::startScan( bool notify, bool skippedToo )
1694 {
1695  _isStopped = false;
1696  if (d) d->startScan(this, notify, skippedToo);
1697 }
1698 
1699 
1700 bool 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 
1713 void KSimpleDirWatch::statistics()
1714 {
1715  if (!dwp_self) {
1716  kdDebug(7001) << "KSimpleDirWatch not used" << endl;
1717  return;
1718  }
1719  dwp_self->statistics();
1720 }
1721 
1722 
1723 void KSimpleDirWatch::setCreated( const TQString & _file )
1724 {
1725  kdDebug(7001) << name() << " emitting created " << _file << endl;
1726  emit created( _file );
1727 }
1728 
1729 void KSimpleDirWatch::setDirty( const TQString & _file )
1730 {
1731  kdDebug(7001) << name() << " emitting dirty " << _file << endl;
1732  emit dirty( _file );
1733 }
1734 
1735 void KSimpleDirWatch::setDeleted( const TQString & _file )
1736 {
1737  kdDebug(7001) << name() << " emitting deleted " << _file << endl;
1738  emit deleted( _file );
1739 }
1740 
1741 KSimpleDirWatch::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
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
TDEGlobal::kdWarning
kdbgstream kdWarning(int area=0)
Returns a warning stream.
Definition: kdebug.cpp:376
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.1
This website is maintained by Timothy Pearson.