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 // is this already an entry for the root / or a full drive Y:?
122 bool isRoot() const;
123
124 // go up one level
125 QString parentDirectory() const;
126
127 void addClient(KDirWatch *, KDirWatch::WatchModes);
128 void removeClient(KDirWatch *);
129 int clientCount() const;
130 bool isValid()
131 {
132 return !m_clients.empty() || !m_entries.empty();
133 }
134
135 Entry *findSubEntry(const QString &path) const
136 {
137 for (Entry *sub_entry : std::as_const(t: m_entries)) {
138 if (sub_entry->path == path) {
139 return sub_entry;
140 }
141 }
142 return nullptr;
143 }
144
145 bool dirty;
146 void propagate_dirty();
147
148 QList<const Client *> clientsForFileOrDir(const QString &tpath, bool *isDir) const;
149 QList<const Client *> inotifyClientsForFileOrDir(bool isDir) const;
150
151 std::vector<Client>::iterator findInstance(KDirWatch *other)
152 {
153 return std::find_if(first: m_clients.begin(), last: m_clients.end(), pred: [other](const Client &client) {
154 return client.instance == other;
155 });
156 }
157
158#if HAVE_SYS_INOTIFY_H
159 int wd;
160 // Creation and Deletion of files happens infrequently, so
161 // can safely be reported as they occur. File changes i.e. those that emit "dirty()" can
162 // happen many times per second, though, so maintain a list of files in this directory
163 // that can be emitted and flushed at the next slotRescan(...).
164 // This will be unused if the Entry is not a directory.
165 QList<QString> m_pendingFileChanges;
166#endif
167 };
168
169 typedef QMap<QString, Entry> EntryMap;
170
171 KDirWatchPrivate();
172 ~KDirWatchPrivate() override;
173
174 void resetList(KDirWatch *instance, bool skippedToo);
175 void useFreq(Entry *e, int newFreq);
176 void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes = KDirWatch::WatchDirOnly);
177 void removeEntry(KDirWatch *instance, const QString &path, Entry *sub_entry);
178 void removeEntry(KDirWatch *instance, Entry *e, Entry *sub_entry);
179 bool stopEntryScan(KDirWatch *instance, Entry *e);
180 bool restartEntryScan(KDirWatch *instance, Entry *e, bool notify);
181 void stopScan(KDirWatch *instance);
182 void startScan(KDirWatch *instance, bool notify, bool skippedToo);
183
184 void removeEntries(KDirWatch *instance);
185
186 void addWatch(Entry *entry);
187 void removeWatch(Entry *entry);
188 Entry *entry(const QString &_path);
189 int scanEntry(Entry *e);
190 void emitEvent(Entry *e, int event, const QString &fileName = QString());
191
192 static bool isNoisyFile(const char *filename);
193
194 void ref(KDirWatch *watch);
195 void unref(KDirWatch *watch);
196
197#if HAVE_SYS_INOTIFY_H
198 QString inotifyEventName(const inotify_event *event) const;
199#endif
200
201public Q_SLOTS:
202 void slotRescan();
203 void inotifyEventReceived(); // for inotify
204 void slotRemoveDelayed();
205 void fswEventReceived(const QString &path); // for QFileSystemWatcher
206
207public:
208 QTimer m_statRescanTimer;
209 EntryMap m_mapEntries;
210
211 KDirWatch::Method m_preferredMethod, m_nfsPreferredMethod;
212 int freq;
213 int statEntries;
214 int m_nfsPollInterval, m_PollInterval;
215 bool useStat(Entry *e);
216
217 // removeList is allowed to contain any entry at most once
218 QSet<Entry *> removeList;
219 bool delayRemove;
220
221 bool rescan_all;
222 QTimer rescan_timer;
223
224#if HAVE_SYS_INOTIFY_H
225 QSocketNotifier *mSn;
226 bool supports_inotify;
227 int m_inotify_fd;
228 QHash<int, Entry *> m_inotify_wd_to_entry;
229
230 bool useINotify(Entry *e);
231#endif
232#if HAVE_QFILESYSTEMWATCHER
233 QFileSystemWatcher *fsWatcher;
234 bool useQFSWatch(Entry *e);
235#endif
236
237 bool _isStopped;
238
239private:
240 // Public objects that reference this thread-local private instance.
241 QList<KDirWatch *> m_referencesObjects;
242};
243
244QDebug operator<<(QDebug debug, const KDirWatchPrivate &dwp);
245QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry);
246
247#endif // KDIRWATCH_P_H
248

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