Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfilesystemwatcher_inotify.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include "private/qcore_unix_p.h"
8#include "private/qsystemerror_p.h"
9
10#include <qdebug.h>
11#include <qfile.h>
12#include <qfileinfo.h>
13#include <qscopeguard.h>
14#include <qsocketnotifier.h>
15#include <qvarlengtharray.h>
16
17#if defined(Q_OS_LINUX)
18#include <sys/syscall.h>
19#include <sys/ioctl.h>
20#include <unistd.h>
21#include <fcntl.h>
22#endif
23
24#if defined(QT_NO_INOTIFY)
25
26#if defined(Q_OS_QNX)
27// These files should only be compiled on QNX if the inotify headers are found
28#error "Should not get here."
29#endif
30
31#include <linux/types.h>
32
33#if defined(__i386__)
34# define __NR_inotify_init 291
35# define __NR_inotify_add_watch 292
36# define __NR_inotify_rm_watch 293
37# define __NR_inotify_init1 332
38#elif defined(__x86_64__)
39# define __NR_inotify_init 253
40# define __NR_inotify_add_watch 254
41# define __NR_inotify_rm_watch 255
42# define __NR_inotify_init1 294
43#elif defined(__powerpc__) || defined(__powerpc64__)
44# define __NR_inotify_init 275
45# define __NR_inotify_add_watch 276
46# define __NR_inotify_rm_watch 277
47# define __NR_inotify_init1 318
48#elif defined (__ia64__)
49# define __NR_inotify_init 1277
50# define __NR_inotify_add_watch 1278
51# define __NR_inotify_rm_watch 1279
52# define __NR_inotify_init1 1318
53#elif defined (__s390__) || defined (__s390x__)
54# define __NR_inotify_init 284
55# define __NR_inotify_add_watch 285
56# define __NR_inotify_rm_watch 286
57# define __NR_inotify_init1 324
58#elif defined (__alpha__)
59# define __NR_inotify_init 444
60# define __NR_inotify_add_watch 445
61# define __NR_inotify_rm_watch 446
62// no inotify_init1 for the Alpha
63#elif defined (__sparc__) || defined (__sparc64__)
64# define __NR_inotify_init 151
65# define __NR_inotify_add_watch 152
66# define __NR_inotify_rm_watch 156
67# define __NR_inotify_init1 322
68#elif defined (__arm__)
69# define __NR_inotify_init 316
70# define __NR_inotify_add_watch 317
71# define __NR_inotify_rm_watch 318
72# define __NR_inotify_init1 360
73#elif defined (__sh__)
74# define __NR_inotify_init 290
75# define __NR_inotify_add_watch 291
76# define __NR_inotify_rm_watch 292
77# define __NR_inotify_init1 332
78#elif defined (__sh64__)
79# define __NR_inotify_init 318
80# define __NR_inotify_add_watch 319
81# define __NR_inotify_rm_watch 320
82# define __NR_inotify_init1 360
83#elif defined (__mips__)
84# define __NR_inotify_init 284
85# define __NR_inotify_add_watch 285
86# define __NR_inotify_rm_watch 286
87# define __NR_inotify_init1 329
88#elif defined (__hppa__)
89# define __NR_inotify_init 269
90# define __NR_inotify_add_watch 270
91# define __NR_inotify_rm_watch 271
92# define __NR_inotify_init1 314
93#elif defined (__avr32__)
94# define __NR_inotify_init 240
95# define __NR_inotify_add_watch 241
96# define __NR_inotify_rm_watch 242
97// no inotify_init1 for AVR32
98#elif defined (__mc68000__)
99# define __NR_inotify_init 284
100# define __NR_inotify_add_watch 285
101# define __NR_inotify_rm_watch 286
102# define __NR_inotify_init1 328
103#elif defined (__aarch64__)
104# define __NR_inotify_init1 26
105# define __NR_inotify_add_watch 27
106# define __NR_inotify_rm_watch 28
107// no inotify_init for aarch64
108#else
109# error "This architecture is not supported. Please see http://www.qt-project.org/"
110#endif
111
112#if !defined(IN_CLOEXEC) && defined(O_CLOEXEC) && defined(__NR_inotify_init1)
113# define IN_CLOEXEC O_CLOEXEC
114#endif
115
117
118#ifdef QT_LINUXBASE
119// ### the LSB doesn't standardize syscall, need to wait until glib2.4 is standardized
120static inline int syscall(...) { return -1; }
121#endif
122
123static inline int inotify_init()
124{
125#ifdef __NR_inotify_init
126 return syscall(__NR_inotify_init);
127#else
128 return syscall(__NR_inotify_init1, 0);
129#endif
130}
131
132static inline int inotify_add_watch(int fd, const char *name, __u32 mask)
133{
134 return syscall(__NR_inotify_add_watch, fd, name, mask);
135}
136
137static inline int inotify_rm_watch(int fd, __u32 wd)
138{
139 return syscall(__NR_inotify_rm_watch, fd, wd);
140}
141
142#ifdef IN_CLOEXEC
143static inline int inotify_init1(int flags)
144{
145 return syscall(__NR_inotify_init1, flags);
146}
147#endif
148
149// the following struct and values are documented in linux/inotify.h
150extern "C" {
151
152struct inotify_event {
153 __s32 wd;
154 __u32 mask;
155 __u32 cookie;
156 __u32 len;
157 char name[0];
158};
159
160#define IN_ACCESS 0x00000001
161#define IN_MODIFY 0x00000002
162#define IN_ATTRIB 0x00000004
163#define IN_CLOSE_WRITE 0x00000008
164#define IN_CLOSE_NOWRITE 0x00000010
165#define IN_OPEN 0x00000020
166#define IN_MOVED_FROM 0x00000040
167#define IN_MOVED_TO 0x00000080
168#define IN_CREATE 0x00000100
169#define IN_DELETE 0x00000200
170#define IN_DELETE_SELF 0x00000400
171#define IN_MOVE_SELF 0x00000800
172#define IN_UNMOUNT 0x00002000
173#define IN_Q_OVERFLOW 0x00004000
174#define IN_IGNORED 0x00008000
175
176#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
177#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
178}
179
181
182// --------- inotify.h end ----------
183
184#else /* QT_NO_INOTIFY */
185
186#include <sys/inotify.h>
187
188// see https://github.com/android-ndk/ndk/issues/394
189# if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
190static inline int inotify_init1(int flags)
191{
192 return syscall(__NR_inotify_init1, flags);
193}
194# endif
195
196#endif
197
199
201{
202 int fd = -1;
203#if defined(IN_CLOEXEC)
204 fd = inotify_init1(IN_CLOEXEC);
205#endif
206 if (fd == -1) {
207 fd = inotify_init();
208 if (fd == -1)
209 return nullptr;
210 }
212}
213
214QInotifyFileSystemWatcherEngine::QInotifyFileSystemWatcherEngine(int fd, QObject *parent)
215 : QFileSystemWatcherEngine(parent),
216 inotifyFd(fd),
218{
219 fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
221 this, &QInotifyFileSystemWatcherEngine::readFromInotify);
222}
223
225{
226 notifier.setEnabled(false);
227 for (int id : std::as_const(pathToID))
228 inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
229
230 ::close(inotifyFd);
231}
232
235 QStringList *directories)
236{
237 QStringList unhandled;
238 for (const QString &path : paths) {
239 QFileInfo fi(path);
240 bool isDir = fi.isDir();
241 auto sg = qScopeGuard([&]{ unhandled.push_back(path); });
242 if (isDir) {
243 if (directories->contains(path))
244 continue;
245 } else {
246 if (files->contains(path))
247 continue;
248 }
249
250 int wd = inotify_add_watch(inotifyFd,
252 (isDir
253 ? (0
254 | IN_ATTRIB
255 | IN_MOVE
256 | IN_CREATE
257 | IN_DELETE
258 | IN_DELETE_SELF
259 )
260 : (0
261 | IN_ATTRIB
262 | IN_MODIFY
263 | IN_MOVE
264 | IN_MOVE_SELF
265 | IN_DELETE_SELF
266 )));
267 if (wd < 0) {
268 if (errno != ENOENT)
269 qErrnoWarning("inotify_add_watch(%ls) failed:", path.constData());
270 continue;
271 }
272
273 sg.dismiss();
274
275 int id = isDir ? -wd : wd;
276 if (id < 0) {
277 directories->append(path);
278 } else {
279 files->append(path);
280 }
281
282 pathToID.insert(path, id);
283 idToPath.insert(id, path);
284 }
285
286 return unhandled;
287}
288
291 QStringList *directories)
292{
293 QStringList unhandled;
294 for (const QString &path : paths) {
295 int id = pathToID.take(path);
296
297 auto sg = qScopeGuard([&]{ unhandled.push_back(path); });
298
299 // Multiple paths could be associated to the same watch descriptor
300 // when a file is moved and added with the new name.
301 // So we should find and delete the correct one by using
302 // both id and path
303 auto path_range = idToPath.equal_range(id);
304 auto path_it = std::find(path_range.first, path_range.second, path);
305 if (path_it == idToPath.end())
306 continue;
307
308 const ssize_t num_elements = std::distance(path_range.first, path_range.second);
309 idToPath.erase(path_it);
310
311 // If there was only one path associated to the given id we should remove the watch
312 if (num_elements == 1) {
313 int wd = id < 0 ? -id : id;
314 inotify_rm_watch(inotifyFd, wd);
315 }
316
317 sg.dismiss();
318
319 if (id < 0) {
320 directories->removeAll(path);
321 } else {
322 files->removeAll(path);
323 }
324 }
325
326 return unhandled;
327}
328
329void QInotifyFileSystemWatcherEngine::readFromInotify()
330{
331 // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify");
332
333 int buffSize = 0;
334 if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0)
335 return;
336
337 QVarLengthArray<char, 4096> buffer(buffSize);
338 buffSize = int(read(inotifyFd, buffer.data(), buffSize));
339 char *at = buffer.data();
340 char * const end = at + buffSize;
341
342 QHash<int, inotify_event *> eventForId;
343 while (at < end) {
344 inotify_event *event = reinterpret_cast<inotify_event *>(at);
345
346 if (eventForId.contains(event->wd))
347 eventForId[event->wd]->mask |= event->mask;
348 else
349 eventForId.insert(event->wd, event);
350
351 at += sizeof(inotify_event) + event->len;
352 }
353
355 while (it != eventForId.constEnd()) {
356 const inotify_event &event = **it;
357 ++it;
358
359 // qDebug() << "inotify event, wd" << event.wd << "mask" << Qt::hex << event.mask;
360
361 int id = event.wd;
362 QString path = getPathFromID(id);
363 if (path.isEmpty()) {
364 // perhaps a directory?
365 id = -id;
366 path = getPathFromID(id);
367 if (path.isEmpty())
368 continue;
369 }
370
371 // qDebug() << "event for path" << path;
372
373 if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
374 pathToID.remove(path);
375 idToPath.remove(id, getPathFromID(id));
376 if (!idToPath.contains(id))
377 inotify_rm_watch(inotifyFd, event.wd);
378
379 if (id < 0)
381 else
382 emit fileChanged(path, true);
383 } else {
384 if (id < 0)
385 emit directoryChanged(path, false);
386 else
387 emit fileChanged(path, false);
388 }
389 }
390}
391
392template <typename Hash, typename Key>
393typename Hash::const_iterator
394find_last_in_equal_range(const Hash &c, const Key &key)
395{
396 // find c.equal_range(key).second - 1 without backwards iteration:
397 auto i = c.find(key);
398 const auto end = c.cend();
399 if (i == end)
400 return end;
401 decltype(i) prev;
402 do {
403 prev = i;
404 ++i;
405 } while (i != end && i.key() == key);
406 return prev;
407}
408
409QString QInotifyFileSystemWatcherEngine::getPathFromID(int id) const
410{
411 auto i = find_last_in_equal_range(idToPath, id);
412 return i == idToPath.cend() ? QString() : i.value() ;
413}
414
416
417#include "moc_qfilesystemwatcher_inotify_p.cpp"
DarwinBluetooth::LECBManagerNotifier * notifier
QVariant data() const
Returns the user data as set in QAction::setData.
Definition qaction.cpp:1056
void directoryChanged(const QString &path, bool removed)
void fileChanged(const QString &path, bool removed)
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
\inmodule QtCore
Definition qhash.h:1145
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:985
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
static QInotifyFileSystemWatcherEngine * create(QObject *parent)
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories) override
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories) override
std::pair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2266
const_iterator cend() const noexcept
Definition qhash.h:1924
bool contains(const Key &key) const noexcept
Definition qhash.h:1658
qsizetype remove(const Key &key)
Definition qhash.h:1596
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
iterator erase(const_iterator it)
Definition qhash.h:1965
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1922
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
\inmodule QtCore
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
void setEnabled(bool)
If enable is true, the notifier is enabled; otherwise the notifier is disabled.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
Definition lalr.h:268
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
Hash::const_iterator find_last_in_equal_range(const Hash &c, const Key &key)
GLuint64 key
GLuint GLuint end
GLenum GLuint id
[7]
GLenum GLuint buffer
GLsizei const GLuint * paths
GLbitfield flags
GLuint64 GLenum GLint fd
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
struct _cl_event * event
const GLubyte * c
GLsizei const GLchar *const * path
GLenum GLsizei len
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define emit
ReturnedValue read(const char *data)
QStringList files
[8]
QAction * at