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

KDECore

  • 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
77static const bool s_verboseDebug = false;
78
79// The KDirWatchPrivate instance is refcounted, and deleted by the last KDirWatch instance
80static KDirWatchPrivate* dwp_self = 0;
81static 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
88static 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
106static 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
153KDirWatchPrivate::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)
250KDirWatchPrivate::~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
271void 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 */
453void 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 */
469void 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
493void 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 */
511int 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
520QString KDirWatchPrivate::Entry::parentDirectory() const
521{
522 return QDir::cleanPath(path + QLatin1String("/.."));
523}
524
525QList<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
549QList<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
561QDebug 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
586KDirWatchPrivate::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
606void 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
621bool 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
680bool 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
713bool 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
734bool 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 */
763void 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
920void 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
968void 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
995bool 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
1011void 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 */
1061void 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
1096bool 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
1122bool 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
1183void 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
1191void 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
1206void 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//
1220int 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 */
1315void 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
1373void 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 */
1388void 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
1502bool 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
1517void 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
1546void 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
1659void KDirWatchPrivate::famEventReceived()
1660{
1661 kWarning (7001) << "Fam event received but FAM is not supported";
1662}
1663#endif
1664
1665
1666void 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
1707void 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
1737void 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
1748K_GLOBAL_STATIC(KDirWatch, s_pKDirWatchSelf)
1749KDirWatch* KDirWatch::self()
1750{
1751 return s_pKDirWatchSelf;
1752}
1753
1754// TODO KDE5: is this used anywhere?
1755bool KDirWatch::exists()
1756{
1757 return s_pKDirWatchSelf.exists();
1758}
1759
1760static void cleanupQFSWatcher()
1761{
1762 s_pKDirWatchSelf->deleteQFSWatcher();
1763}
1764
1765KDirWatch::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
1785KDirWatch::~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
1796void KDirWatch::addDir( const QString& _path, WatchModes watchModes)
1797{
1798 if (d) d->addEntry(this, _path, 0, true, watchModes);
1799}
1800
1801void KDirWatch::addFile( const QString& _path )
1802{
1803 if ( !d )
1804 return;
1805
1806 d->addEntry(this, _path, 0, false);
1807}
1808
1809QDateTime 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
1819void 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
1828void 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
1837bool 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
1846bool 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
1857void KDirWatch::stopScan()
1858{
1859 if (d) {
1860 d->stopScan(this);
1861 d->_isStopped = true;
1862 }
1863}
1864
1865bool KDirWatch::isStopped()
1866{
1867 return d->_isStopped;
1868}
1869
1870void KDirWatch::startScan( bool notify, bool skippedToo )
1871{
1872 if (d) {
1873 d->_isStopped = false;
1874 d->startScan(this, notify, skippedToo);
1875 }
1876}
1877
1878
1879bool 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
1893void KDirWatch::deleteQFSWatcher()
1894{
1895 delete d->fsWatcher;
1896 d->fsWatcher = 0;
1897}
1898
1899void KDirWatch::statistics()
1900{
1901 if (!dwp_self) {
1902 kDebug(7001) << "KDirWatch not used";
1903 return;
1904 }
1905 dwp_self->statistics();
1906}
1907
1908
1909void KDirWatch::setCreated( const QString & _file )
1910{
1911 kDebug(7001) << objectName() << "emitting created" << _file;
1912 emit created( _file );
1913}
1914
1915void KDirWatch::setDirty( const QString & _file )
1916{
1917 //kDebug(7001) << objectName() << "emitting dirty" << _file;
1918 emit dirty( _file );
1919}
1920
1921void KDirWatch::setDeleted( const QString & _file )
1922{
1923 kDebug(7001) << objectName() << "emitting deleted" << _file;
1924 emit deleted( _file );
1925}
1926
1927KDirWatch::Method KDirWatch::internalMethod()
1928{
1929 return d->m_preferredMethod;
1930}
1931
1932
1933#include "kdirwatch.moc"
1934#include "kdirwatch_p.moc"
1935
1936//sven
KConfigGroup
A class for one specific group in a KConfig object.
Definition: kconfiggroup.h:54
KDirWatchPrivate::Entry
Definition: kdirwatch_p.h:137
KDirWatchPrivate::Entry::freq
int freq
Definition: kdirwatch_p.h:154
KDirWatchPrivate::Entry::m_mode
entryMode m_mode
Definition: kdirwatch_p.h:146
KDirWatchPrivate::Entry::removeClient
void removeClient(KDirWatch *)
Definition: kdirwatch.cpp:493
KDirWatchPrivate::Entry::clientCount
int clientCount() const
Definition: kdirwatch.cpp:511
KDirWatchPrivate::Entry::addClient
void addClient(KDirWatch *, KDirWatch::WatchModes)
Definition: kdirwatch.cpp:469
KDirWatchPrivate::Entry::m_ino
ino_t m_ino
Definition: kdirwatch_p.h:144
KDirWatchPrivate::Entry::isValid
bool isValid()
Definition: kdirwatch_p.h:160
KDirWatchPrivate::Entry::isDir
bool isDir
Definition: kdirwatch_p.h:147
KDirWatchPrivate::Entry::msecLeft
int msecLeft
Definition: kdirwatch_p.h:154
KDirWatchPrivate::Entry::path
QString path
Definition: kdirwatch_p.h:152
KDirWatchPrivate::Entry::dirty
bool dirty
Definition: kdirwatch_p.h:170
KDirWatchPrivate::Entry::parentDirectory
QString parentDirectory() const
Definition: kdirwatch.cpp:520
KDirWatchPrivate::Entry::m_status
entryStatus m_status
Definition: kdirwatch_p.h:145
KDirWatchPrivate::Entry::m_clients
QList< Client * > m_clients
Definition: kdirwatch_p.h:149
KDirWatchPrivate::Entry::m_ctime
time_t m_ctime
Definition: kdirwatch_p.h:140
KDirWatchPrivate::Entry::m_entries
QList< Entry * > m_entries
Definition: kdirwatch_p.h:151
KDirWatchPrivate::Entry::clientsForFileOrDir
QList< Client * > clientsForFileOrDir(const QString &tpath, bool *isDir) const
Definition: kdirwatch.cpp:525
KDirWatchPrivate::Entry::inotifyClientsForFileOrDir
QList< Client * > inotifyClientsForFileOrDir(bool isDir) const
Definition: kdirwatch.cpp:549
KDirWatchPrivate::Entry::findSubEntry
Entry * findSubEntry(const QString &path) const
Definition: kdirwatch_p.h:162
KDirWatchPrivate::Entry::propagate_dirty
void propagate_dirty()
Definition: kdirwatch.cpp:453
KDirWatchPrivate::Entry::m_nlink
int m_nlink
Definition: kdirwatch_p.h:142
KDirWatchPrivate
Definition: kdirwatch_p.h:117
KDirWatchPrivate::ref
void ref()
Definition: kdirwatch_p.h:217
KDirWatchPrivate::DNotifyMode
@ DNotifyMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::FAMMode
@ FAMMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::StatMode
@ StatMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::UnknownMode
@ UnknownMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::INotifyMode
@ INotifyMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::QFSWatchMode
@ QFSWatchMode
Definition: kdirwatch_p.h:122
KDirWatchPrivate::removeEntries
void removeEntries(KDirWatch *)
Definition: kdirwatch.cpp:1061
KDirWatchPrivate::freq
int freq
Definition: kdirwatch_p.h:234
KDirWatchPrivate::statEntries
int statEntries
Definition: kdirwatch_p.h:235
KDirWatchPrivate::emitEvent
void emitEvent(const Entry *e, int event, const QString &fileName=QString())
Definition: kdirwatch.cpp:1315
KDirWatchPrivate::m_PollInterval
int m_PollInterval
Definition: kdirwatch_p.h:236
KDirWatchPrivate::useFreq
void useFreq(Entry *e, int newFreq)
Definition: kdirwatch.cpp:606
KDirWatchPrivate::restartEntryScan
bool restartEntryScan(KDirWatch *, Entry *, bool)
Definition: kdirwatch.cpp:1122
KDirWatchPrivate::timer
QTimer timer
Definition: kdirwatch_p.h:230
KDirWatchPrivate::Normal
@ Normal
Definition: kdirwatch_p.h:121
KDirWatchPrivate::NonExistent
@ NonExistent
Definition: kdirwatch_p.h:121
KDirWatchPrivate::Created
@ Created
Definition: kdirwatch_p.h:123
KDirWatchPrivate::Changed
@ Changed
Definition: kdirwatch_p.h:123
KDirWatchPrivate::NoChange
@ NoChange
Definition: kdirwatch_p.h:123
KDirWatchPrivate::Deleted
@ Deleted
Definition: kdirwatch_p.h:123
KDirWatchPrivate::m_nfsPreferredMethod
KDirWatch::Method m_nfsPreferredMethod
Definition: kdirwatch_p.h:233
KDirWatchPrivate::rescan_all
bool rescan_all
Definition: kdirwatch_p.h:244
KDirWatchPrivate::removeWatch
void removeWatch(Entry *entry)
Definition: kdirwatch.cpp:968
KDirWatchPrivate::addEntry
void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes=KDirWatch::WatchDirOnly)
Definition: kdirwatch.cpp:763
KDirWatchPrivate::addWatch
void addWatch(Entry *entry)
Definition: kdirwatch.cpp:920
KDirWatchPrivate::slotRescan
void slotRescan()
Definition: kdirwatch.cpp:1388
KDirWatchPrivate::rescan_timer
QTimer rescan_timer
Definition: kdirwatch_p.h:245
KDirWatchPrivate::slotRemoveDelayed
void slotRemoveDelayed()
Definition: kdirwatch.cpp:1373
KDirWatchPrivate::isNoisyFile
static bool isNoisyFile(const char *filename)
Definition: kdirwatch.cpp:1502
KDirWatchPrivate::stopScan
void stopScan(KDirWatch *)
Definition: kdirwatch.cpp:1183
KDirWatchPrivate::scanEntry
int scanEntry(Entry *e)
Definition: kdirwatch.cpp:1220
KDirWatchPrivate::useStat
bool useStat(Entry *)
Definition: kdirwatch.cpp:734
KDirWatchPrivate::removeEntry
bool removeEntry(KDirWatch *, const QString &, Entry *sub_entry)
Definition: kdirwatch.cpp:995
KDirWatchPrivate::statistics
void statistics()
Definition: kdirwatch.cpp:1666
KDirWatchPrivate::entry
Entry * entry(const QString &)
Definition: kdirwatch.cpp:586
KDirWatchPrivate::removeList
QSet< Entry * > removeList
Definition: kdirwatch_p.h:241
KDirWatchPrivate::inotifyEventReceived
void inotifyEventReceived()
Definition: kdirwatch.cpp:271
KDirWatchPrivate::delayRemove
bool delayRemove
Definition: kdirwatch_p.h:242
KDirWatchPrivate::m_preferredMethod
KDirWatch::Method m_preferredMethod
Definition: kdirwatch_p.h:233
KDirWatchPrivate::useQFSWatch
bool useQFSWatch(Entry *e)
Definition: kdirwatch.cpp:713
KDirWatchPrivate::fswEventReceived
void fswEventReceived(const QString &path)
Definition: kdirwatch.cpp:1707
KDirWatchPrivate::~KDirWatchPrivate
~KDirWatchPrivate()
Definition: kdirwatch.cpp:250
KDirWatchPrivate::deref
bool deref()
Definition: kdirwatch_p.h:218
KDirWatchPrivate::KDirWatchPrivate
KDirWatchPrivate()
Definition: kdirwatch.cpp:153
KDirWatchPrivate::fsWatcher
KFileSystemWatcher * fsWatcher
Definition: kdirwatch_p.h:264
KDirWatchPrivate::_isStopped
bool _isStopped
Definition: kdirwatch_p.h:268
KDirWatchPrivate::startScan
void startScan(KDirWatch *, bool, bool)
Definition: kdirwatch.cpp:1191
KDirWatchPrivate::stopEntryScan
bool stopEntryScan(KDirWatch *, Entry *)
Definition: kdirwatch.cpp:1096
KDirWatchPrivate::famEventReceived
void famEventReceived()
Definition: kdirwatch.cpp:1659
KDirWatchPrivate::m_mapEntries
EntryMap m_mapEntries
Definition: kdirwatch_p.h:231
KDirWatchPrivate::resetList
void resetList(KDirWatch *, bool)
Definition: kdirwatch.cpp:1206
KDirWatchPrivate::m_nfsPollInterval
int m_nfsPollInterval
Definition: kdirwatch_p.h:236
KDirWatch
Class for watching directory and file changes.
Definition: kdirwatch.h:65
KDirWatch::setDeleted
void setDeleted(const QString &path)
Emits deleted().
Definition: kdirwatch.cpp:1921
KDirWatch::addFile
void addFile(const QString &file)
Adds a file to be watched.
Definition: kdirwatch.cpp:1801
KDirWatch::setCreated
void setCreated(const QString &path)
Emits created().
Definition: kdirwatch.cpp:1909
KDirWatch::stopScan
void stopScan()
Stops scanning of all directories in internal list.
Definition: kdirwatch.cpp:1857
KDirWatch::removeFile
void removeFile(const QString &file)
Removes a file from the list of watched files.
Definition: kdirwatch.cpp:1828
KDirWatch::contains
bool contains(const QString &path) const
Check if a directory is being watched by this KDirWatch instance.
Definition: kdirwatch.cpp:1879
KDirWatch::Method
Method
Definition: kdirwatch.h:223
KDirWatch::Stat
@ Stat
Definition: kdirwatch.h:223
KDirWatch::FAM
@ FAM
Definition: kdirwatch.h:223
KDirWatch::DNotify
@ DNotify
Definition: kdirwatch.h:223
KDirWatch::INotify
@ INotify
Definition: kdirwatch.h:223
KDirWatch::QFSWatch
@ QFSWatch
Definition: kdirwatch.h:223
KDirWatch::stopDirScan
bool stopDirScan(const QString &path)
Stops scanning the specified path.
Definition: kdirwatch.cpp:1837
KDirWatch::statistics
static void statistics()
Dump statistic information about the KDirWatch::self() instance.
Definition: kdirwatch.cpp:1899
KDirWatch::removeDir
void removeDir(const QString &path)
Removes a directory from the list of scanned directories.
Definition: kdirwatch.cpp:1819
KDirWatch::internalMethod
Method internalMethod()
Returns the preferred internal method to watch for changes.
Definition: kdirwatch.cpp:1927
KDirWatch::deleted
void deleted(const QString &path)
Emitted when a file or directory is deleted.
KDirWatch::startScan
void startScan(bool notify=false, bool skippedToo=false)
Starts scanning of all dirs in list.
Definition: kdirwatch.cpp:1870
KDirWatch::exists
static bool exists()
Returns true if there is an instance of KDirWatch.
Definition: kdirwatch.cpp:1755
KDirWatch::addDir
void addDir(const QString &path, WatchModes watchModes=WatchDirOnly)
Adds a directory to be watched.
Definition: kdirwatch.cpp:1796
KDirWatch::WatchSubDirs
@ WatchSubDirs
Watch also all the subdirs contained by the directory.
Definition: kdirwatch.h:76
KDirWatch::WatchDirOnly
@ WatchDirOnly
Watch just the specified directory.
Definition: kdirwatch.h:74
KDirWatch::WatchFiles
@ WatchFiles
Watch also all files contained by the directory.
Definition: kdirwatch.h:75
KDirWatch::~KDirWatch
~KDirWatch()
Destructor.
Definition: kdirwatch.cpp:1785
KDirWatch::KDirWatch
KDirWatch(QObject *parent=0)
Constructor.
Definition: kdirwatch.cpp:1765
KDirWatch::isStopped
bool isStopped()
Is scanning stopped? After creation of a KDirWatch instance, this is false.
Definition: kdirwatch.cpp:1865
KDirWatch::dirty
void dirty(const QString &path)
Emitted when a watched object is changed.
KDirWatch::setDirty
void setDirty(const QString &path)
Emits dirty().
Definition: kdirwatch.cpp:1915
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.
KDirWatch::deleteQFSWatcher
void deleteQFSWatcher()
Definition: kdirwatch.cpp:1893
KDirWatch::restartDirScan
bool restartDirScan(const QString &path)
Restarts scanning for specified path.
Definition: kdirwatch.cpp:1846
KFileSystemWatcher
Definition: kdirwatch_p.h:89
KFileSystemWatcher::removePath
void removePath(const QString &file)
Definition: kdirwatch_win.cpp:68
KFileSystemWatcher::addPath
void addPath(const QString &file)
Definition: kdirwatch_win.cpp:60
QDateTime
QList
Definition: kaboutdata.h:33
QObject
QStringList
QString
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
kDebug
#define kDebug
Definition: kdebug.h:316
kWarning
#define kWarning
Definition: kdebug.h:322
mask
#define mask
kconfig.h
kconfiggroup.h
kdebug.h
methodFromString
static KDirWatch::Method methodFromString(const QString &method)
Definition: kdirwatch.cpp:88
methodToString
static const char * methodToString(KDirWatch::Method method)
Definition: kdirwatch.cpp:106
dwp_self
static KDirWatchPrivate * dwp_self
Definition: kdirwatch.cpp:80
createPrivate
static KDirWatchPrivate * createPrivate()
Definition: kdirwatch.cpp:81
cleanupQFSWatcher
static void cleanupQFSWatcher()
Definition: kdirwatch.cpp:1760
s_verboseDebug
static const bool s_verboseDebug
Definition: kdirwatch.cpp:77
kdirwatch.h
kdirwatch_p.h
invalid_ctime
#define invalid_ctime
Definition: kdirwatch_p.h:71
operator<<
QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry)
Definition: kdirwatch.cpp:561
kfilesystemtype_p.h
kglobal.h
timeout
int timeout
Definition: kkernel_mac.cpp:46
ksharedconfig.h
KDE::lstat
int lstat(const QString &path, KDE_struct_stat *buf)
Definition: kde_file_win.cpp:148
KDE::stat
int stat(const QString &path, KDE_struct_stat *buf)
Definition: kde_file_win.cpp:175
KFileSystemType::Nfs
@ Nfs
Definition: kfilesystemtype_p.h:31
KFileSystemType::fileSystemType
Type fileSystemType(const QString &path)
KGlobal::config
KSharedConfigPtr config()
Returns the general config object.
Definition: kglobal.cpp:139
KDirWatchPrivate::Client
Definition: kdirwatch_p.h:126
KDirWatchPrivate::Client::pending
int pending
Definition: kdirwatch_p.h:132
KDirWatchPrivate::Client::watchingStopped
bool watchingStopped
Definition: kdirwatch_p.h:130
KDirWatchPrivate::Client::count
int count
Definition: kdirwatch_p.h:128
KDirWatchPrivate::Client::m_watchModes
KDirWatch::WatchModes m_watchModes
Definition: kdirwatch_p.h:133
KDirWatchPrivate::Client::instance
KDirWatch * instance
Definition: kdirwatch_p.h:127
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 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-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 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
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