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> |
35 | class 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 |
47 | struct inotify_event; |
48 | #endif |
49 | |
50 | /* KDirWatchPrivate is a singleton and does the watching |
51 | * for every KDirWatch instance in the application. |
52 | */ |
53 | class KDirWatchPrivate : public QObject |
54 | { |
55 | Q_OBJECT |
56 | public: |
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 | |
196 | public Q_SLOTS: |
197 | void slotRescan(); |
198 | void inotifyEventReceived(); // for inotify |
199 | void slotRemoveDelayed(); |
200 | void fswEventReceived(const QString &path); // for QFileSystemWatcher |
201 | |
202 | public: |
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 | |
234 | private: |
235 | // Public objects that reference this thread-local private instance. |
236 | QList<KDirWatch *> m_referencesObjects; |
237 | }; |
238 | |
239 | QDebug operator<<(QDebug debug, const KDirWatchPrivate &dwp); |
240 | QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry); |
241 | |
242 | #endif // KDIRWATCH_P_H |
243 | |