• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

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

tdeio/tdeio

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

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • 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 tdeio/tdeio by doxygen 1.8.13
This website is maintained by Timothy Pearson.