1/*
2 This file is part of the KDE libraries
3
4 SPDX-FileCopyrightText: 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
5 SPDX-FileCopyrightText: 2006 Dirk Mueller <mueller@kde.org>
6 SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com>
7 SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
8 SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
9
10 SPDX-License-Identifier: LGPL-2.0-only
11
12 Private Header for class of KDirWatchPrivate
13 this separate header file is needed for MOC processing
14 because KDirWatchPrivate has signals and slots
15*/
16
17#ifndef KDIRWATCH_P_H
18#define KDIRWATCH_P_H
19
20#include "kdirwatch.h"
21#include <io/config-kdirwatch.h>
22
23#ifndef QT_NO_FILESYSTEMWATCHER
24#define HAVE_QFILESYSTEMWATCHER 1
25#else
26#define HAVE_QFILESYSTEMWATCHER 0
27#endif
28
29#include <QList>
30#include <QMap>
31#include <QObject>
32#include <QSet>
33#include <QString>
34#include <QTimer>
35class QSocketNotifier;
36
37#include <ctime>
38#include <sys/types.h> // time_t, ino_t
39
40#define invalid_ctime (static_cast<time_t>(-1))
41
42#if HAVE_QFILESYSTEMWATCHER
43#include <QFileSystemWatcher>
44#endif // HAVE_QFILESYSTEMWATCHER
45
46#if HAVE_SYS_INOTIFY_H
47struct inotify_event;
48#endif
49
50/* KDirWatchPrivate is a singleton and does the watching
51 * for every KDirWatch instance in the application.
52 */
53class KDirWatchPrivate : public QObject
54{
55 Q_OBJECT
56public:
57 enum entryStatus {
58 Normal = 0,
59 NonExistent,
60 };
61 enum entryMode {
62 UnknownMode = 0,
63 StatMode,
64 INotifyMode,
65 QFSWatchMode,
66 };
67 enum {
68 NoChange = 0,
69 Changed = 1,
70 Created = 2,
71 Deleted = 4,
72 };
73
74 struct Client {
75 Client(KDirWatch *inst, KDirWatch::WatchModes watchModes)
76 : instance(inst)
77 , count(1)
78 , watchingStopped(inst->isStopped())
79 , pending(NoChange)
80 , m_watchModes(watchModes)
81 {
82 }
83
84 // The compiler needs a copy ctor for Client when Entry is inserted into m_mapEntries
85 // (even though the vector of clients is empty at that point, so no performance penalty there)
86 // Client(const Client &) = delete;
87 // Client &operator=(const Client &) = delete;
88 // Client(Client &&) = default;
89 // Client &operator=(Client &&) = default;
90
91 KDirWatch *instance;
92 int count;
93 // did the instance stop watching
94 bool watchingStopped;
95 // events blocked when stopped
96 int pending;
97 KDirWatch::WatchModes m_watchModes;
98 };
99
100 class Entry
101 {
102 public:
103 ~Entry();
104 // instances interested in events
105 std::vector<Client> m_clients;
106 // nonexistent entries of this directory
107 QList<Entry *> m_entries;
108 QString path;
109
110 // the last observed modification time
111 time_t m_ctime;
112 // last observed inode
113 ino_t m_ino;
114 // the last observed link count
115 int m_nlink;
116 entryStatus m_status;
117 entryMode m_mode;
118 int msecLeft, freq;
119 bool isDir;
120
121 QString parentDirectory() const;
122 void addClient(KDirWatch *, KDirWatch::WatchModes);
123 void removeClient(KDirWatch *);
124 int clientCount() const;
125 bool isValid()
126 {
127 return !m_clients.empty() || !m_entries.empty();
128 }
129
130 Entry *findSubEntry(const QString &path) const
131 {
132 for (Entry *sub_entry : std::as_const(t: m_entries)) {
133 if (sub_entry->path == path) {
134 return sub_entry;
135 }
136 }
137 return nullptr;
138 }
139
140 bool dirty;
141 void propagate_dirty();
142
143 QList<const Client *> clientsForFileOrDir(const QString &tpath, bool *isDir) const;
144 QList<const Client *> inotifyClientsForFileOrDir(bool isDir) const;
145
146 std::vector<Client>::iterator findInstance(KDirWatch *other)
147 {
148 return std::find_if(first: m_clients.begin(), last: m_clients.end(), pred: [other](const Client &client) {
149 return client.instance == other;
150 });
151 }
152
153#if HAVE_SYS_INOTIFY_H
154 int wd;
155 // Creation and Deletion of files happens infrequently, so
156 // can safely be reported as they occur. File changes i.e. those that emit "dirty()" can
157 // happen many times per second, though, so maintain a list of files in this directory
158 // that can be emitted and flushed at the next slotRescan(...).
159 // This will be unused if the Entry is not a directory.
160 QList<QString> m_pendingFileChanges;
161#endif
162 };
163
164 typedef QMap<QString, Entry> EntryMap;
165
166 KDirWatchPrivate();
167 ~KDirWatchPrivate() override;
168
169 void resetList(KDirWatch *instance, bool skippedToo);
170 void useFreq(Entry *e, int newFreq);
171 void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes = KDirWatch::WatchDirOnly);
172 void removeEntry(KDirWatch *instance, const QString &path, Entry *sub_entry);
173 void removeEntry(KDirWatch *instance, Entry *e, Entry *sub_entry);
174 bool stopEntryScan(KDirWatch *instance, Entry *e);
175 bool restartEntryScan(KDirWatch *instance, Entry *e, bool notify);
176 void stopScan(KDirWatch *instance);
177 void startScan(KDirWatch *instance, bool notify, bool skippedToo);
178
179 void removeEntries(KDirWatch *instance);
180
181 void addWatch(Entry *entry);
182 void removeWatch(Entry *entry);
183 Entry *entry(const QString &_path);
184 int scanEntry(Entry *e);
185 void emitEvent(Entry *e, int event, const QString &fileName = QString());
186
187 static bool isNoisyFile(const char *filename);
188
189 void ref(KDirWatch *watch);
190 void unref(KDirWatch *watch);
191
192#if HAVE_SYS_INOTIFY_H
193 QString inotifyEventName(const inotify_event *event) const;
194#endif
195
196public Q_SLOTS:
197 void slotRescan();
198 void inotifyEventReceived(); // for inotify
199 void slotRemoveDelayed();
200 void fswEventReceived(const QString &path); // for QFileSystemWatcher
201
202public:
203 QTimer m_statRescanTimer;
204 EntryMap m_mapEntries;
205
206 KDirWatch::Method m_preferredMethod, m_nfsPreferredMethod;
207 int freq;
208 int statEntries;
209 int m_nfsPollInterval, m_PollInterval;
210 bool useStat(Entry *e);
211
212 // removeList is allowed to contain any entry at most once
213 QSet<Entry *> removeList;
214 bool delayRemove;
215
216 bool rescan_all;
217 QTimer rescan_timer;
218
219#if HAVE_SYS_INOTIFY_H
220 QSocketNotifier *mSn;
221 bool supports_inotify;
222 int m_inotify_fd;
223 QHash<int, Entry *> m_inotify_wd_to_entry;
224
225 bool useINotify(Entry *e);
226#endif
227#if HAVE_QFILESYSTEMWATCHER
228 QFileSystemWatcher *fsWatcher;
229 bool useQFSWatch(Entry *e);
230#endif
231
232 bool _isStopped;
233
234private:
235 // Public objects that reference this thread-local private instance.
236 QList<KDirWatch *> m_referencesObjects;
237};
238
239QDebug operator<<(QDebug debug, const KDirWatchPrivate &dwp);
240QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry);
241
242#endif // KDIRWATCH_P_H
243

source code of kcoreaddons/src/lib/io/kdirwatch_p.h