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_kqueue.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
4#include <qplatformdefs.h>
5
8#include "private/qcore_unix_p.h"
9
10#include <qdebug.h>
11#include <qfile.h>
12#include <qscopeguard.h>
13#include <qsocketnotifier.h>
14#include <qvarlengtharray.h>
15
16#include <sys/types.h>
17#include <sys/event.h>
18#include <sys/stat.h>
19#include <sys/time.h>
20#include <fcntl.h>
21
23
24// #define KEVENT_DEBUG
25#ifdef KEVENT_DEBUG
26# define DEBUG qDebug
27#else
28# define DEBUG if(false)qDebug
29#endif
30
32{
33 int kqfd = kqueue();
34 if (kqfd == -1)
35 return 0;
36 return new QKqueueFileSystemWatcherEngine(kqfd, parent);
37}
38
39QKqueueFileSystemWatcherEngine::QKqueueFileSystemWatcherEngine(int kqfd, QObject *parent)
41 kqfd(kqfd),
43{
44 connect(&notifier, SIGNAL(activated(QSocketDescriptor)), SLOT(readFromKqueue()));
45
46 fcntl(kqfd, F_SETFD, FD_CLOEXEC);
47}
48
50{
51 notifier.setEnabled(false);
52 close(kqfd);
53
54 for (int id : std::as_const(pathToID))
55 ::close(id < 0 ? -id : id);
56}
57
60 QStringList *directories)
61{
62 QStringList unhandled;
63 for (const QString &path : paths) {
64 auto sg = qScopeGuard([&]{unhandled.push_back(path);});
65 int fd;
66#if defined(O_EVTONLY)
68#else
70#endif
71 if (fd == -1) {
72 perror("QKqueueFileSystemWatcherEngine::addPaths: open");
73 continue;
74 }
75 if (fd >= (int)FD_SETSIZE / 2 && fd < (int)FD_SETSIZE) {
76 int fddup = qt_safe_dup(fd, FD_SETSIZE);
77 if (fddup != -1) {
78 ::close(fd);
79 fd = fddup;
80 }
81 }
82
83 QT_STATBUF st;
84 if (QT_FSTAT(fd, &st) == -1) {
85 perror("QKqueueFileSystemWatcherEngine::addPaths: fstat");
86 ::close(fd);
87 continue;
88 }
89 int id = (S_ISDIR(st.st_mode)) ? -fd : fd;
90 if (id < 0) {
91 if (directories->contains(path)) {
92 ::close(fd);
93 continue;
94 }
95 } else {
96 if (files->contains(path)) {
97 ::close(fd);
98 continue;
99 }
100 }
101
102 struct kevent kev;
103 EV_SET(&kev,
104 fd,
105 EVFILT_VNODE,
106 EV_ADD | EV_ENABLE | EV_CLEAR,
107 NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
108 0,
109 0);
110 if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
111 perror("QKqueueFileSystemWatcherEngine::addPaths: kevent");
112 ::close(fd);
113 continue;
114 }
115
116 sg.dismiss();
117
118 if (id < 0) {
119 DEBUG() << "QKqueueFileSystemWatcherEngine: added directory path" << path;
120 directories->append(path);
121 } else {
122 DEBUG() << "QKqueueFileSystemWatcherEngine: added file path" << path;
123 files->append(path);
124 }
125
126 pathToID.insert(path, id);
127 idToPath.insert(id, path);
128 }
129
130 return unhandled;
131}
132
135 QStringList *directories)
136{
137 if (pathToID.isEmpty())
138 return paths;
139
140 QStringList unhandled;
141 for (const QString &path : paths) {
142 auto sg = qScopeGuard([&]{unhandled.push_back(path);});
143 int id = pathToID.take(path);
144 QString x = idToPath.take(id);
145 if (x.isEmpty() || x != path)
146 continue;
147
148 ::close(id < 0 ? -id : id);
149
150 sg.dismiss();
151
152 if (id < 0)
153 directories->removeAll(path);
154 else
155 files->removeAll(path);
156 }
157
158 return unhandled;
159}
160
161void QKqueueFileSystemWatcherEngine::readFromKqueue()
162{
163 forever {
164 DEBUG() << "QKqueueFileSystemWatcherEngine: polling for changes";
165 int r;
166 struct kevent kev;
167 struct timespec ts = { 0, 0 }; // 0 ts, because we want to poll
168 QT_EINTR_LOOP(r, kevent(kqfd, 0, 0, &kev, 1, &ts));
169 if (r < 0) {
170 perror("QKqueueFileSystemWatcherEngine: error during kevent wait");
171 return;
172 } else if (r == 0) {
173 // polling returned no events, so stop
174 break;
175 } else {
176 int fd = kev.ident;
177
178 DEBUG() << "QKqueueFileSystemWatcherEngine: processing kevent" << kev.ident << kev.filter;
179
180 int id = fd;
181 QString path = idToPath.value(id);
182 if (path.isEmpty()) {
183 // perhaps a directory?
184 id = -id;
185 path = idToPath.value(id);
186 if (path.isEmpty()) {
187 DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent for a file we're not watching";
188 continue;
189 }
190 }
191 if (kev.filter != EVFILT_VNODE) {
192 DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent with the wrong filter";
193 continue;
194 }
195
196 if ((kev.fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) != 0) {
197 DEBUG() << path << "removed, removing watch also";
198
199 pathToID.remove(path);
200 idToPath.remove(id);
201 ::close(fd);
202
203 if (id < 0)
205 else
206 emit fileChanged(path, true);
207 } else {
208 DEBUG() << path << "changed";
209
210 if (id < 0)
211 emit directoryChanged(path, false);
212 else
213 emit fileChanged(path, false);
214 }
215 }
216
217 }
218}
219
221
222#include "moc_qfilesystemwatcher_kqueue_p.cpp"
DarwinBluetooth::LECBManagerNotifier * notifier
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
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
T value(const Key &key) const noexcept
Definition qhash.h:1054
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:928
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories) override
static QKqueueFileSystemWatcherEngine * create(QObject *parent)
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories) override
\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
\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:129
Definition lalr.h:268
#define this
Definition dialogs.cpp:9
Combined button and popup list for selecting options.
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
#define QT_EINTR_LOOP(var, cmd)
static int qt_safe_dup(int oldfd, int atleast=0, int flags=FD_CLOEXEC)
#define forever
Definition qforeach.h:78
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLint GLint GLint GLint GLint x
[0]
GLboolean r
[2]
GLenum GLuint id
[7]
GLsizei const GLuint * paths
GLuint64 GLenum GLint fd
GLsizei const GLchar *const * path
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define emit
QStringList files
[8]