Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qplatformdefs.h>
41
42#include "qfilesystemwatcher.h"
43#include "qfilesystemwatcher_kqueue_p.h"
44#include "private/qcore_unix_p.h"
45
46#include <qdebug.h>
47#include <qfile.h>
48#include <qscopeguard.h>
49#include <qsocketnotifier.h>
50#include <qvarlengtharray.h>
51
52#include <sys/types.h>
53#include <sys/event.h>
54#include <sys/stat.h>
55#include <sys/time.h>
56#include <fcntl.h>
57
58QT_BEGIN_NAMESPACE
59
60// #define KEVENT_DEBUG
61#ifdef KEVENT_DEBUG
62# define DEBUG qDebug
63#else
64# define DEBUG if(false)qDebug
65#endif
66
67QKqueueFileSystemWatcherEngine *QKqueueFileSystemWatcherEngine::create(QObject *parent)
68{
69 int kqfd = kqueue();
70 if (kqfd == -1)
71 return 0;
72 return new QKqueueFileSystemWatcherEngine(kqfd, parent);
73}
74
75QKqueueFileSystemWatcherEngine::QKqueueFileSystemWatcherEngine(int kqfd, QObject *parent)
76 : QFileSystemWatcherEngine(parent),
77 kqfd(kqfd),
78 notifier(kqfd, QSocketNotifier::Read, this)
79{
80 connect(&notifier, SIGNAL(activated(int)), SLOT(readFromKqueue()));
81
82 fcntl(kqfd, F_SETFD, FD_CLOEXEC);
83}
84
85QKqueueFileSystemWatcherEngine::~QKqueueFileSystemWatcherEngine()
86{
87 notifier.setEnabled(false);
88 close(kqfd);
89
90 for (int id : qAsConst(pathToID))
91 ::close(id < 0 ? -id : id);
92}
93
94QStringList QKqueueFileSystemWatcherEngine::addPaths(const QStringList &paths,
95 QStringList *files,
96 QStringList *directories)
97{
98 QStringList unhandled;
99 for (const QString &path : paths) {
100 auto sg = qScopeGuard([&]{unhandled.push_back(path);});
101 int fd;
102#if defined(O_EVTONLY)
103 fd = qt_safe_open(QFile::encodeName(path), O_EVTONLY);
104#else
105 fd = qt_safe_open(QFile::encodeName(path), O_RDONLY);
106#endif
107 if (fd == -1) {
108 perror("QKqueueFileSystemWatcherEngine::addPaths: open");
109 continue;
110 }
111 if (fd >= (int)FD_SETSIZE / 2 && fd < (int)FD_SETSIZE) {
112 int fddup = qt_safe_dup(fd, FD_SETSIZE);
113 if (fddup != -1) {
114 ::close(fd);
115 fd = fddup;
116 }
117 }
118
119 QT_STATBUF st;
120 if (QT_FSTAT(fd, &st) == -1) {
121 perror("QKqueueFileSystemWatcherEngine::addPaths: fstat");
122 ::close(fd);
123 continue;
124 }
125 int id = (S_ISDIR(st.st_mode)) ? -fd : fd;
126 if (id < 0) {
127 if (directories->contains(path)) {
128 ::close(fd);
129 continue;
130 }
131 } else {
132 if (files->contains(path)) {
133 ::close(fd);
134 continue;
135 }
136 }
137
138 struct kevent kev;
139 EV_SET(&kev,
140 fd,
141 EVFILT_VNODE,
142 EV_ADD | EV_ENABLE | EV_CLEAR,
143 NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
144 0,
145 0);
146 if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
147 perror("QKqueueFileSystemWatcherEngine::addPaths: kevent");
148 ::close(fd);
149 continue;
150 }
151
152 sg.dismiss();
153
154 if (id < 0) {
155 DEBUG() << "QKqueueFileSystemWatcherEngine: added directory path" << path;
156 directories->append(path);
157 } else {
158 DEBUG() << "QKqueueFileSystemWatcherEngine: added file path" << path;
159 files->append(path);
160 }
161
162 pathToID.insert(path, id);
163 idToPath.insert(id, path);
164 }
165
166 return unhandled;
167}
168
169QStringList QKqueueFileSystemWatcherEngine::removePaths(const QStringList &paths,
170 QStringList *files,
171 QStringList *directories)
172{
173 if (pathToID.isEmpty())
174 return paths;
175
176 QStringList unhandled;
177 for (const QString &path : paths) {
178 auto sg = qScopeGuard([&]{unhandled.push_back(path);});
179 int id = pathToID.take(path);
180 QString x = idToPath.take(id);
181 if (x.isEmpty() || x != path)
182 continue;
183
184 ::close(id < 0 ? -id : id);
185
186 sg.dismiss();
187
188 if (id < 0)
189 directories->removeAll(path);
190 else
191 files->removeAll(path);
192 }
193
194 return unhandled;
195}
196
197void QKqueueFileSystemWatcherEngine::readFromKqueue()
198{
199 forever {
200 DEBUG() << "QKqueueFileSystemWatcherEngine: polling for changes";
201 int r;
202 struct kevent kev;
203 struct timespec ts = { 0, 0 }; // 0 ts, because we want to poll
204 EINTR_LOOP(r, kevent(kqfd, 0, 0, &kev, 1, &ts));
205 if (r < 0) {
206 perror("QKqueueFileSystemWatcherEngine: error during kevent wait");
207 return;
208 } else if (r == 0) {
209 // polling returned no events, so stop
210 break;
211 } else {
212 int fd = kev.ident;
213
214 DEBUG() << "QKqueueFileSystemWatcherEngine: processing kevent" << kev.ident << kev.filter;
215
216 int id = fd;
217 QString path = idToPath.value(id);
218 if (path.isEmpty()) {
219 // perhaps a directory?
220 id = -id;
221 path = idToPath.value(id);
222 if (path.isEmpty()) {
223 DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent for a file we're not watching";
224 continue;
225 }
226 }
227 if (kev.filter != EVFILT_VNODE) {
228 DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent with the wrong filter";
229 continue;
230 }
231
232 if ((kev.fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) != 0) {
233 DEBUG() << path << "removed, removing watch also";
234
235 pathToID.remove(path);
236 idToPath.remove(id);
237 ::close(fd);
238
239 if (id < 0)
240 emit directoryChanged(path, true);
241 else
242 emit fileChanged(path, true);
243 } else {
244 DEBUG() << path << "changed";
245
246 if (id < 0)
247 emit directoryChanged(path, false);
248 else
249 emit fileChanged(path, false);
250 }
251 }
252
253 }
254}
255
256QT_END_NAMESPACE
257

Warning: That file was not part of the compilation database. It may have many parsing errors.