Qt 6.x
The Qt SDK
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)
216 inotifyFd(fd),
218{
219 fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
220 connect(&notifier, SIGNAL(activated(QSocketDescriptor)), SLOT(readFromInotify()));
221}
222
224{
225 notifier.setEnabled(false);
226 for (int id : std::as_const(pathToID))
227 inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
228
229 ::close(inotifyFd);
230}
231
234 QStringList *directories)
235{
236 QStringList unhandled;
237 for (const QString &path : paths) {
239 bool isDir = fi.isDir();
240 auto sg = qScopeGuard([&]{ unhandled.push_back(path); });
241 if (isDir) {
242 if (directories->contains(path))
243 continue;
244 } else {
245 if (files->contains(path))
246 continue;
247 }
248
249 int wd = inotify_add_watch(inotifyFd,
251 (isDir
252 ? (0
253 | IN_ATTRIB
254 | IN_MOVE
255 | IN_CREATE
256 | IN_DELETE
257 | IN_DELETE_SELF
258 )
259 : (0
260 | IN_ATTRIB
261 | IN_MODIFY
262 | IN_MOVE
263 | IN_MOVE_SELF
264 | IN_DELETE_SELF
265 )));
266 if (wd < 0) {
267 if (errno != ENOENT)
268 qErrnoWarning("inotify_add_watch(%ls) failed:", path.constData());
269 continue;
270 }
271
272 sg.dismiss();
273
274 int id = isDir ? -wd : wd;
275 if (id < 0) {
276 directories->append(path);
277 } else {
278 files->append(path);
279 }
280
281 pathToID.insert(path, id);
282 idToPath.insert(id, path);
283 }
284
285 return unhandled;
286}
287
290 QStringList *directories)
291{
292 QStringList unhandled;
293 for (const QString &path : paths) {
294 int id = pathToID.take(path);
295
296 auto sg = qScopeGuard([&]{ unhandled.push_back(path); });
297
298 // Multiple paths could be associated to the same watch descriptor
299 // when a file is moved and added with the new name.
300 // So we should find and delete the correct one by using
301 // both id and path
302 auto path_range = idToPath.equal_range(id);
303 auto path_it = std::find(path_range.first, path_range.second, path);
304 if (path_it == idToPath.end())
305 continue;
306
307 const ssize_t num_elements = std::distance(path_range.first, path_range.second);
308 idToPath.erase(path_it);
309
310 // If there was only one path associated to the given id we should remove the watch
311 if (num_elements == 1) {
312 int wd = id < 0 ? -id : id;
313 inotify_rm_watch(inotifyFd, wd);
314 }
315
316 sg.dismiss();
317
318 if (id < 0) {
319 directories->removeAll(path);
320 } else {
321 files->removeAll(path);
322 }
323 }
324
325 return unhandled;
326}
327
328void QInotifyFileSystemWatcherEngine::readFromInotify()
329{
330 // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify");
331
332 int buffSize = 0;
333 if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0)
334 return;
335
337 buffSize = int(read(inotifyFd, buffer.data(), buffSize));
338 char *at = buffer.data();
339 char * const end = at + buffSize;
340
342 while (at < end) {
343 inotify_event *event = reinterpret_cast<inotify_event *>(at);
344
345 if (eventForId.contains(event->wd))
346 eventForId[event->wd]->mask |= event->mask;
347 else
348 eventForId.insert(event->wd, event);
349
350 at += sizeof(inotify_event) + event->len;
351 }
352
354 while (it != eventForId.constEnd()) {
355 const inotify_event &event = **it;
356 ++it;
357
358 // qDebug() << "inotify event, wd" << event.wd << "mask" << Qt::hex << event.mask;
359
360 int id = event.wd;
361 QString path = getPathFromID(id);
362 if (path.isEmpty()) {
363 // perhaps a directory?
364 id = -id;
365 path = getPathFromID(id);
366 if (path.isEmpty())
367 continue;
368 }
369
370 // qDebug() << "event for path" << path;
371
372 if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
373 pathToID.remove(path);
374 idToPath.remove(id, getPathFromID(id));
375 if (!idToPath.contains(id))
376 inotify_rm_watch(inotifyFd, event.wd);
377
378 if (id < 0)
380 else
381 emit fileChanged(path, true);
382 } else {
383 if (id < 0)
384 emit directoryChanged(path, false);
385 else
386 emit fileChanged(path, false);
387 }
388 }
389}
390
391template <typename Hash, typename Key>
392typename Hash::const_iterator
393find_last_in_equal_range(const Hash &c, const Key &key)
394{
395 // find c.equal_range(key).second - 1 without backwards iteration:
396 auto i = c.find(key);
397 const auto end = c.cend();
398 if (i == end)
399 return end;
400 decltype(i) prev;
401 do {
402 prev = i;
403 ++i;
404 } while (i != end && i.key() == key);
405 return prev;
406}
407
408QString QInotifyFileSystemWatcherEngine::getPathFromID(int id) const
409{
410 auto i = find_last_in_equal_range(idToPath, id);
411 return i == idToPath.cend() ? QString() : i.value() ;
412}
413
415
416#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
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
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:1135
\inmodule QtCore
Definition qhash.h:818
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:956
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1205
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:975
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
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
QPair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2128
const_iterator cend() const noexcept
Definition qhash.h:1832
bool contains(const Key &key) const noexcept
Definition qhash.h:1567
qsizetype remove(const Key &key)
Definition qhash.h:1519
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1930
iterator erase(const_iterator it)
Definition qhash.h:1873
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1830
\inmodule QtCore
Definition qobject.h:90
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
\inmodule QtCore
\inmodule QtCore
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:127
Definition lalr.h:269
#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)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
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
GLenum GLsizei len
GLsizei const GLchar *const * path
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define emit
ReturnedValue read(const char *data)
QFileInfo fi("c:/temp/foo")
[newstuff]
QStringList files
[8]
QAction * at
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent