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

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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.8.8
This website is maintained by Timothy Pearson.