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

tdecore

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