Qt 4.8
qfilesystemwatcher_inotify.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qfilesystemwatcher.h"
44 
45 #ifndef QT_NO_FILESYSTEMWATCHER
46 
47 #include "private/qcore_unix_p.h"
48 
49 #include <qdebug.h>
50 #include <qfile.h>
51 #include <qfileinfo.h>
52 #include <qsocketnotifier.h>
53 #include <qvarlengtharray.h>
54 
55 #if defined(Q_OS_LINUX)
56 #include <sys/syscall.h>
57 #include <sys/ioctl.h>
58 #include <unistd.h>
59 #include <fcntl.h>
60 #endif
61 
62 #if defined(QT_NO_INOTIFY)
63 
64 #if defined(Q_OS_QNX)
65 // These files should only be compiled on QNX if the inotify headers are found
66 #error "Should not get here."
67 #endif
68 
69 #include <linux/types.h>
70 
71 #if defined(__i386__)
72 # define __NR_inotify_init 291
73 # define __NR_inotify_add_watch 292
74 # define __NR_inotify_rm_watch 293
75 # define __NR_inotify_init1 332
76 #elif defined(__x86_64__)
77 # define __NR_inotify_init 253
78 # define __NR_inotify_add_watch 254
79 # define __NR_inotify_rm_watch 255
80 # define __NR_inotify_init1 294
81 #elif defined(__powerpc__) || defined(__powerpc64__)
82 # define __NR_inotify_init 275
83 # define __NR_inotify_add_watch 276
84 # define __NR_inotify_rm_watch 277
85 # define __NR_inotify_init1 318
86 #elif defined (__ia64__)
87 # define __NR_inotify_init 1277
88 # define __NR_inotify_add_watch 1278
89 # define __NR_inotify_rm_watch 1279
90 # define __NR_inotify_init1 1318
91 #elif defined (__s390__) || defined (__s390x__)
92 # define __NR_inotify_init 284
93 # define __NR_inotify_add_watch 285
94 # define __NR_inotify_rm_watch 286
95 # define __NR_inotify_init1 324
96 #elif defined (__alpha__)
97 # define __NR_inotify_init 444
98 # define __NR_inotify_add_watch 445
99 # define __NR_inotify_rm_watch 446
100 // no inotify_init1 for the Alpha
101 #elif defined (__sparc__) || defined (__sparc64__)
102 # define __NR_inotify_init 151
103 # define __NR_inotify_add_watch 152
104 # define __NR_inotify_rm_watch 156
105 # define __NR_inotify_init1 322
106 #elif defined (__arm__)
107 # define __NR_inotify_init 316
108 # define __NR_inotify_add_watch 317
109 # define __NR_inotify_rm_watch 318
110 # define __NR_inotify_init1 360
111 #elif defined (__sh__)
112 # define __NR_inotify_init 290
113 # define __NR_inotify_add_watch 291
114 # define __NR_inotify_rm_watch 292
115 # define __NR_inotify_init1 332
116 #elif defined (__sh64__)
117 # define __NR_inotify_init 318
118 # define __NR_inotify_add_watch 319
119 # define __NR_inotify_rm_watch 320
120 # define __NR_inotify_init1 360
121 #elif defined (__mips__)
122 # define __NR_inotify_init 284
123 # define __NR_inotify_add_watch 285
124 # define __NR_inotify_rm_watch 286
125 # define __NR_inotify_init1 329
126 #elif defined (__hppa__)
127 # define __NR_inotify_init 269
128 # define __NR_inotify_add_watch 270
129 # define __NR_inotify_rm_watch 271
130 # define __NR_inotify_init1 314
131 #elif defined (__avr32__)
132 # define __NR_inotify_init 240
133 # define __NR_inotify_add_watch 241
134 # define __NR_inotify_rm_watch 242
135 // no inotify_init1 for AVR32
136 #elif defined (__mc68000__)
137 # define __NR_inotify_init 284
138 # define __NR_inotify_add_watch 285
139 # define __NR_inotify_rm_watch 286
140 # define __NR_inotify_init1 328
141 #elif defined (__aarch64__)
142 # define __NR_inotify_init1 26
143 # define __NR_inotify_add_watch 27
144 # define __NR_inotify_rm_watch 28
145 // no inotify_init for aarch64
146 #else
147 # error "This architecture is not supported. Please talk to qt-bugs@trolltech.com"
148 #endif
149 
150 #if !defined(IN_CLOEXEC) && defined(O_CLOEXEC) && defined(__NR_inotify_init1)
151 # define IN_CLOEXEC O_CLOEXEC
152 #endif
153 
155 
156 #ifdef QT_LINUXBASE
157 // ### the LSB doesn't standardize syscall, need to wait until glib2.4 is standardized
158 static inline int syscall(...) { return -1; }
159 #endif
160 
161 static inline int inotify_init()
162 {
163 #ifdef __NR_inotify_init
164  return syscall(__NR_inotify_init);
165 #else
166  return syscall(__NR_inotify_init1, 0);
167 #endif
168 }
169 
170 static inline int inotify_add_watch(int fd, const char *name, __u32 mask)
171 {
172  return syscall(__NR_inotify_add_watch, fd, name, mask);
173 }
174 
175 static inline int inotify_rm_watch(int fd, __u32 wd)
176 {
177  return syscall(__NR_inotify_rm_watch, fd, wd);
178 }
179 
180 #ifdef IN_CLOEXEC
181 static inline int inotify_init1(int flags)
182 {
183  return syscall(__NR_inotify_init1, flags);
184 }
185 #endif
186 
187 // the following struct and values are documented in linux/inotify.h
188 extern "C" {
189 
190 struct inotify_event {
191  __s32 wd;
192  __u32 mask;
193  __u32 cookie;
194  __u32 len;
195  char name[0];
196 };
197 
198 #define IN_ACCESS 0x00000001
199 #define IN_MODIFY 0x00000002
200 #define IN_ATTRIB 0x00000004
201 #define IN_CLOSE_WRITE 0x00000008
202 #define IN_CLOSE_NOWRITE 0x00000010
203 #define IN_OPEN 0x00000020
204 #define IN_MOVED_FROM 0x00000040
205 #define IN_MOVED_TO 0x00000080
206 #define IN_CREATE 0x00000100
207 #define IN_DELETE 0x00000200
208 #define IN_DELETE_SELF 0x00000400
209 #define IN_MOVE_SELF 0x00000800
210 #define IN_UNMOUNT 0x00002000
211 #define IN_Q_OVERFLOW 0x00004000
212 #define IN_IGNORED 0x00008000
213 
214 #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
215 #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
216 }
217 
219 
220 // --------- inotify.h end ----------
221 
222 #else /* QT_NO_INOTIFY */
223 
224 #include <sys/inotify.h>
225 
226 #endif
227 
229 
231 {
232  register int fd = -1;
233 #ifdef IN_CLOEXEC
234  fd = inotify_init1(IN_CLOEXEC);
235 #endif
236  if (fd == -1) {
237  fd = inotify_init();
238  if (fd == -1)
239  return 0;
240  ::fcntl(fd, F_SETFD, FD_CLOEXEC);
241  }
242  return new QInotifyFileSystemWatcherEngine(fd);
243 }
244 
246  : inotifyFd(fd)
247 {
248  fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
249 
250  moveToThread(this);
251 }
252 
254 {
255  foreach (int id, pathToID)
256  inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
257 
258  ::close(inotifyFd);
259 }
260 
262 {
264  connect(&sn, SIGNAL(activated(int)), SLOT(readFromInotify()));
265  (void) exec();
266 }
267 
269  QStringList *files,
270  QStringList *directories)
271 {
272  QMutexLocker locker(&mutex);
273 
274  QStringList p = paths;
275  QMutableListIterator<QString> it(p);
276  while (it.hasNext()) {
277  QString path = it.next();
278  QFileInfo fi(path);
279  bool isDir = fi.isDir();
280  if (isDir) {
281  if (directories->contains(path))
282  continue;
283  } else {
284  if (files->contains(path))
285  continue;
286  }
287 
288  int wd = inotify_add_watch(inotifyFd,
289  QFile::encodeName(path),
290  (isDir
291  ? (0
292  | IN_ATTRIB
293  | IN_MOVE
294  | IN_CREATE
295  | IN_DELETE
296  | IN_DELETE_SELF
297  )
298  : (0
299  | IN_ATTRIB
300  | IN_MODIFY
301  | IN_MOVE
302  | IN_MOVE_SELF
303  | IN_DELETE_SELF
304  )));
305  if (wd <= 0) {
306  perror("QInotifyFileSystemWatcherEngine::addPaths: inotify_add_watch failed");
307  continue;
308  }
309 
310  it.remove();
311 
312  int id = isDir ? -wd : wd;
313  if (id < 0) {
314  directories->append(path);
315  } else {
316  files->append(path);
317  }
318 
319  pathToID.insert(path, id);
320  idToPath.insert(id, path);
321  }
322 
323  start();
324 
325  return p;
326 }
327 
329  QStringList *files,
330  QStringList *directories)
331 {
332  QMutexLocker locker(&mutex);
333 
334  QStringList p = paths;
335  QMutableListIterator<QString> it(p);
336  while (it.hasNext()) {
337  QString path = it.next();
338  int id = pathToID.take(path);
339  QString x = idToPath.take(id);
340  if (x.isEmpty() || x != path)
341  continue;
342 
343  int wd = id < 0 ? -id : id;
344  // qDebug() << "removing watch for path" << path << "wd" << wd;
345  inotify_rm_watch(inotifyFd, wd);
346 
347  it.remove();
348  if (id < 0) {
349  directories->removeAll(path);
350  } else {
351  files->removeAll(path);
352  }
353  }
354 
355  return p;
356 }
357 
359 {
360  quit();
361 }
362 
364 {
365  QMutexLocker locker(&mutex);
366 
367  // qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify";
368 
369  int buffSize = 0;
370  ioctl(inotifyFd, FIONREAD, (char *) &buffSize);
371  QVarLengthArray<char, 4096> buffer(buffSize);
372  buffSize = read(inotifyFd, buffer.data(), buffSize);
373  char *at = buffer.data();
374  char * const end = at + buffSize;
375 
376  QHash<int, inotify_event *> eventForId;
377  while (at < end) {
378  inotify_event *event = reinterpret_cast<inotify_event *>(at);
379 
380  if (eventForId.contains(event->wd))
381  eventForId[event->wd]->mask |= event->mask;
382  else
383  eventForId.insert(event->wd, event);
384 
385  at += sizeof(inotify_event) + event->len;
386  }
387 
389  while (it != eventForId.constEnd()) {
390  const inotify_event &event = **it;
391  ++it;
392 
393  // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask;
394 
395  int id = event.wd;
396  QString path = idToPath.value(id);
397  if (path.isEmpty()) {
398  // perhaps a directory?
399  id = -id;
400  path = idToPath.value(id);
401  if (path.isEmpty())
402  continue;
403  }
404 
405  // qDebug() << "event for path" << path;
406 
407  if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
408  pathToID.remove(path);
409  idToPath.remove(id);
410  inotify_rm_watch(inotifyFd, event.wd);
411 
412  if (id < 0)
413  emit directoryChanged(path, true);
414  else
415  emit fileChanged(path, true);
416  } else {
417  if (id < 0)
418  emit directoryChanged(path, false);
419  else
420  emit fileChanged(path, false);
421  }
422  }
423 }
424 
426 
427 #endif // QT_NO_FILESYSTEMWATCHER
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
The QHash::const_iterator class provides an STL-style const iterator for QHash and QMultiHash...
Definition: qhash.h:395
void fileChanged(const QString &path, bool removed)
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
int remove(const Key &key)
Removes all the items that have the key from the hash.
Definition: qhash.h:784
#define it(className, varName)
#define at(className, varName)
#define SLOT(a)
Definition: qobjectdefs.h:226
static QInotifyFileSystemWatcherEngine * create()
The QString class provides a Unicode character string.
Definition: qstring.h:83
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it...
Definition: qhash.h:807
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
virtual bool event(QEvent *)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition: qobject.cpp:1200
bool contains(const Key &key) const
Returns true if the hash contains an item with the key; otherwise returns false.
Definition: qhash.h:872
void quit()
Tells the thread&#39;s event loop to exit with return code 0 (success).
Definition: qthread.cpp:614
const T value(const Key &key) const
Returns the value associated with the key.
Definition: qhash.h:606
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:753
The QSocketNotifier class provides support for monitoring activity on a file descriptor.
#define SIGNAL(a)
Definition: qobjectdefs.h:227
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory; otherwise ret...
Definition: qfileinfo.cpp:990
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
const char * name
#define emit
Definition: qobjectdefs.h:76
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
QBool contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the list contains the string str; otherwise returns false.
Definition: qstringlist.h:172
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
Definition: qobject.cpp:1458
void directoryChanged(const QString &path, bool removed)
const_iterator constBegin() const
Returns a const STL-style iterator pointing to the first item in the hash.
Definition: qhash.h:466
const_iterator constEnd() const
Returns a const STL-style iterator pointing to the imaginary item after the last item in the hash...
Definition: qhash.h:469
int fcntl(int, int,...)
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
void start(Priority=InheritPriority)
Begins execution of the thread by calling run().
int exec()
Enters the event loop and waits until exit() is called, returning the value that was passed to exit()...
Definition: qthread.cpp:551
static QByteArray encodeName(const QString &fileName)
By default, this function converts fileName to the local 8-bit encoding determined by the user&#39;s loca...
Definition: qfile.cpp:528
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:60
static const KeyPair *const end
void run()
The starting point for the thread.
int removeAll(const T &t)
Removes all occurrences of value in the list and returns the number of entries removed.
Definition: qlist.h:770