• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

KDECore

  • sources
  • kde-4.14
  • kdelibs
  • kdecore
  • io
kdirwatch.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
3  Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
4  Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
5  Copyright (C) 2008 Rafal Rzepecki <divided.mind@gmail.com>
6  Copyright (C) 2010 David Faure <faure@kde.org>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License version 2 as published by the Free Software Foundation.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 
24 // CHANGES:
25 // Jul 30, 2008 - Don't follow symlinks when recursing to avoid loops (Rafal)
26 // Aug 6, 2007 - KDirWatch::WatchModes support complete, flags work fine also
27 // when using FAMD (Flavio Castelli)
28 // Aug 3, 2007 - Handled KDirWatch::WatchModes flags when using inotify, now
29 // recursive and file monitoring modes are implemented (Flavio Castelli)
30 // Jul 30, 2007 - Substituted addEntry boolean params with KDirWatch::WatchModes
31 // flag (Flavio Castelli)
32 // Oct 4, 2005 - Inotify support (Dirk Mueller)
33 // Februar 2002 - Add file watching and remote mount check for STAT
34 // Mar 30, 2001 - Native support for Linux dir change notification.
35 // Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
36 // May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
37 // May 23. 1998 - Removed static pointer - you can have more instances.
38 // It was Needed for KRegistry. KDirWatch now emits signals and doesn't
39 // call (or need) KFM. No more URL's - just plain paths. (sven)
40 // Mar 29. 1998 - added docs, stop/restart for particular Dirs and
41 // deep copies for list of dirs. (sven)
42 // Mar 28. 1998 - Created. (sven)
43 
44 #include "kdirwatch.h"
45 #include "kdirwatch_p.h"
46 #include "kfilesystemtype_p.h"
47 
48 #include <io/config-kdirwatch.h>
49 #include <config.h>
50 
51 #include <sys/stat.h>
52 #include <assert.h>
53 #include <errno.h>
54 #include <QtCore/QDir>
55 #include <QtCore/QFile>
56 #include <QtCore/QSocketNotifier>
57 #include <QtCore/QTimer>
58 #include <QtCore/QCoreApplication>
59 
60 #include <ksharedconfig.h>
61 #include <kdebug.h>
62 #include <kconfig.h>
63 #include <kglobal.h>
64 #include <kde_file.h>
65 #include <kconfiggroup.h>
66 
67 #include <stdlib.h>
68 #include <string.h>
69 
70 // debug
71 #include <sys/ioctl.h>
72 
73 
74 #include <sys/utsname.h>
75 
76 // set this to true for much more verbose debug output
77 static const bool s_verboseDebug = false;
78 
79 // The KDirWatchPrivate instance is refcounted, and deleted by the last KDirWatch instance
80 static KDirWatchPrivate* dwp_self = 0;
81 static KDirWatchPrivate* createPrivate() {
82  if (!dwp_self)
83  dwp_self = new KDirWatchPrivate;
84  return dwp_self;
85 }
86 
87 // Convert a string into a watch Method
88 static KDirWatch::Method methodFromString(const QString& method) {
89  if (method == QLatin1String("Fam")) {
90  return KDirWatch::FAM;
91  } else if (method == QLatin1String("Stat")) {
92  return KDirWatch::Stat;
93  } else if (method == QLatin1String("QFSWatch")) {
94  return KDirWatch::QFSWatch;
95  } else {
96 #ifdef Q_OS_LINUX
97  // inotify supports delete+recreate+modify, which QFSWatch doesn't support
98  return KDirWatch::INotify;
99 #else
100  return KDirWatch::QFSWatch;
101 #endif
102  }
103 }
104 
105 #ifndef NDEBUG
106 static const char* methodToString(KDirWatch::Method method)
107 {
108  switch (method) {
109  case KDirWatch::FAM:
110  return "Fam";
111  case KDirWatch::INotify:
112  return "INotify";
113  case KDirWatch::DNotify:
114  return "DNotify";
115  case KDirWatch::Stat:
116  return "Stat";
117  case KDirWatch::QFSWatch:
118  return "QFSWatch";
119  default:
120  return "ERROR!";
121  }
122 }
123 #endif
124 
125 //
126 // Class KDirWatchPrivate (singleton)
127 //
128 
129 /* All entries (files/directories) to be watched in the
130  * application (coming from multiple KDirWatch instances)
131  * are registered in a single KDirWatchPrivate instance.
132  *
133  * At the moment, the following methods for file watching
134  * are supported:
135  * - Polling: All files to be watched are polled regularly
136  * using stat (more precise: QFileInfo.lastModified()).
137  * The polling frequency is determined from global kconfig
138  * settings, defaulting to 500 ms for local directories
139  * and 5000 ms for remote mounts
140  * - FAM (File Alternation Monitor): first used on IRIX, SGI
141  * has ported this method to LINUX. It uses a kernel part
142  * (IMON, sending change events to /dev/imon) and a user
143  * level damon (fam), to which applications connect for
144  * notification of file changes. For NFS, the fam damon
145  * on the NFS server machine is used; if IMON is not built
146  * into the kernel, fam uses polling for local files.
147  * - INOTIFY: In LINUX 2.6.13, inode change notification was
148  * introduced. You're now able to watch arbitrary inode's
149  * for changes, and even get notification when they're
150  * unmounted.
151  */
152 
153 KDirWatchPrivate::KDirWatchPrivate()
154  : timer(),
155  freq( 3600000 ), // 1 hour as upper bound
156  statEntries( 0 ),
157  m_ref( 0 ),
158  delayRemove( false ),
159  rescan_all( false ),
160  rescan_timer()
161 {
162  timer.setObjectName(QLatin1String("KDirWatchPrivate::timer"));
163  connect (&timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
164 
165  KConfigGroup config(KGlobal::config(), "DirWatch");
166  m_nfsPollInterval = config.readEntry("NFSPollInterval", 5000);
167  m_PollInterval = config.readEntry("PollInterval", 500);
168 
169  QString method = config.readEntry("PreferredMethod", "inotify");
170  m_preferredMethod = methodFromString(method);
171 
172  // The nfs method defaults to the normal (local) method
173  m_nfsPreferredMethod = methodFromString(config.readEntry("nfsPreferredMethod", "Fam"));
174 
175  QList<QByteArray> availableMethods;
176 
177  availableMethods << "Stat";
178 
179  // used for FAM and inotify
180  rescan_timer.setObjectName(QString::fromLatin1("KDirWatchPrivate::rescan_timer"));
181  rescan_timer.setSingleShot( true );
182  connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
183 
184 #ifdef HAVE_FAM
185  // It's possible that FAM server can't be started
186  if (FAMOpen(&fc) ==0) {
187  availableMethods << "FAM";
188  use_fam=true;
189  sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
190  QSocketNotifier::Read, this);
191  connect( sn, SIGNAL(activated(int)),
192  this, SLOT(famEventReceived()) );
193  }
194  else {
195  kDebug(7001) << "Can't use FAM (fam daemon not running?)";
196  use_fam=false;
197  }
198 #endif
199 
200 #ifdef HAVE_SYS_INOTIFY_H
201  supports_inotify = true;
202 
203  m_inotify_fd = inotify_init();
204 
205  if ( m_inotify_fd <= 0 ) {
206  kDebug(7001) << "Can't use Inotify, kernel doesn't support it";
207  supports_inotify = false;
208  }
209 
210  {
211  struct utsname uts;
212  int major, minor, patch;
213  if (uname(&uts) < 0) {
214  supports_inotify = false;
215  kDebug(7001) << "Unable to get uname";
216  } else if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
217  supports_inotify = false;
218  kDebug(7001) << "The version is malformed: " << uts.release;
219  } else if(major == 2 && minor == 6) { // If it is 2.6 check further...
220  if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) {
221  supports_inotify = false;
222  kDebug() << "Detected 2.6 kernel but can't know more: " << uts.release;
223  } else if (major * 1000000 + minor * 1000 + patch < 2006014 ){
224  supports_inotify = false;
225  kDebug(7001) << "Can't use INotify, Linux kernel too old " << uts.release;
226  }
227  }
228  }
229 
230  kDebug(7001) << "INotify available: " << supports_inotify;
231  if ( supports_inotify ) {
232  availableMethods << "INotify";
233  (void)fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
234 
235  mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this );
236  connect( mSn, SIGNAL(activated(int)),
237  this, SLOT(inotifyEventReceived()) );
238  }
239 #endif
240 #ifdef HAVE_QFILESYSTEMWATCHER
241  availableMethods << "QFileSystemWatcher";
242  fsWatcher = 0;
243 #endif
244 #ifndef NDEBUG
245  kDebug(7001) << "Available methods: " << availableMethods << "preferred=" << methodToString(m_preferredMethod);
246 #endif
247 }
248 
249 // This is called on app exit (when K_GLOBAL_STATIC deletes KDirWatch::self)
250 KDirWatchPrivate::~KDirWatchPrivate()
251 {
252  timer.stop();
253 
254  /* remove all entries being watched */
255  removeEntries(0);
256 
257 #ifdef HAVE_FAM
258  if (use_fam) {
259  FAMClose(&fc);
260  }
261 #endif
262 #ifdef HAVE_SYS_INOTIFY_H
263  if ( supports_inotify )
264  ::close( m_inotify_fd );
265 #endif
266 #ifdef HAVE_QFILESYSTEMWATCHER
267  delete fsWatcher;
268 #endif
269 }
270 
271 void KDirWatchPrivate::inotifyEventReceived()
272 {
273  //kDebug(7001);
274 #ifdef HAVE_SYS_INOTIFY_H
275  if ( !supports_inotify )
276  return;
277 
278  int pending = -1;
279  int offsetStartRead = 0; // where we read into buffer
280  char buf[8192];
281  assert( m_inotify_fd > -1 );
282  ioctl( m_inotify_fd, FIONREAD, &pending );
283 
284  while ( pending > 0 ) {
285 
286  const int bytesToRead = qMin( pending, (int)sizeof( buf ) - offsetStartRead );
287 
288  int bytesAvailable = read( m_inotify_fd, &buf[offsetStartRead], bytesToRead );
289  pending -= bytesAvailable;
290  bytesAvailable += offsetStartRead;
291  offsetStartRead = 0;
292 
293  int offsetCurrent = 0;
294  while ( bytesAvailable >= (int)sizeof( struct inotify_event ) ) {
295  const struct inotify_event * const event = (struct inotify_event *) &buf[offsetCurrent];
296  const int eventSize = sizeof( struct inotify_event ) + event->len;
297  if ( bytesAvailable < eventSize ) {
298  break;
299  }
300 
301  bytesAvailable -= eventSize;
302  offsetCurrent += eventSize;
303 
304  QString path;
305  QByteArray cpath(event->name, event->len);
306  if(event->len)
307  path = QFile::decodeName ( cpath );
308 
309  if ( path.length() && isNoisyFile( cpath ) )
310  continue;
311 
312  // Is set to true if the new event is a directory, false otherwise. This prevents a stat call in clientsForFileOrDir
313  const bool isDir = (event->mask & (IN_ISDIR));
314 
315  // now we're in deep trouble of finding the
316  // associated entries
317  // for now, we suck and iterate
318  for ( EntryMap::Iterator it = m_mapEntries.begin();
319  it != m_mapEntries.end(); ) {
320 
321  Entry* e = &( *it );
322  ++it;
323  if ( e->wd == event->wd ) {
324  const bool wasDirty = e->dirty;
325  e->dirty = true;
326 
327  const QString tpath = e->path + QLatin1Char('/') + path;
328 
329  //if (s_verboseDebug) {
330  // kDebug(7001) << "got event" << "0x"+QString::number(event->mask, 16) << "for" << e->path;
331  //}
332 
333  if( event->mask & IN_DELETE_SELF) {
334  if (s_verboseDebug) {
335  kDebug(7001) << "-->got deleteself signal for" << e->path;
336  }
337  e->m_status = NonExistent;
338  e->wd = -1;
339  e->m_ctime = invalid_ctime;
340  emitEvent(e, Deleted, e->path);
341  // If the parent dir was already watched, tell it something changed
342  Entry* parentEntry = entry(e->parentDirectory());
343  if (parentEntry)
344  parentEntry->dirty = true;
345  // Add entry to parent dir to notice if the entry gets recreated
346  addEntry(0, e->parentDirectory(), e, true /*isDir*/);
347  }
348  if ( event->mask & IN_IGNORED ) {
349  // Causes bug #207361 with kernels 2.6.31 and 2.6.32!
350  //e->wd = -1;
351  }
352  if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
353  Entry* sub_entry = e->findSubEntry(tpath);
354 
355  if (s_verboseDebug) {
356  kDebug(7001) << "-->got CREATE signal for" << (tpath) << "sub_entry=" << sub_entry;
357  kDebug(7001) << *e;
358  }
359 
360  // The code below is very similar to the one in checkFAMEvent...
361  if (sub_entry) {
362  // We were waiting for this new file/dir to be created
363  sub_entry->dirty = true;
364  rescan_timer.start(0); // process this asap, to start watching that dir
365  } else if (e->isDir && !e->m_clients.empty()) {
366  const QList<Client *> clients = e->inotifyClientsForFileOrDir(isDir);
367  Q_FOREACH(Client *client, clients) {
368  // See discussion in addEntry for why we don't addEntry for individual
369  // files in WatchFiles mode with inotify.
370  if (isDir) {
371  addEntry(client->instance, tpath, 0, isDir,
372  isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
373  }
374  }
375  if (!clients.isEmpty()) {
376  emitEvent(e, Created, tpath);
377  kDebug(7001).nospace() << clients.count() << " instance(s) monitoring the new "
378  << (isDir ? "dir " : "file ") << tpath;
379  }
380  e->m_pendingFileChanges.append(e->path);
381  if (!rescan_timer.isActive())
382  rescan_timer.start(m_PollInterval); // singleshot
383  }
384  }
385  if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
386  if (s_verboseDebug) {
387  kDebug(7001) << "-->got DELETE signal for" << tpath;
388  }
389  if ((e->isDir) && (!e->m_clients.empty())) {
390  Client* client = 0;
391  // A file in this directory has been removed. It wasn't an explicitly
392  // watched file as it would have its own watch descriptor, so
393  // no addEntry/ removeEntry bookkeeping should be required. Emit
394  // the event immediately if any clients are interested.
395  KDirWatch::WatchModes flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
396  int counter = 0;
397  Q_FOREACH(client, e->m_clients) { // krazy:exclude=foreach
398  if (client->m_watchModes & flag) {
399  counter++;
400  }
401  }
402  if (counter != 0) {
403  emitEvent(e, Deleted, tpath);
404  }
405  }
406  }
407  if (event->mask & (IN_MODIFY|IN_ATTRIB)) {
408  if ((e->isDir) && (!e->m_clients.empty())) {
409  if (s_verboseDebug) {
410  kDebug(7001) << "-->got MODIFY signal for" << (tpath);
411  }
412  // A file in this directory has been changed. No
413  // addEntry/ removeEntry bookkeeping should be required.
414  // Add the path to the list of pending file changes if
415  // there are any interested clients.
416  //KDE_struct_stat stat_buf;
417  //QByteArray tpath = QFile::encodeName(e->path+'/'+path);
418  //KDE_stat(tpath, &stat_buf);
419  //bool isDir = S_ISDIR(stat_buf.st_mode);
420 
421  // The API doc is somewhat vague as to whether we should emit
422  // dirty() for implicitly watched files when WatchFiles has
423  // not been specified - we'll assume they are always interested,
424  // regardless.
425  // Don't worry about duplicates for the time
426  // being; this is handled in slotRescan.
427  e->m_pendingFileChanges.append(tpath);
428  // Avoid stat'ing the directory if only an entry inside it changed.
429  e->dirty = (wasDirty || (path.isEmpty() && (event->mask & IN_ATTRIB)));
430  }
431  }
432 
433  if (!rescan_timer.isActive())
434  rescan_timer.start(m_PollInterval); // singleshot
435 
436  break;
437  }
438  }
439  }
440  if (bytesAvailable > 0) {
441  // copy partial event to beginning of buffer
442  memmove(buf, &buf[offsetCurrent], bytesAvailable);
443  offsetStartRead = bytesAvailable;
444  }
445  }
446 #endif
447 }
448 
449 /* In FAM mode, only entries which are marked dirty are scanned.
450  * We first need to mark all yet nonexistent, but possible created
451  * entries as dirty...
452  */
453 void KDirWatchPrivate::Entry::propagate_dirty()
454 {
455  foreach(Entry *sub_entry, m_entries)
456  {
457  if (!sub_entry->dirty)
458  {
459  sub_entry->dirty = true;
460  sub_entry->propagate_dirty();
461  }
462  }
463 }
464 
465 
466 /* A KDirWatch instance is interested in getting events for
467  * this file/Dir entry.
468  */
469 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance,
470  KDirWatch::WatchModes watchModes)
471 {
472  if (instance == 0)
473  return;
474 
475  foreach(Client* client, m_clients) {
476  if (client->instance == instance) {
477  client->count++;
478  client->m_watchModes = watchModes;
479  return;
480  }
481  }
482 
483  Client* client = new Client;
484  client->instance = instance;
485  client->count = 1;
486  client->watchingStopped = instance->isStopped();
487  client->pending = NoChange;
488  client->m_watchModes = watchModes;
489 
490  m_clients.append(client);
491 }
492 
493 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
494 {
495  QList<Client *>::iterator it = m_clients.begin();
496  const QList<Client *>::iterator end = m_clients.end();
497  for ( ; it != end ; ++it ) {
498  Client* client = *it;
499  if (client->instance == instance) {
500  client->count--;
501  if (client->count == 0) {
502  m_clients.erase(it);
503  delete client;
504  }
505  return;
506  }
507  }
508 }
509 
510 /* get number of clients */
511 int KDirWatchPrivate::Entry::clientCount() const
512 {
513  int clients = 0;
514  foreach(Client* client, m_clients)
515  clients += client->count;
516 
517  return clients;
518 }
519 
520 QString KDirWatchPrivate::Entry::parentDirectory() const
521 {
522  return QDir::cleanPath(path + QLatin1String("/.."));
523 }
524 
525 QList<KDirWatchPrivate::Client *> KDirWatchPrivate::Entry::clientsForFileOrDir(const QString& tpath, bool* isDir) const
526 {
527  QList<Client *> ret;
528  KDE_struct_stat stat_buf;
529  if (KDE::stat(tpath, &stat_buf) == 0) {
530  *isDir = S_ISDIR(stat_buf.st_mode);
531  const KDirWatch::WatchModes flag =
532  *isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
533  Q_FOREACH(Client *client, this->m_clients) {
534  if (client->m_watchModes & flag) {
535  ret.append(client);
536  }
537  }
538  } else {
539  // Happens frequently, e.g. ERROR: couldn't stat "/home/dfaure/.viminfo.tmp"
540  //kDebug(7001) << "ERROR: couldn't stat" << tpath;
541  }
542  // If KDE_stat fails then isDir is not set, but ret is empty anyway
543  // so isDir won't be used.
544  return ret;
545 }
546 
547 // inotify specific function that doesn't call KDE::stat to figure out if we have a file or folder.
548 // isDir is determined through inotify's "IN_ISDIR" flag in KDirWatchPrivate::inotifyEventReceived
549 QList<KDirWatchPrivate::Client *> KDirWatchPrivate::Entry::inotifyClientsForFileOrDir(bool isDir) const
550 {
551  QList<Client *> ret;
552  const KDirWatch::WatchModes flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles;
553  Q_FOREACH(Client *client, this->m_clients) {
554  if (client->m_watchModes & flag) {
555  ret.append(client);
556  }
557  }
558  return ret;
559 }
560 
561 QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
562 {
563  debug.nospace() << "[ Entry for " << entry.path << ", " << (entry.isDir ? "dir" : "file");
564  if (entry.m_status == KDirWatchPrivate::NonExistent)
565  debug << ", non-existent";
566  debug << ", using " << ((entry.m_mode == KDirWatchPrivate::FAMMode) ? "FAM" :
567  (entry.m_mode == KDirWatchPrivate::INotifyMode) ? "INotify" :
568  (entry.m_mode == KDirWatchPrivate::DNotifyMode) ? "DNotify" :
569  (entry.m_mode == KDirWatchPrivate::QFSWatchMode) ? "QFSWatch" :
570  (entry.m_mode == KDirWatchPrivate::StatMode) ? "Stat" : "Unknown Method");
571 #ifdef HAVE_SYS_INOTIFY_H
572  if (entry.m_mode == KDirWatchPrivate::INotifyMode)
573  debug << " inotify_wd=" << entry.wd;
574 #endif
575  debug << ", has " << entry.m_clients.count() << " clients";
576  debug.space();
577  if (!entry.m_entries.isEmpty()) {
578  debug << ", nonexistent subentries:";
579  Q_FOREACH(KDirWatchPrivate::Entry* subEntry, entry.m_entries)
580  debug << subEntry << subEntry->path;
581  }
582  debug << ']';
583  return debug;
584 }
585 
586 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
587 {
588 // we only support absolute paths
589  if (_path.isEmpty() || QDir::isRelativePath(_path)) {
590  return 0;
591  }
592 
593  QString path (_path);
594 
595  if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
596  path.truncate( path.length() - 1 );
597 
598  EntryMap::Iterator it = m_mapEntries.find( path );
599  if ( it == m_mapEntries.end() )
600  return 0;
601  else
602  return &(*it);
603 }
604 
605 // set polling frequency for a entry and adjust global freq if needed
606 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
607 {
608  e->freq = newFreq;
609 
610  // a reasonable frequency for the global polling timer
611  if (e->freq < freq) {
612  freq = e->freq;
613  if (timer.isActive()) timer.start(freq);
614  kDebug(7001) << "Global Poll Freq is now" << freq << "msec";
615  }
616 }
617 
618 
619 #if defined(HAVE_FAM)
620 // setup FAM notification, returns false if not possible
621 bool KDirWatchPrivate::useFAM(Entry* e)
622 {
623  if (!use_fam) return false;
624 
625  // handle FAM events to avoid deadlock
626  // (FAM sends back all files in a directory when monitoring)
627  famEventReceived();
628 
629  e->m_mode = FAMMode;
630  e->dirty = false;
631 
632  if (e->isDir) {
633  if (e->m_status == NonExistent) {
634  // If the directory does not exist we watch the parent directory
635  addEntry(0, e->parentDirectory(), e, true);
636  }
637  else {
638  int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
639  &(e->fr), e);
640  if (res<0) {
641  e->m_mode = UnknownMode;
642  use_fam=false;
643  delete sn; sn = 0;
644  return false;
645  }
646  kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
647  << ") for " << e->path;
648  }
649  }
650  else {
651  if (e->m_status == NonExistent) {
652  // If the file does not exist we watch the directory
653  addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
654  }
655  else {
656  int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
657  &(e->fr), e);
658  if (res<0) {
659  e->m_mode = UnknownMode;
660  use_fam=false;
661  delete sn; sn = 0;
662  return false;
663  }
664 
665  kDebug(7001).nospace() << " Setup FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
666  << ") for " << e->path;
667  }
668  }
669 
670  // handle FAM events to avoid deadlock
671  // (FAM sends back all files in a directory when monitoring)
672  famEventReceived();
673 
674  return true;
675 }
676 #endif
677 
678 #ifdef HAVE_SYS_INOTIFY_H
679 // setup INotify notification, returns false if not possible
680 bool KDirWatchPrivate::useINotify( Entry* e )
681 {
682  //kDebug (7001) << "trying to use inotify for monitoring";
683 
684  e->wd = -1;
685  e->dirty = false;
686 
687  if (!supports_inotify) return false;
688 
689  e->m_mode = INotifyMode;
690 
691  if ( e->m_status == NonExistent ) {
692  addEntry(0, e->parentDirectory(), e, true);
693  return true;
694  }
695 
696  // May as well register for almost everything - it's free!
697  int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW|IN_MOVED_FROM|IN_MODIFY|IN_ATTRIB;
698 
699  if ( ( e->wd = inotify_add_watch( m_inotify_fd,
700  QFile::encodeName( e->path ), mask) ) >= 0)
701  {
702  if (s_verboseDebug) {
703  kDebug(7001) << "inotify successfully used for monitoring" << e->path << "wd=" << e->wd;
704  }
705  return true;
706  }
707 
708  kDebug(7001) << "inotify failed for monitoring" << e->path << ":" << strerror(errno);
709  return false;
710 }
711 #endif
712 #ifdef HAVE_QFILESYSTEMWATCHER
713 bool KDirWatchPrivate::useQFSWatch(Entry* e)
714 {
715  e->m_mode = QFSWatchMode;
716  e->dirty = false;
717 
718  if ( e->m_status == NonExistent ) {
719  addEntry(0, e->parentDirectory(), e, true /*isDir*/);
720  return true;
721  }
722 
723  kDebug(7001) << "fsWatcher->addPath" << e->path;
724  if (!fsWatcher) {
725  fsWatcher = new KFileSystemWatcher();
726  connect(fsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(fswEventReceived(QString)));
727  connect(fsWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fswEventReceived(QString)));
728  }
729  fsWatcher->addPath( e->path );
730  return true;
731 }
732 #endif
733 
734 bool KDirWatchPrivate::useStat(Entry* e)
735 {
736  if (KFileSystemType::fileSystemType(e->path) == KFileSystemType::Nfs) // TODO: or Smbfs?
737  useFreq(e, m_nfsPollInterval);
738  else
739  useFreq(e, m_PollInterval);
740 
741  if (e->m_mode != StatMode) {
742  e->m_mode = StatMode;
743  statEntries++;
744 
745  if ( statEntries == 1 ) {
746  // if this was first STAT entry (=timer was stopped)
747  timer.start(freq); // then start the timer
748  kDebug(7001) << " Started Polling Timer, freq " << freq;
749  }
750  }
751 
752  kDebug(7001) << " Setup Stat (freq " << e->freq << ") for " << e->path;
753 
754  return true;
755 }
756 
757 
758 /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
759  * providing in <isDir> the type of the entry to be watched.
760  * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
761  * this entry needs another entry to watch himself (when notExistent).
762  */
763 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
764  Entry* sub_entry, bool isDir, KDirWatch::WatchModes watchModes)
765 {
766  QString path (_path);
767  if (path.isEmpty()
768 #ifndef Q_WS_WIN
769  || path == QLatin1String("/dev")
770  || (path.startsWith(QLatin1String("/dev/")) && !path.startsWith(QLatin1String("/dev/."))
771  && !path.startsWith(QLatin1String("/dev/shm")))
772 #endif
773  )
774  return; // Don't even go there.
775 
776  if ( path.length() > 1 && path.endsWith( QLatin1Char( '/' ) ) )
777  path.truncate( path.length() - 1 );
778 
779  EntryMap::Iterator it = m_mapEntries.find( path );
780  if ( it != m_mapEntries.end() )
781  {
782  if (sub_entry) {
783  (*it).m_entries.append(sub_entry);
784  if (s_verboseDebug) {
785  kDebug(7001) << "Added already watched Entry" << path
786  << "(for" << sub_entry->path << ")";
787  }
788 #ifdef HAVE_SYS_INOTIFY_H
789  Entry* e = &(*it);
790  if( (e->m_mode == INotifyMode) && (e->wd >= 0) ) {
791  int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
792  if(!e->isDir)
793  mask |= IN_MODIFY|IN_ATTRIB;
794  else
795  mask |= IN_ONLYDIR;
796 
797  inotify_rm_watch (m_inotify_fd, e->wd);
798  e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ),
799  mask);
800  //Q_ASSERT(e->wd >= 0); // fails in KDirListerTest::testDeleteCurrentDir
801  }
802 #endif
803  }
804  else {
805  (*it).addClient(instance, watchModes);
806  if (s_verboseDebug) {
807  kDebug(7001) << "Added already watched Entry" << path
808  << "(now" << (*it).clientCount() << "clients)"
809  << QString::fromLatin1("[%1]").arg(instance->objectName());
810  }
811  }
812  return;
813  }
814 
815  // we have a new path to watch
816 
817  KDE_struct_stat stat_buf;
818  bool exists = (KDE::stat(path, &stat_buf) == 0);
819 
820  EntryMap::iterator newIt = m_mapEntries.insert( path, Entry() );
821  // the insert does a copy, so we have to use <e> now
822  Entry* e = &(*newIt);
823 
824  if (exists) {
825  e->isDir = S_ISDIR(stat_buf.st_mode);
826 
827  if (e->isDir && !isDir) {
828  if (KDE::lstat(path, &stat_buf) == 0) {
829  if (S_ISLNK(stat_buf.st_mode))
830  // if it's a symlink, don't follow it
831  e->isDir = false;
832  else
833  qWarning() << "KDirWatch:" << path << "is a directory. Use addDir!";
834  }
835  } else if (!e->isDir && isDir)
836  qWarning("KDirWatch: %s is a file. Use addFile!", qPrintable(path));
837 
838  if (!e->isDir && ( watchModes != KDirWatch::WatchDirOnly)) {
839  qWarning() << "KDirWatch:" << path << "is a file. You can't use recursive or "
840  "watchFiles options";
841  watchModes = KDirWatch::WatchDirOnly;
842  }
843 
844 #ifdef Q_OS_WIN
845  // ctime is the 'creation time' on windows - use mtime instead
846  e->m_ctime = stat_buf.st_mtime;
847 #else
848  e->m_ctime = stat_buf.st_ctime;
849 #endif
850  e->m_status = Normal;
851  e->m_nlink = stat_buf.st_nlink;
852  e->m_ino = stat_buf.st_ino;
853  }
854  else {
855  e->isDir = isDir;
856  e->m_ctime = invalid_ctime;
857  e->m_status = NonExistent;
858  e->m_nlink = 0;
859  e->m_ino = 0;
860  }
861 
862  e->path = path;
863  if (sub_entry)
864  e->m_entries.append(sub_entry);
865  else
866  e->addClient(instance, watchModes);
867 
868  kDebug(7001).nospace() << "Added " << (e->isDir ? "Dir " : "File ") << path
869  << (e->m_status == NonExistent ? " NotExisting" : "")
870  << " for " << (sub_entry ? sub_entry->path : QString())
871  << " [" << (instance ? instance->objectName() : QString()) << "]";
872 
873  // now setup the notification method
874  e->m_mode = UnknownMode;
875  e->msecLeft = 0;
876 
877  if ( isNoisyFile( QFile::encodeName( path ) ) )
878  return;
879 
880  if (exists && e->isDir && (watchModes != KDirWatch::WatchDirOnly)) {
881  QFlags<QDir::Filter> filters = QDir::NoDotAndDotDot;
882 
883  if ((watchModes & KDirWatch::WatchSubDirs) &&
884  (watchModes & KDirWatch::WatchFiles)) {
885  filters |= (QDir::Dirs|QDir::Files);
886  } else if (watchModes & KDirWatch::WatchSubDirs) {
887  filters |= QDir::Dirs;
888  } else if (watchModes & KDirWatch::WatchFiles) {
889  filters |= QDir::Files;
890  }
891 
892 #if defined(HAVE_SYS_INOTIFY_H)
893  if (e->m_mode == INotifyMode || (e->m_mode == UnknownMode && m_preferredMethod == KDirWatch::INotify) )
894  {
895  //kDebug(7001) << "Ignoring WatchFiles directive - this is implicit with inotify";
896  // Placing a watch on individual files is redundant with inotify
897  // (inotify gives us WatchFiles functionality "for free") and indeed
898  // actively harmful, so prevent it. WatchSubDirs is necessary, though.
899  filters &= ~QDir::Files;
900  }
901 #endif
902 
903  QDir basedir (e->path);
904  const QFileInfoList contents = basedir.entryInfoList(filters);
905  for (QFileInfoList::const_iterator iter = contents.constBegin();
906  iter != contents.constEnd(); ++iter)
907  {
908  const QFileInfo &fileInfo = *iter;
909  // treat symlinks as files--don't follow them.
910  bool isDir = fileInfo.isDir() && !fileInfo.isSymLink();
911 
912  addEntry (instance, fileInfo.absoluteFilePath(), 0, isDir,
913  isDir ? watchModes : KDirWatch::WatchDirOnly);
914  }
915  }
916 
917  addWatch(e);
918 }
919 
920 void KDirWatchPrivate::addWatch(Entry* e)
921 {
922  // If the watch is on a network filesystem use the nfsPreferredMethod as the
923  // default, otherwise use preferredMethod as the default, if the methods are
924  // the same we can skip the mountpoint check
925 
926  // This allows to configure a different method for NFS mounts, since inotify
927  // cannot detect changes made by other machines. However as a default inotify
928  // is fine, since the most common case is a NFS-mounted home, where all changes
929  // are made locally. #177892.
930  KDirWatch::Method preferredMethod = m_preferredMethod;
931  if (m_nfsPreferredMethod != m_preferredMethod) {
932  if (KFileSystemType::fileSystemType(e->path) == KFileSystemType::Nfs) {
933  preferredMethod = m_nfsPreferredMethod;
934  }
935  }
936 
937  // Try the appropriate preferred method from the config first
938  bool entryAdded = false;
939  switch (preferredMethod) {
940 #if defined(HAVE_FAM)
941  case KDirWatch::FAM: entryAdded = useFAM(e); break;
942 #endif
943 #if defined(HAVE_SYS_INOTIFY_H)
944  case KDirWatch::INotify: entryAdded = useINotify(e); break;
945 #endif
946 #if defined(HAVE_QFILESYSTEMWATCHER)
947  case KDirWatch::QFSWatch: entryAdded = useQFSWatch(e); break;
948 #endif
949  case KDirWatch::Stat: entryAdded = useStat(e); break;
950  default: break;
951  }
952 
953  // Failing that try in order INotify, FAM, QFSWatch, Stat
954  if (!entryAdded) {
955 #if defined(HAVE_SYS_INOTIFY_H)
956  if (useINotify(e)) return;
957 #endif
958 #if defined(HAVE_FAM)
959  if (useFAM(e)) return;
960 #endif
961 #if defined(HAVE_QFILESYSTEMWATCHER)
962  if (useQFSWatch(e)) return;
963 #endif
964  useStat(e);
965  }
966 }
967 
968 void KDirWatchPrivate::removeWatch(Entry* e)
969 {
970 #ifdef HAVE_FAM
971  if (e->m_mode == FAMMode) {
972  FAMCancelMonitor(&fc, &(e->fr) );
973  kDebug(7001).nospace() << "Cancelled FAM (Req " << FAMREQUEST_GETREQNUM(&(e->fr))
974  << ") for " << e->path;
975  }
976 #endif
977 #ifdef HAVE_SYS_INOTIFY_H
978  if (e->m_mode == INotifyMode) {
979  (void) inotify_rm_watch( m_inotify_fd, e->wd );
980  if (s_verboseDebug) {
981  kDebug(7001).nospace() << "Cancelled INotify (fd " << m_inotify_fd << ", "
982  << e->wd << ") for " << e->path;
983  }
984  }
985 #endif
986 #ifdef HAVE_QFILESYSTEMWATCHER
987  if (e->m_mode == QFSWatchMode && fsWatcher) {
988  if (s_verboseDebug)
989  kDebug(7001) << "fsWatcher->removePath" << e->path;
990  fsWatcher->removePath(e->path);
991  }
992 #endif
993 }
994 
995 bool KDirWatchPrivate::removeEntry(KDirWatch* instance,
996  const QString& _path,
997  Entry* sub_entry)
998 {
999  if (s_verboseDebug) {
1000  kDebug(7001) << "path=" << _path << "sub_entry:" << sub_entry;
1001  }
1002  Entry* e = entry(_path);
1003  if (!e) {
1004  return false;
1005  }
1006 
1007  removeEntry(instance, e, sub_entry);
1008  return true;
1009 }
1010 
1011 void KDirWatchPrivate::removeEntry(KDirWatch* instance,
1012  Entry* e,
1013  Entry* sub_entry)
1014 {
1015  removeList.remove(e);
1016 
1017  if (sub_entry)
1018  e->m_entries.removeAll(sub_entry);
1019  else
1020  e->removeClient(instance);
1021 
1022  if (e->m_clients.count() || e->m_entries.count())
1023  return;
1024 
1025  if (delayRemove) {
1026  removeList.insert(e);
1027  // now e->isValid() is false
1028  return;
1029  }
1030 
1031  if ( e->m_status == Normal) {
1032  removeWatch(e);
1033  } else {
1034  // Removed a NonExistent entry - we just remove it from the parent
1035  if (e->isDir)
1036  removeEntry(0, e->parentDirectory(), e);
1037  else
1038  removeEntry(0, QFileInfo(e->path).absolutePath(), e);
1039  }
1040 
1041  if (e->m_mode == StatMode) {
1042  statEntries--;
1043  if ( statEntries == 0 ) {
1044  timer.stop(); // stop timer if lists are empty
1045  kDebug(7001) << " Stopped Polling Timer";
1046  }
1047  }
1048 
1049  if (s_verboseDebug) {
1050  kDebug(7001).nospace() << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
1051  << " for " << (sub_entry ? sub_entry->path : QString())
1052  << " [" << (instance ? instance->objectName() : QString()) << "]";
1053  }
1054  m_mapEntries.remove( e->path ); // <e> not valid any more
1055 }
1056 
1057 
1058 /* Called from KDirWatch destructor:
1059  * remove <instance> as client from all entries
1060  */
1061 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
1062 {
1063  int minfreq = 3600000;
1064 
1065  QStringList pathList;
1066  // put all entries where instance is a client in list
1067  EntryMap::Iterator it = m_mapEntries.begin();
1068  for( ; it != m_mapEntries.end(); ++it ) {
1069  Client* c = 0;
1070  foreach(Client* client, (*it).m_clients) {
1071  if (client->instance == instance) {
1072  c = client;
1073  break;
1074  }
1075  }
1076  if (c) {
1077  c->count = 1; // forces deletion of instance as client
1078  pathList.append((*it).path);
1079  }
1080  else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
1081  minfreq = (*it).freq;
1082  }
1083 
1084  foreach(const QString &path, pathList)
1085  removeEntry(instance, path, 0);
1086 
1087  if (minfreq > freq) {
1088  // we can decrease the global polling frequency
1089  freq = minfreq;
1090  if (timer.isActive()) timer.start(freq);
1091  kDebug(7001) << "Poll Freq now" << freq << "msec";
1092  }
1093 }
1094 
1095 // instance ==0: stop scanning for all instances
1096 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
1097 {
1098  int stillWatching = 0;
1099  foreach(Client* client, e->m_clients) {
1100  if (!instance || instance == client->instance)
1101  client->watchingStopped = true;
1102  else if (!client->watchingStopped)
1103  stillWatching += client->count;
1104  }
1105 
1106  kDebug(7001) << (instance ? instance->objectName() : QString::fromLatin1("all"))
1107  << "stopped scanning" << e->path << "(now"
1108  << stillWatching << "watchers)";
1109 
1110  if (stillWatching == 0) {
1111  // if nobody is interested, we don't watch
1112  if ( e->m_mode != INotifyMode ) {
1113  e->m_ctime = invalid_ctime; // invalid
1114  e->m_status = NonExistent;
1115  }
1116  // e->m_status = Normal;
1117  }
1118  return true;
1119 }
1120 
1121 // instance ==0: start scanning for all instances
1122 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
1123  bool notify)
1124 {
1125  int wasWatching = 0, newWatching = 0;
1126  foreach(Client* client, e->m_clients) {
1127  if (!client->watchingStopped)
1128  wasWatching += client->count;
1129  else if (!instance || instance == client->instance) {
1130  client->watchingStopped = false;
1131  newWatching += client->count;
1132  }
1133  }
1134  if (newWatching == 0)
1135  return false;
1136 
1137  kDebug(7001) << (instance ? instance->objectName() : QString::fromLatin1("all"))
1138  << "restarted scanning" << e->path
1139  << "(now" << wasWatching+newWatching << "watchers)";
1140 
1141  // restart watching and emit pending events
1142 
1143  int ev = NoChange;
1144  if (wasWatching == 0) {
1145  if (!notify) {
1146  KDE_struct_stat stat_buf;
1147  bool exists = (KDE::stat(e->path, &stat_buf) == 0);
1148  if (exists) {
1149 #ifdef Q_OS_WIN
1150  // ctime is the 'creation time' on windows - use mtime instead
1151  e->m_ctime = stat_buf.st_mtime;
1152 #else
1153  e->m_ctime = stat_buf.st_ctime;
1154 #endif
1155  e->m_status = Normal;
1156  if (s_verboseDebug) {
1157  kDebug(7001) << "Setting status to Normal for" << e << e->path;
1158  }
1159  e->m_nlink = stat_buf.st_nlink;
1160  e->m_ino = stat_buf.st_ino;
1161 
1162  // Same as in scanEntry: ensure no subentry in parent dir
1163  removeEntry(0, e->parentDirectory(), e);
1164  }
1165  else {
1166  e->m_ctime = invalid_ctime;
1167  e->m_status = NonExistent;
1168  e->m_nlink = 0;
1169  if (s_verboseDebug) {
1170  kDebug(7001) << "Setting status to NonExistent for" << e << e->path;
1171  }
1172  }
1173  }
1174  e->msecLeft = 0;
1175  ev = scanEntry(e);
1176  }
1177  emitEvent(e,ev);
1178 
1179  return true;
1180 }
1181 
1182 // instance ==0: stop scanning for all instances
1183 void KDirWatchPrivate::stopScan(KDirWatch* instance)
1184 {
1185  EntryMap::Iterator it = m_mapEntries.begin();
1186  for( ; it != m_mapEntries.end(); ++it )
1187  stopEntryScan(instance, &(*it));
1188 }
1189 
1190 
1191 void KDirWatchPrivate::startScan(KDirWatch* instance,
1192  bool notify, bool skippedToo )
1193 {
1194  if (!notify)
1195  resetList(instance,skippedToo);
1196 
1197  EntryMap::Iterator it = m_mapEntries.begin();
1198  for( ; it != m_mapEntries.end(); ++it )
1199  restartEntryScan(instance, &(*it), notify);
1200 
1201  // timer should still be running when in polling mode
1202 }
1203 
1204 
1205 // clear all pending events, also from stopped
1206 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, bool skippedToo )
1207 {
1208  EntryMap::Iterator it = m_mapEntries.begin();
1209  for( ; it != m_mapEntries.end(); ++it ) {
1210 
1211  foreach(Client* client, (*it).m_clients) {
1212  if (!client->watchingStopped || skippedToo)
1213  client->pending = NoChange;
1214  }
1215  }
1216 }
1217 
1218 // Return event happened on <e>
1219 //
1220 int KDirWatchPrivate::scanEntry(Entry* e)
1221 {
1222  // Shouldn't happen: Ignore "unknown" notification method
1223  if (e->m_mode == UnknownMode) return NoChange;
1224 
1225  if (e->m_mode == FAMMode || e->m_mode == INotifyMode) {
1226  // we know nothing has changed, no need to stat
1227  if(!e->dirty) return NoChange;
1228  e->dirty = false;
1229  }
1230 
1231  if (e->m_mode == StatMode) {
1232  // only scan if timeout on entry timer happens;
1233  // e.g. when using 500msec global timer, a entry
1234  // with freq=5000 is only watched every 10th time
1235 
1236  e->msecLeft -= freq;
1237  if (e->msecLeft>0) return NoChange;
1238  e->msecLeft += e->freq;
1239  }
1240 
1241  KDE_struct_stat stat_buf;
1242  const bool exists = (KDE::stat(e->path, &stat_buf) == 0);
1243  if (exists) {
1244 
1245  if (e->m_status == NonExistent) {
1246  // ctime is the 'creation time' on windows, but with qMax
1247  // we get the latest change of any kind, on any platform.
1248  e->m_ctime = qMax(stat_buf.st_ctime, stat_buf.st_mtime);
1249  e->m_status = Normal;
1250  e->m_ino = stat_buf.st_ino;
1251  if (s_verboseDebug) {
1252  kDebug(7001) << "Setting status to Normal for just created" << e << e->path;
1253  }
1254  // We need to make sure the entry isn't listed in its parent's subentries... (#222974, testMoveTo)
1255  removeEntry(0, e->parentDirectory(), e);
1256 
1257  return Created;
1258  }
1259 
1260 #if 1 // for debugging the if() below
1261  if (s_verboseDebug) {
1262  struct tm* tmp = localtime(&e->m_ctime);
1263  char outstr[200];
1264  strftime(outstr, sizeof(outstr), "%T", tmp);
1265  kDebug(7001) << "e->m_ctime=" << e->m_ctime << outstr
1266  << "stat_buf.st_ctime=" << stat_buf.st_ctime
1267  << "e->m_nlink=" << e->m_nlink
1268  << "stat_buf.st_nlink=" << stat_buf.st_nlink
1269  << "e->m_ino=" << e->m_ino
1270  << "stat_buf.st_ino=" << stat_buf.st_ino;
1271  }
1272 #endif
1273 
1274  if ( ((e->m_ctime != invalid_ctime) &&
1275  (qMax(stat_buf.st_ctime, stat_buf.st_mtime) != e->m_ctime ||
1276  stat_buf.st_ino != e->m_ino ||
1277  stat_buf.st_nlink != nlink_t(e->m_nlink)))
1278  // we trust QFSW to get it right, the ctime comparisons above
1279  // fail for example when adding files to directories on Windows
1280  // which doesn't change the mtime of the directory
1281  || e->m_mode == QFSWatchMode
1282  ) {
1283  e->m_ctime = qMax(stat_buf.st_ctime, stat_buf.st_mtime);
1284  e->m_nlink = stat_buf.st_nlink;
1285  if (e->m_ino != stat_buf.st_ino) {
1286  // The file got deleted and recreated. We need to watch it again.
1287  removeWatch(e);
1288  addWatch(e);
1289  }
1290  e->m_ino = stat_buf.st_ino;
1291  return Changed;
1292  }
1293 
1294  return NoChange;
1295  }
1296 
1297  // dir/file doesn't exist
1298 
1299  e->m_nlink = 0;
1300  e->m_ino = 0;
1301  e->m_status = NonExistent;
1302 
1303  if (e->m_ctime == invalid_ctime) {
1304  return NoChange;
1305  }
1306 
1307  e->m_ctime = invalid_ctime;
1308  return Deleted;
1309 }
1310 
1311 /* Notify all interested KDirWatch instances about a given event on an entry
1312  * and stored pending events. When watching is stopped, the event is
1313  * added to the pending events.
1314  */
1315 void KDirWatchPrivate::emitEvent(const Entry* e, int event, const QString &fileName)
1316 {
1317  QString path (e->path);
1318  if (!fileName.isEmpty()) {
1319  if (!QDir::isRelativePath(fileName))
1320  path = fileName;
1321  else {
1322 #ifdef Q_OS_UNIX
1323  path += QLatin1Char('/') + fileName;
1324 #elif defined(Q_WS_WIN)
1325  //current drive is passed instead of /
1326  path += QDir::currentPath().left(2) + QLatin1Char('/') + fileName;
1327 #endif
1328  }
1329  }
1330 
1331  if (s_verboseDebug) {
1332  kDebug(7001) << event << path << e->m_clients.count() << "clients";
1333  }
1334 
1335  foreach(Client* c, e->m_clients)
1336  {
1337  if (c->instance==0 || c->count==0) continue;
1338 
1339  if (c->watchingStopped) {
1340  // add event to pending...
1341  if (event == Changed)
1342  c->pending |= event;
1343  else if (event == Created || event == Deleted)
1344  c->pending = event;
1345  continue;
1346  }
1347  // not stopped
1348  if (event == NoChange || event == Changed)
1349  event |= c->pending;
1350  c->pending = NoChange;
1351  if (event == NoChange) continue;
1352 
1353  // Emit the signals delayed, to avoid unexpected re-entrancy from the slots (#220153)
1354 
1355  if (event & Deleted) {
1356  QMetaObject::invokeMethod(c->instance, "setDeleted", Qt::QueuedConnection, Q_ARG(QString, path));
1357  // emit only Deleted event...
1358  continue;
1359  }
1360 
1361  if (event & Created) {
1362  QMetaObject::invokeMethod(c->instance, "setCreated", Qt::QueuedConnection, Q_ARG(QString, path));
1363  // possible emit Change event after creation
1364  }
1365 
1366  if (event & Changed) {
1367  QMetaObject::invokeMethod(c->instance, "setDirty", Qt::QueuedConnection, Q_ARG(QString, path));
1368  }
1369  }
1370 }
1371 
1372 // Remove entries which were marked to be removed
1373 void KDirWatchPrivate::slotRemoveDelayed()
1374 {
1375  delayRemove = false;
1376  // Removing an entry could also take care of removing its parent
1377  // (e.g. in FAM or inotify mode), which would remove other entries in removeList,
1378  // so don't use foreach or iterators here...
1379  while (!removeList.isEmpty()) {
1380  Entry* entry = *removeList.begin();
1381  removeEntry(0, entry, 0); // this will remove entry from removeList
1382  }
1383 }
1384 
1385 /* Scan all entries to be watched for changes. This is done regularly
1386  * when polling. FAM and inotify use a single-shot timer to call this slot delayed.
1387  */
1388 void KDirWatchPrivate::slotRescan()
1389 {
1390  if (s_verboseDebug)
1391  kDebug(7001);
1392 
1393  EntryMap::Iterator it;
1394 
1395  // People can do very long things in the slot connected to dirty(),
1396  // like showing a message box. We don't want to keep polling during
1397  // that time, otherwise the value of 'delayRemove' will be reset.
1398  // ### TODO: now the emitEvent delays emission, this can be cleaned up
1399  bool timerRunning = timer.isActive();
1400  if ( timerRunning )
1401  timer.stop();
1402 
1403  // We delay deletions of entries this way.
1404  // removeDir(), when called in slotDirty(), can cause a crash otherwise
1405  // ### TODO: now the emitEvent delays emission, this can be cleaned up
1406  delayRemove = true;
1407 
1408  if (rescan_all)
1409  {
1410  // mark all as dirty
1411  it = m_mapEntries.begin();
1412  for( ; it != m_mapEntries.end(); ++it )
1413  (*it).dirty = true;
1414  rescan_all = false;
1415  }
1416  else
1417  {
1418  // progate dirty flag to dependant entries (e.g. file watches)
1419  it = m_mapEntries.begin();
1420  for( ; it != m_mapEntries.end(); ++it )
1421  if (((*it).m_mode == INotifyMode || (*it).m_mode == QFSWatchMode) && (*it).dirty )
1422  (*it).propagate_dirty();
1423  }
1424 
1425 #ifdef HAVE_SYS_INOTIFY_H
1426  QList<Entry*> cList;
1427 #endif
1428 
1429  it = m_mapEntries.begin();
1430  for( ; it != m_mapEntries.end(); ++it ) {
1431  // we don't check invalid entries (i.e. remove delayed)
1432  Entry* entry = &(*it);
1433  if (!entry->isValid()) continue;
1434 
1435  const int ev = scanEntry(entry);
1436  if (s_verboseDebug)
1437  kDebug(7001) << "scanEntry for" << entry->path << "says" << ev;
1438 
1439  switch(entry->m_mode) {
1440 #ifdef HAVE_SYS_INOTIFY_H
1441  case INotifyMode:
1442  if ( ev == Deleted ) {
1443  if (s_verboseDebug)
1444  kDebug(7001) << "scanEntry says" << entry->path << "was deleted";
1445  addEntry(0, entry->parentDirectory(), entry, true);
1446  } else if (ev == Created) {
1447  if (s_verboseDebug)
1448  kDebug(7001) << "scanEntry says" << entry->path << "was created. wd=" << entry->wd;
1449  if (entry->wd < 0) {
1450  cList.append(entry);
1451  addWatch(entry);
1452  }
1453  }
1454  break;
1455 #endif
1456  case FAMMode:
1457  case QFSWatchMode:
1458  if (ev == Created) {
1459  addWatch(entry);
1460  }
1461  break;
1462  default:
1463  // dunno about StatMode...
1464  break;
1465  }
1466 
1467 #ifdef HAVE_SYS_INOTIFY_H
1468  if (entry->isDir) {
1469  // Report and clear the the list of files that have changed in this directory.
1470  // Remove duplicates by changing to set and back again:
1471  // we don't really care about preserving the order of the
1472  // original changes.
1473  QStringList pendingFileChanges = entry->m_pendingFileChanges;
1474  pendingFileChanges.removeDuplicates();
1475  Q_FOREACH(const QString &changedFilename, pendingFileChanges) {
1476  if (s_verboseDebug) {
1477  kDebug(7001) << "processing pending file change for" << changedFilename;
1478  }
1479  emitEvent(entry, Changed, changedFilename);
1480  }
1481  entry->m_pendingFileChanges.clear();
1482  }
1483 #endif
1484 
1485  if ( ev != NoChange ) {
1486  emitEvent(entry, ev);
1487  }
1488  }
1489 
1490  if ( timerRunning )
1491  timer.start(freq);
1492 
1493 #ifdef HAVE_SYS_INOTIFY_H
1494  // Remove watch of parent of new created directories
1495  Q_FOREACH(Entry* e, cList)
1496  removeEntry(0, e->parentDirectory(), e);
1497 #endif
1498 
1499  QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
1500 }
1501 
1502 bool KDirWatchPrivate::isNoisyFile( const char * filename )
1503 {
1504  // $HOME/.X.err grows with debug output, so don't notify change
1505  if ( *filename == '.') {
1506  if (strncmp(filename, ".X.err", 6) == 0) return true;
1507  if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
1508  // fontconfig updates the cache on every KDE app start
1509  // (inclusive kio_thumbnail slaves)
1510  if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
1511  }
1512 
1513  return false;
1514 }
1515 
1516 #ifdef HAVE_FAM
1517 void KDirWatchPrivate::famEventReceived()
1518 {
1519  static FAMEvent fe;
1520 
1521  delayRemove = true;
1522 
1523  //kDebug(7001) << "Fam event received";
1524 
1525  while(use_fam && FAMPending(&fc)) {
1526  if (FAMNextEvent(&fc, &fe) == -1) {
1527  kWarning(7001) << "FAM connection problem, switching to polling.";
1528  use_fam = false;
1529  delete sn; sn = 0;
1530 
1531  // Replace all FAMMode entries with INotify/Stat
1532  EntryMap::Iterator it = m_mapEntries.begin();
1533  for( ; it != m_mapEntries.end(); ++it )
1534  if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
1535  Entry* e = &(*it);
1536  addWatch(e);
1537  }
1538  }
1539  else
1540  checkFAMEvent(&fe);
1541  }
1542 
1543  QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
1544 }
1545 
1546 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
1547 {
1548  //kDebug(7001);
1549 
1550  // Don't be too verbose ;-)
1551  if ((fe->code == FAMExists) ||
1552  (fe->code == FAMEndExist) ||
1553  (fe->code == FAMAcknowledge)) return;
1554 
1555  if ( isNoisyFile( fe->filename ) )
1556  return;
1557 
1558  Entry* e = 0;
1559  EntryMap::Iterator it = m_mapEntries.begin();
1560  for( ; it != m_mapEntries.end(); ++it )
1561  if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
1562  FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
1563  e = &(*it);
1564  break;
1565  }
1566 
1567  // Entry* e = static_cast<Entry*>(fe->userdata);
1568 
1569  if (s_verboseDebug) { // don't enable this except when debugging, see #88538
1570  kDebug(7001) << "Processing FAM event ("
1571  << ((fe->code == FAMChanged) ? "FAMChanged" :
1572  (fe->code == FAMDeleted) ? "FAMDeleted" :
1573  (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
1574  (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
1575  (fe->code == FAMCreated) ? "FAMCreated" :
1576  (fe->code == FAMMoved) ? "FAMMoved" :
1577  (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
1578  (fe->code == FAMExists) ? "FAMExists" :
1579  (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
1580  << ", " << fe->filename
1581  << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) << ") e=" << e;
1582  }
1583 
1584  if (!e) {
1585  // this happens e.g. for FAMAcknowledge after deleting a dir...
1586  // kDebug(7001) << "No entry for FAM event ?!";
1587  return;
1588  }
1589 
1590  if (e->m_status == NonExistent) {
1591  kDebug(7001) << "FAM event for nonExistent entry " << e->path;
1592  return;
1593  }
1594 
1595  // Delayed handling. This rechecks changes with own stat calls.
1596  e->dirty = true;
1597  if (!rescan_timer.isActive())
1598  rescan_timer.start(m_PollInterval); // singleshot
1599 
1600  // needed FAM control actions on FAM events
1601  switch (fe->code) {
1602  case FAMDeleted:
1603  // fe->filename is an absolute path when a watched file-or-dir is deleted
1604  if (!QDir::isRelativePath(QFile::decodeName(fe->filename))) {
1605  FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
1606  kDebug(7001) << "Cancelled FAMReq"
1607  << FAMREQUEST_GETREQNUM(&(e->fr))
1608  << "for" << e->path;
1609  e->m_status = NonExistent;
1610  e->m_ctime = invalid_ctime;
1611  emitEvent(e, Deleted, e->path);
1612  // If the parent dir was already watched, tell it something changed
1613  Entry* parentEntry = entry(e->parentDirectory());
1614  if (parentEntry)
1615  parentEntry->dirty = true;
1616  // Add entry to parent dir to notice if the entry gets recreated
1617  addEntry(0, e->parentDirectory(), e, true /*isDir*/);
1618  } else {
1619  // A file in this directory has been removed, and wasn't explicitly watched.
1620  // We could still inform clients, like inotify does? But stat can't.
1621  // For now we just marked e dirty and slotRescan will emit the dir as dirty.
1622  //kDebug(7001) << "Got FAMDeleted for" << QFile::decodeName(fe->filename) << "in" << e->path << ". Absolute path -> NOOP!";
1623  }
1624  break;
1625 
1626  case FAMCreated: {
1627  // check for creation of a directory we have to watch
1628  QString tpath(e->path + QLatin1Char('/') + QFile::decodeName(fe->filename));
1629 
1630  // This code is very similar to the one in inotifyEventReceived...
1631  Entry* sub_entry = e->findSubEntry(tpath);
1632  if (sub_entry /*&& sub_entry->isDir*/) {
1633  // We were waiting for this new file/dir to be created
1634  emitEvent(sub_entry, Created);
1635  sub_entry->dirty = true;
1636  rescan_timer.start(0); // process this asap, to start watching that dir
1637  } else if (e->isDir && !e->m_clients.empty()) {
1638  bool isDir = false;
1639  const QList<Client *> clients = e->clientsForFileOrDir(tpath, &isDir);
1640  Q_FOREACH(Client *client, clients) {
1641  addEntry (client->instance, tpath, 0, isDir,
1642  isDir ? client->m_watchModes : KDirWatch::WatchDirOnly);
1643  }
1644 
1645  if (!clients.isEmpty()) {
1646  emitEvent(e, Created, tpath);
1647 
1648  kDebug(7001).nospace() << clients.count() << " instance(s) monitoring the new "
1649  << (isDir ? "dir " : "file ") << tpath;
1650  }
1651  }
1652  }
1653  break;
1654  default:
1655  break;
1656  }
1657 }
1658 #else
1659 void KDirWatchPrivate::famEventReceived()
1660 {
1661  kWarning (7001) << "Fam event received but FAM is not supported";
1662 }
1663 #endif
1664 
1665 
1666 void KDirWatchPrivate::statistics()
1667 {
1668  EntryMap::Iterator it;
1669 
1670  kDebug(7001) << "Entries watched:";
1671  if (m_mapEntries.count()==0) {
1672  kDebug(7001) << " None.";
1673  }
1674  else {
1675  it = m_mapEntries.begin();
1676  for( ; it != m_mapEntries.end(); ++it ) {
1677  Entry* e = &(*it);
1678  kDebug(7001) << " " << *e;
1679 
1680  foreach(Client* c, e->m_clients) {
1681  QByteArray pending;
1682  if (c->watchingStopped) {
1683  if (c->pending & Deleted) pending += "deleted ";
1684  if (c->pending & Created) pending += "created ";
1685  if (c->pending & Changed) pending += "changed ";
1686  if (!pending.isEmpty()) pending = " (pending: " + pending + ')';
1687  pending = ", stopped" + pending;
1688  }
1689  kDebug(7001) << " by " << c->instance->objectName()
1690  << " (" << c->count << " times)" << pending;
1691  }
1692  if (e->m_entries.count()>0) {
1693  kDebug(7001) << " dependent entries:";
1694  foreach(Entry *d, e->m_entries) {
1695  kDebug(7001) << " " << d << d->path << (d->m_status == NonExistent ? "NonExistent" : "EXISTS!!! ERROR!");
1696  if (s_verboseDebug) {
1697  Q_ASSERT(d->m_status == NonExistent); // it doesn't belong here otherwise
1698  }
1699  }
1700  }
1701  }
1702  }
1703 }
1704 
1705 #ifdef HAVE_QFILESYSTEMWATCHER
1706 // Slot for QFileSystemWatcher
1707 void KDirWatchPrivate::fswEventReceived(const QString &path)
1708 {
1709  if (s_verboseDebug)
1710  kDebug(7001) << path;
1711  EntryMap::Iterator it = m_mapEntries.find(path);
1712  if(it != m_mapEntries.end()) {
1713  Entry* e = &(*it);
1714  e->dirty = true;
1715  const int ev = scanEntry(e);
1716  if (s_verboseDebug)
1717  kDebug(7001) << "scanEntry for" << e->path << "says" << ev;
1718  if (ev != NoChange)
1719  emitEvent(e, ev);
1720  if(ev == Deleted) {
1721  if (e->isDir)
1722  addEntry(0, e->parentDirectory(), e, true);
1723  else
1724  addEntry(0, QFileInfo(e->path).absolutePath(), e, true);
1725  } else if (ev == Created) {
1726  // We were waiting for it to appear; now watch it
1727  addWatch(e);
1728  } else if (e->isDir) {
1729  // Check if any file or dir was created under this directory, that we were waiting for
1730  Q_FOREACH(Entry* sub_entry, e->m_entries) {
1731  fswEventReceived(sub_entry->path); // recurse, to call scanEntry and see if something changed
1732  }
1733  }
1734  }
1735 }
1736 #else
1737 void KDirWatchPrivate::fswEventReceived(const QString &path)
1738 {
1739  Q_UNUSED(path);
1740  kWarning (7001) << "QFileSystemWatcher event received but QFileSystemWatcher is not supported";
1741 }
1742 #endif // HAVE_QFILESYSTEMWATCHER
1743 
1744 //
1745 // Class KDirWatch
1746 //
1747 
1748 K_GLOBAL_STATIC(KDirWatch, s_pKDirWatchSelf)
1749 KDirWatch* KDirWatch::self()
1750 {
1751  return s_pKDirWatchSelf;
1752 }
1753 
1754 // TODO KDE5: is this used anywhere?
1755 bool KDirWatch::exists()
1756 {
1757  return s_pKDirWatchSelf.exists();
1758 }
1759 
1760 static void cleanupQFSWatcher()
1761 {
1762  s_pKDirWatchSelf->deleteQFSWatcher();
1763 }
1764 
1765 KDirWatch::KDirWatch (QObject* parent)
1766  : QObject(parent), d(createPrivate())
1767 {
1768  static int nameCounter = 0;
1769 
1770  nameCounter++;
1771  setObjectName(QString::fromLatin1("KDirWatch-%1").arg(nameCounter) );
1772 
1773  d->ref();
1774 
1775  d->_isStopped = false;
1776 
1777  static bool cleanupRegistered = false;
1778  if (!cleanupRegistered) {
1779  cleanupRegistered = true;
1780  // Must delete QFileSystemWatcher before qApp is gone - bug 261541
1781  qAddPostRoutine(cleanupQFSWatcher);
1782  }
1783 }
1784 
1785 KDirWatch::~KDirWatch()
1786 {
1787  d->removeEntries(this);
1788  if ( d->deref() )
1789  {
1790  // delete it if it's the last one
1791  delete d;
1792  dwp_self = 0;
1793  }
1794 }
1795 
1796 void KDirWatch::addDir( const QString& _path, WatchModes watchModes)
1797 {
1798  if (d) d->addEntry(this, _path, 0, true, watchModes);
1799 }
1800 
1801 void KDirWatch::addFile( const QString& _path )
1802 {
1803  if ( !d )
1804  return;
1805 
1806  d->addEntry(this, _path, 0, false);
1807 }
1808 
1809 QDateTime KDirWatch::ctime( const QString &_path ) const
1810 {
1811  KDirWatchPrivate::Entry* e = d->entry(_path);
1812 
1813  if (!e)
1814  return QDateTime();
1815 
1816  return QDateTime::fromTime_t(e->m_ctime);
1817 }
1818 
1819 void KDirWatch::removeDir( const QString& _path )
1820 {
1821  if (d) {
1822  if (!d->removeEntry(this, _path, 0)) {
1823  kWarning() << "doesn't know" << _path;
1824  }
1825  }
1826 }
1827 
1828 void KDirWatch::removeFile( const QString& _path )
1829 {
1830  if (d) {
1831  if (!d->removeEntry(this, _path, 0)) {
1832  kWarning() << "doesn't know" << _path;
1833  }
1834  }
1835 }
1836 
1837 bool KDirWatch::stopDirScan( const QString& _path )
1838 {
1839  if (d) {
1840  KDirWatchPrivate::Entry *e = d->entry(_path);
1841  if (e && e->isDir) return d->stopEntryScan(this, e);
1842  }
1843  return false;
1844 }
1845 
1846 bool KDirWatch::restartDirScan( const QString& _path )
1847 {
1848  if (d) {
1849  KDirWatchPrivate::Entry *e = d->entry(_path);
1850  if (e && e->isDir)
1851  // restart without notifying pending events
1852  return d->restartEntryScan(this, e, false);
1853  }
1854  return false;
1855 }
1856 
1857 void KDirWatch::stopScan()
1858 {
1859  if (d) {
1860  d->stopScan(this);
1861  d->_isStopped = true;
1862  }
1863 }
1864 
1865 bool KDirWatch::isStopped()
1866 {
1867  return d->_isStopped;
1868 }
1869 
1870 void KDirWatch::startScan( bool notify, bool skippedToo )
1871 {
1872  if (d) {
1873  d->_isStopped = false;
1874  d->startScan(this, notify, skippedToo);
1875  }
1876 }
1877 
1878 
1879 bool KDirWatch::contains( const QString& _path ) const
1880 {
1881  KDirWatchPrivate::Entry* e = d->entry(_path);
1882  if (!e)
1883  return false;
1884 
1885  foreach(KDirWatchPrivate::Client* client, e->m_clients) {
1886  if (client->instance == this)
1887  return true;
1888  }
1889 
1890  return false;
1891 }
1892 
1893 void KDirWatch::deleteQFSWatcher()
1894 {
1895  delete d->fsWatcher;
1896  d->fsWatcher = 0;
1897 }
1898 
1899 void KDirWatch::statistics()
1900 {
1901  if (!dwp_self) {
1902  kDebug(7001) << "KDirWatch not used";
1903  return;
1904  }
1905  dwp_self->statistics();
1906 }
1907 
1908 
1909 void KDirWatch::setCreated( const QString & _file )
1910 {
1911  kDebug(7001) << objectName() << "emitting created" << _file;
1912  emit created( _file );
1913 }
1914 
1915 void KDirWatch::setDirty( const QString & _file )
1916 {
1917  //kDebug(7001) << objectName() << "emitting dirty" << _file;
1918  emit dirty( _file );
1919 }
1920 
1921 void KDirWatch::setDeleted( const QString & _file )
1922 {
1923  kDebug(7001) << objectName() << "emitting deleted" << _file;
1924  emit deleted( _file );
1925 }
1926 
1927 KDirWatch::Method KDirWatch::internalMethod()
1928 {
1929  return d->m_preferredMethod;
1930 }
1931 
1932 
1933 #include "kdirwatch.moc"
1934 #include "kdirwatch_p.moc"
1935 
1936 //sven
KDirWatchPrivate::ref
void ref()
Definition: kdirwatch_p.h:217
KDirWatchPrivate::Entry::parentDirectory
QString parentDirectory() const
Definition: kdirwatch.cpp:520
methodToString
static const char * methodToString(KDirWatch::Method method)
Definition: kdirwatch.cpp:106
KDirWatchPrivate::Entry::inotifyClientsForFileOrDir
QList< Client * > inotifyClientsForFileOrDir(bool isDir) const
Definition: kdirwatch.cpp:549
createPrivate
static KDirWatchPrivate * createPrivate()
Definition: kdirwatch.cpp:81
KDirWatchPrivate::Entry::clientsForFileOrDir
QList< Client * > clientsForFileOrDir(const QString &tpath, bool *isDir) const
Definition: kdirwatch.cpp:525
QString::truncate
void truncate(int position)
KDirWatch::setCreated
void setCreated(const QString &path)
Emits created().
Definition: kdirwatch.cpp:1909
KDirWatchPrivate::famEventReceived
void famEventReceived()
Definition: kdirwatch.cpp:1659
kdebug.h
KDirWatch::addFile
void addFile(const QString &file)
Adds a file to be watched.
Definition: kdirwatch.cpp:1801
KDirWatchPrivate::Entry::m_clients
QList< Client * > m_clients
Definition: kdirwatch_p.h:149
KDirWatchPrivate::useFreq
void useFreq(Entry *e, int newFreq)
Definition: kdirwatch.cpp:606
QSocketNotifier
KDirWatchPrivate::Entry
Definition: kdirwatch_p.h:136
KDirWatchPrivate::removeEntry
bool removeEntry(KDirWatch *, const QString &, Entry *sub_entry)
Definition: kdirwatch.cpp:995
KDirWatchPrivate::isNoisyFile
static bool isNoisyFile(const char *filename)
Definition: kdirwatch.cpp:1502
KDirWatchPrivate::Entry::isValid
bool isValid()
Definition: kdirwatch_p.h:160
KDirWatch::KDirWatch
KDirWatch(QObject *parent=0)
Constructor.
Definition: kdirwatch.cpp:1765
KDirWatch::stopScan
void stopScan()
Stops scanning of all directories in internal list.
Definition: kdirwatch.cpp:1857
QByteArray
KDirWatchPrivate::rescan_timer
QTimer rescan_timer
Definition: kdirwatch_p.h:245
KDirWatch::WatchDirOnly
Watch just the specified directory.
Definition: kdirwatch.h:74
KDirWatchPrivate::stopScan
void stopScan(KDirWatch *)
Definition: kdirwatch.cpp:1183
KDirWatchPrivate::stopEntryScan
bool stopEntryScan(KDirWatch *, Entry *)
Definition: kdirwatch.cpp:1096
kdirwatch.h
KDirWatchPrivate::INotifyMode
Definition: kdirwatch_p.h:122
timeout
int timeout
Definition: kkernel_mac.cpp:46
mask
#define mask
QStringList::removeDuplicates
int removeDuplicates()
KDirWatch::INotify
Definition: kdirwatch.h:223
K_GLOBAL_STATIC
#define K_GLOBAL_STATIC(TYPE, NAME)
This macro makes it easy to use non-POD types as global statics.
Definition: kglobal.h:221
KDirWatchPrivate::FAMMode
Definition: kdirwatch_p.h:122
KDirWatch::startScan
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
Definition: kdirwatch.cpp:1870
kconfig.h
KDirWatchPrivate::m_mapEntries
EntryMap m_mapEntries
Definition: kdirwatch_p.h:231
KDirWatchPrivate::Client
Definition: kdirwatch_p.h:126
KDirWatch::Method
Method
Definition: kdirwatch.h:223
QFileInfo::isSymLink
bool isSymLink() const
QByteArray::isEmpty
bool isEmpty() const
KDirWatch::internalMethod
Method internalMethod()
Returns the preferred internal method to watch for changes.
Definition: kdirwatch.cpp:1927
KDE::stat
int stat(const QString &path, KDE_struct_stat *buf)
Definition: kde_file_win.cpp:175
QDebug::nospace
QDebug & nospace()
KDirWatchPrivate::scanEntry
int scanEntry(Entry *e)
Definition: kdirwatch.cpp:1220
KDirWatchPrivate::Entry::m_status
entryStatus m_status
Definition: kdirwatch_p.h:145
invalid_ctime
#define invalid_ctime
Definition: kdirwatch_p.h:71
KDirWatchPrivate::Created
Definition: kdirwatch_p.h:123
KDirWatchPrivate::restartEntryScan
bool restartEntryScan(KDirWatch *, Entry *, bool)
Definition: kdirwatch.cpp:1122
KDirWatchPrivate::slotRescan
void slotRescan()
Definition: kdirwatch.cpp:1388
QDir::currentPath
QString currentPath()
KDirWatchPrivate::NoChange
Definition: kdirwatch_p.h:123
KDirWatchPrivate::NonExistent
Definition: kdirwatch_p.h:121
KDirWatchPrivate::Entry::m_entries
QList< Entry * > m_entries
Definition: kdirwatch_p.h:151
KDirWatchPrivate::Entry::m_ino
ino_t m_ino
Definition: kdirwatch_p.h:144
KDirWatchPrivate::useStat
bool useStat(Entry *)
Definition: kdirwatch.cpp:734
KDirWatchPrivate::QFSWatchMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::removeEntries
void removeEntries(KDirWatch *)
Definition: kdirwatch.cpp:1061
KDirWatchPrivate::Client::pending
int pending
Definition: kdirwatch_p.h:132
KDirWatchPrivate::Client::m_watchModes
KDirWatch::WatchModes m_watchModes
Definition: kdirwatch_p.h:133
KDirWatchPrivate::Client::instance
KDirWatch * instance
Definition: kdirwatch_p.h:127
KFileSystemWatcher::removePath
void removePath(const QString &file)
Definition: kdirwatch_win.cpp:68
KFileSystemType::fileSystemType
Type fileSystemType(const QString &path)
KDirWatchPrivate::Entry::path
QString path
Definition: kdirwatch_p.h:152
KGlobal::config
KSharedConfigPtr config()
Returns the general config object.
Definition: kglobal.cpp:139
KDirWatch::stopDirScan
bool stopDirScan(const QString &path)
Stops scanning the specified path.
Definition: kdirwatch.cpp:1837
KDirWatch::deleted
void deleted(const QString &path)
Emitted when a file or directory is deleted.
QObject::event
virtual bool event(QEvent *e)
KDirWatchPrivate::~KDirWatchPrivate
~KDirWatchPrivate()
Definition: kdirwatch.cpp:250
KDirWatchPrivate::Client::count
int count
Definition: kdirwatch_p.h:128
KDirWatchPrivate::UnknownMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::resetList
void resetList(KDirWatch *, bool)
Definition: kdirwatch.cpp:1206
KDirWatchPrivate::statistics
void statistics()
Definition: kdirwatch.cpp:1666
KDirWatchPrivate::StatMode
Definition: kdirwatch_p.h:122
QFlags
QDateTime::fromTime_t
QDateTime fromTime_t(uint seconds)
kglobal.h
KDirWatchPrivate::useQFSWatch
bool useQFSWatch(Entry *e)
Definition: kdirwatch.cpp:713
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
KDirWatchPrivate::emitEvent
void emitEvent(const Entry *e, int event, const QString &fileName=QString())
Definition: kdirwatch.cpp:1315
KDirWatchPrivate::m_preferredMethod
KDirWatch::Method m_preferredMethod
Definition: kdirwatch_p.h:233
KDirWatchPrivate::m_nfsPreferredMethod
KDirWatch::Method m_nfsPreferredMethod
Definition: kdirwatch_p.h:233
KDirWatchPrivate::Entry::propagate_dirty
void propagate_dirty()
Definition: kdirwatch.cpp:453
KDirWatch::statistics
static void statistics()
Dump statistic information about the KDirWatch::self() instance.
Definition: kdirwatch.cpp:1899
QFileInfo::isDir
bool isDir() const
KDirWatchPrivate::fsWatcher
KFileSystemWatcher * fsWatcher
Definition: kdirwatch_p.h:264
QObject
KDirWatch::DNotify
Definition: kdirwatch.h:223
kdirwatch_p.h
KDirWatchPrivate::KDirWatchPrivate
KDirWatchPrivate()
Definition: kdirwatch.cpp:153
QList::isEmpty
bool isEmpty() const
QDir::entryInfoList
QFileInfoList entryInfoList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
KDirWatchPrivate::fswEventReceived
void fswEventReceived(const QString &path)
Definition: kdirwatch.cpp:1707
QObject::setObjectName
void setObjectName(const QString &name)
KDirWatch::setDeleted
void setDeleted(const QString &path)
Emits deleted().
Definition: kdirwatch.cpp:1921
QFileInfo::absoluteFilePath
QString absoluteFilePath() const
QString::isEmpty
bool isEmpty() const
KDirWatch::QFSWatch
Definition: kdirwatch.h:223
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
KDirWatchPrivate::timer
QTimer timer
Definition: kdirwatch_p.h:230
KDirWatchPrivate::removeWatch
void removeWatch(Entry *entry)
Definition: kdirwatch.cpp:968
KDirWatchPrivate::m_PollInterval
int m_PollInterval
Definition: kdirwatch_p.h:236
dwp_self
static KDirWatchPrivate * dwp_self
Definition: kdirwatch.cpp:80
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
KDirWatchPrivate::Entry::m_mode
entryMode m_mode
Definition: kdirwatch_p.h:146
ksharedconfig.h
QString
KDirWatchPrivate::DNotifyMode
Definition: kdirwatch_p.h:122
QList< QByteArray >
QMap::end
iterator end()
KDirWatchPrivate::Normal
Definition: kdirwatch_p.h:121
KDirWatch::ctime
QDateTime ctime(const QString &path) const
Returns the time the directory/file was last changed.
Definition: kdirwatch.cpp:1809
KDirWatch::created
void created(const QString &path)
Emitted when a file or directory is created.
QList::iterator
KDirWatchPrivate::Entry::m_ctime
time_t m_ctime
Definition: kdirwatch_p.h:140
QMap::begin
iterator begin()
QStringList
QFileInfo
QDebug::space
QDebug & space()
QLatin1Char
kWarning
#define kWarning
Definition: kdebug.h:322
QDir::isRelativePath
bool isRelativePath(const QString &path)
QTimer::stop
void stop()
KDirWatchPrivate::startScan
void startScan(KDirWatch *, bool, bool)
Definition: kdirwatch.cpp:1191
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
KDE::lstat
int lstat(const QString &path, KDE_struct_stat *buf)
Definition: kde_file_win.cpp:148
QDebug
KDirWatch::WatchFiles
Watch also all files contained by the directory.
Definition: kdirwatch.h:75
KDirWatch::contains
bool contains(const QString &path) const
Check if a directory is being watched by this KDirWatch instance.
Definition: kdirwatch.cpp:1879
KDirWatchPrivate::Entry::msecLeft
int msecLeft
Definition: kdirwatch_p.h:154
QDir
KConfigGroup
A class for one specific group in a KConfig object.
Definition: kconfiggroup.h:53
KDirWatchPrivate::inotifyEventReceived
void inotifyEventReceived()
Definition: kdirwatch.cpp:271
QDir::cleanPath
QString cleanPath(const QString &path)
cleanupQFSWatcher
static void cleanupQFSWatcher()
Definition: kdirwatch.cpp:1760
KDirWatchPrivate::rescan_all
bool rescan_all
Definition: kdirwatch_p.h:244
kfilesystemtype_p.h
KFileSystemWatcher
Definition: kdirwatch_p.h:88
KFileSystemType::Nfs
Definition: kfilesystemtype_p.h:30
KDirWatchPrivate::Entry::removeClient
void removeClient(KDirWatch *)
Definition: kdirwatch.cpp:493
QLatin1String
KDirWatch::deleteQFSWatcher
void deleteQFSWatcher()
Definition: kdirwatch.cpp:1893
operator<<
QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
Definition: kdirwatch.cpp:561
KDirWatchPrivate::Entry::m_nlink
int m_nlink
Definition: kdirwatch_p.h:142
KDirWatchPrivate
Definition: kdirwatch_p.h:116
KDirWatchPrivate::removeList
QSet< Entry * > removeList
Definition: kdirwatch_p.h:241
QString::length
int length() const
KFileSystemWatcher::addPath
void addPath(const QString &file)
Definition: kdirwatch_win.cpp:60
QString::left
QString left(int n) const
KDirWatch::removeDir
void removeDir(const QString &path)
Removes a directory from the list of scanned directories.
Definition: kdirwatch.cpp:1819
QString::fromLatin1
QString fromLatin1(const char *str, int size)
KDirWatchPrivate::Entry::dirty
bool dirty
Definition: kdirwatch_p.h:170
KDirWatch::addDir
void addDir(const QString &path, WatchModes watchModes=WatchDirOnly)
Adds a directory to be watched.
Definition: kdirwatch.cpp:1796
QTimer::start
void start(int msec)
KDirWatch::Stat
Definition: kdirwatch.h:223
KDirWatchPrivate::m_nfsPollInterval
int m_nfsPollInterval
Definition: kdirwatch_p.h:236
kDebug
#define kDebug
Definition: kdebug.h:316
KDirWatchPrivate::Client::watchingStopped
bool watchingStopped
Definition: kdirwatch_p.h:130
QMap::insert
iterator insert(const Key &key, const T &value)
KDirWatchPrivate::entry
Entry * entry(const QString &)
Definition: kdirwatch.cpp:586
KDirWatchPrivate::Entry::freq
int freq
Definition: kdirwatch_p.h:154
KDirWatch::FAM
Definition: kdirwatch.h:223
KDirWatch::exists
static bool exists()
Returns true if there is an instance of KDirWatch.
Definition: kdirwatch.cpp:1755
KDirWatch
Class for watching directory and file changes.
Definition: kdirwatch.h:64
KDirWatch::~KDirWatch
~KDirWatch()
Destructor.
Definition: kdirwatch.cpp:1785
KDirWatchPrivate::Entry::clientCount
int clientCount() const
Definition: kdirwatch.cpp:511
KDirWatchPrivate::deref
bool deref()
Definition: kdirwatch_p.h:218
QMap< QString, Entry >::Iterator
typedef Iterator
KDirWatchPrivate::slotRemoveDelayed
void slotRemoveDelayed()
Definition: kdirwatch.cpp:1373
KDirWatch::dirty
void dirty(const QString &path)
Emitted when a watched object is changed.
KDirWatchPrivate::statEntries
int statEntries
Definition: kdirwatch_p.h:235
KDirWatch::WatchSubDirs
Watch also all the subdirs contained by the directory.
Definition: kdirwatch.h:76
KDirWatch::setDirty
void setDirty(const QString &path)
Emits dirty().
Definition: kdirwatch.cpp:1915
QFileInfo::absolutePath
QString absolutePath() const
KDirWatch::isStopped
bool isStopped()
Is scanning stopped? After creation of a KDirWatch instance, this is false.
Definition: kdirwatch.cpp:1865
QTimer::isActive
bool isActive() const
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KDirWatch::removeFile
void removeFile(const QString &file)
Removes a file from the list of watched files.
Definition: kdirwatch.cpp:1828
QObject::parent
QObject * parent() const
KDirWatchPrivate::Entry::isDir
bool isDir
Definition: kdirwatch_p.h:147
KDirWatchPrivate::delayRemove
bool delayRemove
Definition: kdirwatch_p.h:242
KDirWatchPrivate::Deleted
Definition: kdirwatch_p.h:123
KDirWatchPrivate::Entry::findSubEntry
Entry * findSubEntry(const QString &path) const
Definition: kdirwatch_p.h:162
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
methodFromString
static KDirWatch::Method methodFromString(const QString &method)
Definition: kdirwatch.cpp:88
KDirWatchPrivate::freq
int freq
Definition: kdirwatch_p.h:234
KConfigGroup::readEntry
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
Definition: kconfiggroup.h:248
KDirWatchPrivate::Entry::addClient
void addClient(KDirWatch *, KDirWatch::WatchModes)
Definition: kdirwatch.cpp:469
KDirWatchPrivate::addEntry
void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes=KDirWatch::WatchDirOnly)
Definition: kdirwatch.cpp:763
KDirWatch::restartDirScan
bool restartDirScan(const QString &path)
Restarts scanning for specified path.
Definition: kdirwatch.cpp:1846
QMap::count
int count(const Key &key) const
QMap::find
iterator find(const Key &key)
QFile::encodeName
QByteArray encodeName(const QString &fileName)
QFile::decodeName
QString decodeName(const QByteArray &localFileName)
kconfiggroup.h
KDirWatchPrivate::Changed
Definition: kdirwatch_p.h:123
KDirWatchPrivate::addWatch
void addWatch(Entry *entry)
Definition: kdirwatch.cpp:920
QDateTime
KDirWatchPrivate::_isStopped
bool _isStopped
Definition: kdirwatch_p.h:268
QMap::remove
int remove(const Key &key)
QTimer::setSingleShot
void setSingleShot(bool singleShot)
s_verboseDebug
static const bool s_verboseDebug
Definition: kdirwatch.cpp:77
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:22:11 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDECore

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal