Qt 4.8
qfilesystemwatcher_dnotify.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 "qplatformdefs.h"
43 #include "qfilesystemwatcher.h"
45 
46 #ifndef QT_NO_FILESYSTEMWATCHER
47 
48 #include <qsocketnotifier.h>
49 #include <qcoreapplication.h>
50 #include <qfileinfo.h>
51 #include <qtimer.h>
52 #include <qwaitcondition.h>
53 #include <qmutex.h>
54 #include <dirent.h>
55 #include <qdir.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include <fcntl.h>
61 #include <time.h>
62 
63 #include "private/qcore_unix_p.h"
64 
65 #ifdef QT_LINUXBASE
66 
67 /* LSB doesn't standardize these */
68 #define F_NOTIFY 1026
69 #define DN_ACCESS 0x00000001
70 #define DN_MODIFY 0x00000002
71 #define DN_CREATE 0x00000004
72 #define DN_DELETE 0x00000008
73 #define DN_RENAME 0x00000010
74 #define DN_ATTRIB 0x00000020
75 #define DN_MULTISHOT 0x80000000
76 
77 #endif
78 
80 
81 static int qfswd_fileChanged_pipe[2];
82 static void (*qfswd_old_sigio_handler)(int) = 0;
83 static void (*qfswd_old_sigio_action)(int, siginfo_t *, void *) = 0;
84 static void qfswd_sigio_monitor(int signum, siginfo_t *i, void *v)
85 {
86  qt_safe_write(qfswd_fileChanged_pipe[1], reinterpret_cast<char*>(&i->si_fd), sizeof(int));
87 
91  qfswd_old_sigio_action(signum, i, v);
92 }
93 
95 {
97 public:
99  virtual ~QDnotifySignalThread();
100 
101  void startNotify();
102 
103  virtual void run();
104 
105 signals:
106  void fdChanged(int);
107 
108 protected:
109  virtual bool event(QEvent *);
110 
111 private slots:
112  void readFromDnotify();
113 
114 private:
117  bool isExecing;
118 };
119 
121 
123 : isExecing(false)
124 {
125  moveToThread(this);
126 
128 
129  struct sigaction oldAction;
130  struct sigaction action;
131  memset(&action, 0, sizeof(action));
132  action.sa_sigaction = qfswd_sigio_monitor;
133  action.sa_flags = SA_SIGINFO;
134  ::sigaction(SIGIO, &action, &oldAction);
135  if (!(oldAction.sa_flags & SA_SIGINFO))
136  qfswd_old_sigio_handler = oldAction.sa_handler;
137  else
138  qfswd_old_sigio_action = oldAction.sa_sigaction;
139 }
140 
142 {
143  if(isRunning()) {
144  quit();
145  QThread::wait();
146  }
147 }
148 
150 {
151  if(e->type() == QEvent::User) {
152  QMutexLocker locker(&mutex);
153  isExecing = true;
154  wait.wakeAll();
155  return true;
156  } else {
157  return QThread::event(e);
158  }
159 }
160 
162 {
163  // Note: All this fancy waiting for the thread to enter its event
164  // loop is to avoid nasty messages at app shutdown when the
165  // QDnotifySignalThread singleton is deleted
166  start();
167  mutex.lock();
168  while(!isExecing)
169  wait.wait(&mutex);
170  mutex.unlock();
171 }
172 
174 {
176  connect(&sn, SIGNAL(activated(int)), SLOT(readFromDnotify()));
177 
179  (void) exec();
180 }
181 
183 {
184  int fd;
185  int readrv = qt_safe_read(qfswd_fileChanged_pipe[0], reinterpret_cast<char*>(&fd), sizeof(int));
186  // Only expect EAGAIN or EINTR. Other errors are assumed to be impossible.
187  if(readrv != -1) {
188  Q_ASSERT(readrv == sizeof(int));
189  Q_UNUSED(readrv);
190 
191  if(0 == fd)
192  quit();
193  else
194  emit fdChanged(fd);
195  }
196 }
197 
199 {
200  QObject::connect(dnotifySignal(), SIGNAL(fdChanged(int)),
201  this, SLOT(refresh(int)), Qt::DirectConnection);
202 }
203 
205 {
206  QMutexLocker locker(&mutex);
207 
208  for(QHash<int, Directory>::ConstIterator iter = fdToDirectory.constBegin();
209  iter != fdToDirectory.constEnd();
210  ++iter) {
211  qt_safe_close(iter->fd);
212  if(iter->parentFd)
213  qt_safe_close(iter->parentFd);
214  }
215 }
216 
218 {
219  return new QDnotifyFileSystemWatcherEngine();
220 }
221 
223 {
224  qFatal("QDnotifyFileSystemWatcherEngine thread should not be run");
225 }
226 
228 {
229  QMutexLocker locker(&mutex);
230 
231  QStringList p = paths;
232  QMutableListIterator<QString> it(p);
233 
234  while (it.hasNext()) {
235  QString path = it.next();
236 
237  QFileInfo fi(path);
238 
239  if(!fi.exists()) {
240  continue;
241  }
242 
243  bool isDir = fi.isDir();
244 
245  if (isDir && directories->contains(path)) {
246  continue; // Skip monitored directories
247  } else if(!isDir && files->contains(path)) {
248  continue; // Skip monitored files
249  }
250 
251  if(!isDir)
252  path = fi.canonicalPath();
253 
254  // Locate the directory entry (creating if needed)
255  int fd = pathToFD[path];
256 
257  if(fd == 0) {
258 
259  QT_DIR *d = QT_OPENDIR(path.toUtf8().constData());
260  if(!d) continue; // Could not open directory
261  QT_DIR *parent = 0;
262 
263  QDir parentDir(path);
264  if(!parentDir.isRoot()) {
265  parentDir.cdUp();
266  parent = QT_OPENDIR(parentDir.path().toUtf8().constData());
267  if(!parent) {
268  QT_CLOSEDIR(d);
269  continue;
270  }
271  }
272 
273  fd = qt_safe_dup(::dirfd(d));
274  int parentFd = parent ? qt_safe_dup(::dirfd(parent)) : 0;
275 
276  QT_CLOSEDIR(d);
277  if(parent) QT_CLOSEDIR(parent);
278 
279  Q_ASSERT(fd);
280  if(::fcntl(fd, F_SETSIG, SIGIO) ||
281  ::fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_DELETE |
282  DN_RENAME | DN_ATTRIB | DN_MULTISHOT) ||
283  (parent && ::fcntl(parentFd, F_SETSIG, SIGIO)) ||
284  (parent && ::fcntl(parentFd, F_NOTIFY, DN_DELETE | DN_RENAME |
285  DN_MULTISHOT))) {
286  continue; // Could not set appropriate flags
287  }
288 
289  Directory dir;
290  dir.path = path;
291  dir.fd = fd;
292  dir.parentFd = parentFd;
293 
294  fdToDirectory.insert(fd, dir);
295  pathToFD.insert(path, fd);
296  if(parentFd)
297  parentToFD.insert(parentFd, fd);
298  }
299 
300  Directory &directory = fdToDirectory[fd];
301 
302  if(isDir) {
303  directory.isMonitored = true;
304  } else {
305  Directory::File file;
306  file.path = fi.filePath();
307  file.lastWrite = fi.lastModified();
308  directory.files.append(file);
309  pathToFD.insert(fi.filePath(), fd);
310  }
311 
312  it.remove();
313 
314  if(isDir) {
315  directories->append(path);
316  } else {
317  files->append(fi.filePath());
318  }
319  }
320 
321  dnotifySignal()->startNotify();
322 
323  return p;
324 }
325 
327 {
328  QMutexLocker locker(&mutex);
329 
330  QStringList p = paths;
331  QMutableListIterator<QString> it(p);
332  while (it.hasNext()) {
333 
334  QString path = it.next();
335  int fd = pathToFD.take(path);
336 
337  if(!fd)
338  continue;
339 
340  Directory &directory = fdToDirectory[fd];
341  bool isDir = false;
342  if(directory.path == path) {
343  isDir = true;
344  directory.isMonitored = false;
345  } else {
346  for(int ii = 0; ii < directory.files.count(); ++ii) {
347  if(directory.files.at(ii).path == path) {
348  directory.files.removeAt(ii);
349  break;
350  }
351  }
352  }
353 
354  if(!directory.isMonitored && directory.files.isEmpty()) {
355  // No longer needed
356  qt_safe_close(directory.fd);
357  pathToFD.remove(directory.path);
358  fdToDirectory.remove(fd);
359  }
360 
361  if(isDir) {
362  directories->removeAll(path);
363  } else {
364  files->removeAll(path);
365  }
366 
367  it.remove();
368  }
369 
370  return p;
371 }
372 
374 {
375  QMutexLocker locker(&mutex);
376 
377  bool wasParent = false;
378  QHash<int, Directory>::Iterator iter = fdToDirectory.find(fd);
379  if(iter == fdToDirectory.end()) {
380  QHash<int, int>::Iterator pIter = parentToFD.find(fd);
381  if(pIter == parentToFD.end())
382  return;
383 
384  iter = fdToDirectory.find(*pIter);
385  if (iter == fdToDirectory.end())
386  return;
387  wasParent = true;
388  }
389 
390  Directory &directory = *iter;
391 
392  if(!wasParent) {
393  for(int ii = 0; ii < directory.files.count(); ++ii) {
394  Directory::File &file = directory.files[ii];
395  if(file.updateInfo()) {
396  // Emit signal
397  QString filePath = file.path;
398  bool removed = !QFileInfo(filePath).exists();
399 
400  if(removed) {
401  directory.files.removeAt(ii);
402  --ii;
403  }
404 
405  emit fileChanged(filePath, removed);
406  }
407  }
408  }
409 
410  if(directory.isMonitored) {
411  // Emit signal
412  bool removed = !QFileInfo(directory.path).exists();
413  QString path = directory.path;
414 
415  if(removed)
416  directory.isMonitored = false;
417 
418  emit directoryChanged(path, removed);
419  }
420 
421  if(!directory.isMonitored && directory.files.isEmpty()) {
422  qt_safe_close(directory.fd);
423  if(directory.parentFd) {
424  qt_safe_close(directory.parentFd);
425  parentToFD.remove(directory.parentFd);
426  }
427  fdToDirectory.erase(iter);
428  }
429 }
430 
432 {
433 }
434 
436 {
437  QFileInfo fi(path);
438  QDateTime nLastWrite = fi.lastModified();
439  uint nOwnerId = fi.ownerId();
440  uint nGroupId = fi.groupId();
441  QFile::Permissions nPermissions = fi.permissions();
442 
443  if(nLastWrite != lastWrite ||
444  nOwnerId != ownerId ||
445  nGroupId != groupId ||
446  nPermissions != permissions) {
447  ownerId = nOwnerId;
448  groupId = nGroupId;
449  permissions = nPermissions;
450  lastWrite = nLastWrite;
451  return true;
452  } else {
453  return false;
454  }
455 }
456 
458 
459 #include "qfilesystemwatcher_dnotify.moc"
460 
461 #endif // QT_NO_FILESYSTEMWATCHER
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:58
double d
Definition: qnumeric_p.h:62
static void(* qfswd_old_sigio_handler)(int)=0
The QHash::const_iterator class provides an STL-style const iterator for QHash and QMultiHash...
Definition: qhash.h:395
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
void lock()
Locks the mutex.
Definition: qmutex.cpp:151
The QMutex class provides access serialization between threads.
Definition: qmutex.h:60
#define it(className, varName)
static void postEvent(QObject *receiver, QEvent *event)
Adds the event event, with the object receiver as the receiver of the event, to an event queue and re...
QByteArray toUtf8() const Q_REQUIRED_RESULT
Returns a UTF-8 representation of the string as a QByteArray.
Definition: qstring.cpp:4074
#define SLOT(a)
Definition: qobjectdefs.h:226
virtual void run()
The starting point for the thread.
static void(* qfswd_old_sigio_action)(int, siginfo_t *, void *)=0
bool exists() const
Returns true if the file exists; otherwise returns false.
Definition: qfileinfo.cpp:675
static int qt_safe_close(int fd)
Definition: qcore_unix_p.h:297
The QString class provides a Unicode character string.
Definition: qstring.h:83
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
QFile::Permissions permissions() const
Returns the complete OR-ed together combination of QFile::Permissions for the file.
Definition: qfileinfo.cpp:1228
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
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
virtual bool event(QEvent *)
This virtual function receives events to an object and should return true if the event e was recogniz...
void quit()
Tells the thread&#39;s event loop to exit with return code 0 (success).
Definition: qthread.cpp:614
The QSocketNotifier class provides support for monitoring activity on a file descriptor.
#define SIGNAL(a)
Definition: qobjectdefs.h:227
bool cdUp()
Changes directory by moving one directory up from the QDir&#39;s current directory.
Definition: qdir.cpp:937
uint ownerId() const
Returns the id of the owner of the file.
Definition: qfileinfo.cpp:1144
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
#define Q_GLOBAL_STATIC(TYPE, NAME)
Declares a global static variable with the given type and name.
Definition: qglobal.h:1968
#define emit
Definition: qobjectdefs.h:76
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
unsigned int uint
Definition: qglobal.h:996
static int qt_safe_pipe(int pipefd[2], int flags=0)
Definition: qcore_unix_p.h:191
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 unlock()
Unlocks the mutex.
Definition: qmutex.cpp:296
QString canonicalPath() const
Returns the file&#39;s path canonical path (excluding the file name), i.e.
Definition: qfileinfo.cpp:598
#define Q_OBJECT
Definition: qobjectdefs.h:157
bool wait(QMutex *mutex, unsigned long time=ULONG_MAX)
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
static QDnotifyFileSystemWatcherEngine * create()
Q_CORE_EXPORT void qFatal(const char *,...)
int fcntl(int, int,...)
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
The QDateTime class provides date and time functions.
Definition: qdatetime.h:216
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the hash...
Definition: qhash.h:467
static QCoreApplication * instance()
Returns a pointer to the application&#39;s QCoreApplication (or QApplication) instance.
void start(Priority=InheritPriority)
Begins execution of the thread by calling run().
int sigaction(int, const struct sigaction *, struct sigaction *)
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
bool wait(unsigned long time=ULONG_MAX)
Blocks the thread until either of these conditions is met:
int exec()
Enters the event loop and waits until exit() is called, returning the value that was passed to exit()...
Definition: qthread.cpp:551
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
The QHash::iterator class provides an STL-style non-const iterator for QHash and QMultiHash.
Definition: qhash.h:330
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
uint groupId() const
Returns the id of the group the file belongs to.
Definition: qfileinfo.cpp:1183
static void qfswd_sigio_monitor(int signum, siginfo_t *i, void *v)
QString path() const
Returns the path.
Definition: qdir.cpp:605
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition: qhash.h:865
#define slots
Definition: qobjectdefs.h:68
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:60
#define signals
Definition: qobjectdefs.h:69
QString filePath() const
Returns the file name, including the path (which may be absolute or relative).
Definition: qfileinfo.cpp:707
bool isRoot() const
Returns true if the directory is the root directory; otherwise returns false.
Definition: qdir.cpp:1577
The QEvent class is the base class of all event classes.
Definition: qcoreevent.h:56
Type type() const
Returns the event type.
Definition: qcoreevent.h:303
The QThread class provides a platform-independent way to manage threads.
Definition: qthread.h:59
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
bool isRunning() const
Returns true if the thread is running; otherwise returns false.
Definition: qthread.cpp:494
void run()
The starting point for the thread.
static int qfswd_fileChanged_pipe[2]
static qint64 qt_safe_write(int fd, const void *data, qint64 len)
Definition: qcore_unix_p.h:282
static qint64 qt_safe_read(int fd, void *data, qint64 maxlen)
Definition: qcore_unix_p.h:273
static int qt_safe_dup(int oldfd, int atleast=0, int flags=FD_CLOEXEC)
Definition: qcore_unix_p.h:227
int removeAll(const T &t)
Removes all occurrences of value in the list and returns the number of entries removed.
Definition: qlist.h:770
QDateTime lastModified() const
Returns the date and time when the file was last modified.
Definition: qfileinfo.cpp:1296