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