1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtCore module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qfilesystemwatcher.h"
43 #include "qfilesystemwatcher_inotify_p.h"
45 #ifndef QT_NO_FILESYSTEMWATCHER
47 #include "private/qcore_unix_p.h"
51 #include <qfileinfo.h>
52 #include <qsocketnotifier.h>
53 #include <qvarlengtharray.h>
55 #include <sys/syscall.h>
56 #include <sys/ioctl.h>
60 #if defined(QT_NO_INOTIFY)
61 #include <linux/types.h>
64 # define __NR_inotify_init 291
65 # define __NR_inotify_add_watch 292
66 # define __NR_inotify_rm_watch 293
67 # define __NR_inotify_init1 332
68 #elif defined(__x86_64__)
69 # define __NR_inotify_init 253
70 # define __NR_inotify_add_watch 254
71 # define __NR_inotify_rm_watch 255
72 # define __NR_inotify_init1 294
73 #elif defined(__powerpc__) || defined(__powerpc64__)
74 # define __NR_inotify_init 275
75 # define __NR_inotify_add_watch 276
76 # define __NR_inotify_rm_watch 277
77 # define __NR_inotify_init1 318
78 #elif defined (__ia64__)
79 # define __NR_inotify_init 1277
80 # define __NR_inotify_add_watch 1278
81 # define __NR_inotify_rm_watch 1279
82 # define __NR_inotify_init1 1318
83 #elif defined (__s390__) || defined (__s390x__)
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 324
88 #elif defined (__alpha__)
89 # define __NR_inotify_init 444
90 # define __NR_inotify_add_watch 445
91 # define __NR_inotify_rm_watch 446
92 // no inotify_init1 for the Alpha
93 #elif defined (__sparc__) || defined (__sparc64__)
94 # define __NR_inotify_init 151
95 # define __NR_inotify_add_watch 152
96 # define __NR_inotify_rm_watch 156
97 # define __NR_inotify_init1 322
98 #elif defined (__arm__)
99 # define __NR_inotify_init 316
100 # define __NR_inotify_add_watch 317
101 # define __NR_inotify_rm_watch 318
102 # define __NR_inotify_init1 360
103 #elif defined (__sh__)
104 # define __NR_inotify_init 290
105 # define __NR_inotify_add_watch 291
106 # define __NR_inotify_rm_watch 292
107 # define __NR_inotify_init1 332
108 #elif defined (__sh64__)
109 # define __NR_inotify_init 318
110 # define __NR_inotify_add_watch 319
111 # define __NR_inotify_rm_watch 320
112 # define __NR_inotify_init1 360
113 #elif defined (__mips__)
114 # define __NR_inotify_init 284
115 # define __NR_inotify_add_watch 285
116 # define __NR_inotify_rm_watch 286
117 # define __NR_inotify_init1 329
118 #elif defined (__hppa__)
119 # define __NR_inotify_init 269
120 # define __NR_inotify_add_watch 270
121 # define __NR_inotify_rm_watch 271
122 # define __NR_inotify_init1 314
123 #elif defined (__avr32__)
124 # define __NR_inotify_init 240
125 # define __NR_inotify_add_watch 241
126 # define __NR_inotify_rm_watch 242
127 // no inotify_init1 for AVR32
128 #elif defined (__mc68000__)
129 # define __NR_inotify_init 284
130 # define __NR_inotify_add_watch 285
131 # define __NR_inotify_rm_watch 286
132 # define __NR_inotify_init1 328
134 # error "This architecture is not supported. Please talk to qt-bugs@trolltech.com"
137 #if !defined(IN_CLOEXEC) && defined(O_CLOEXEC) && defined(__NR_inotify_init1)
138 # define IN_CLOEXEC O_CLOEXEC
144 // ### the LSB doesn't standardize syscall, need to wait until glib2.4 is standardized
145 static inline int syscall(...) { return -1; }
148 static inline int inotify_init()
150 return syscall(__NR_inotify_init
);
153 static inline int inotify_add_watch(int fd
, const char *name
, __u32 mask
)
155 return syscall(__NR_inotify_add_watch
, fd
, name
, mask
);
158 static inline int inotify_rm_watch(int fd
, __u32 wd
)
160 return syscall(__NR_inotify_rm_watch
, fd
, wd
);
164 static inline int inotify_init1(int flags
)
166 return syscall(__NR_inotify_init1
, flags
);
170 // the following struct and values are documented in linux/inotify.h
173 struct inotify_event
{
181 #define IN_ACCESS 0x00000001
182 #define IN_MODIFY 0x00000002
183 #define IN_ATTRIB 0x00000004
184 #define IN_CLOSE_WRITE 0x00000008
185 #define IN_CLOSE_NOWRITE 0x00000010
186 #define IN_OPEN 0x00000020
187 #define IN_MOVED_FROM 0x00000040
188 #define IN_MOVED_TO 0x00000080
189 #define IN_CREATE 0x00000100
190 #define IN_DELETE 0x00000200
191 #define IN_DELETE_SELF 0x00000400
192 #define IN_MOVE_SELF 0x00000800
193 #define IN_UNMOUNT 0x00002000
194 #define IN_Q_OVERFLOW 0x00004000
195 #define IN_IGNORED 0x00008000
197 #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
198 #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
203 // --------- inotify.h end ----------
205 #else /* QT_NO_INOTIFY */
207 #include <sys/inotify.h>
213 QInotifyFileSystemWatcherEngine
*QInotifyFileSystemWatcherEngine::create()
215 register int fd
= -1;
217 fd
= inotify_init1(IN_CLOEXEC
);
223 ::fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
225 return new QInotifyFileSystemWatcherEngine(fd
);
228 QInotifyFileSystemWatcherEngine::QInotifyFileSystemWatcherEngine(int fd
)
231 fcntl(inotifyFd
, F_SETFD
, FD_CLOEXEC
);
236 QInotifyFileSystemWatcherEngine::~QInotifyFileSystemWatcherEngine()
238 foreach (int id
, pathToID
.values())
239 inotify_rm_watch(inotifyFd
, id
< 0 ? -id
: id
);
244 void QInotifyFileSystemWatcherEngine::run()
246 QSocketNotifier
sn(inotifyFd
, QSocketNotifier::Read
, this);
247 connect(&sn
, SIGNAL(activated(int)), SLOT(readFromInotify()));
251 QStringList
QInotifyFileSystemWatcherEngine::addPaths(const QStringList
&paths
,
253 QStringList
*directories
)
255 QMutexLocker
locker(&mutex
);
257 QStringList p
= paths
;
258 QMutableListIterator
<QString
> it(p
);
259 while (it
.hasNext()) {
260 QString path
= it
.next();
262 bool isDir
= fi
.isDir();
264 if (directories
->contains(path
))
267 if (files
->contains(path
))
271 int wd
= inotify_add_watch(inotifyFd
,
272 QFile::encodeName(path
),
289 perror("QInotifyFileSystemWatcherEngine::addPaths: inotify_add_watch failed");
295 int id
= isDir
? -wd
: wd
;
297 directories
->append(path
);
302 pathToID
.insert(path
, id
);
303 idToPath
.insert(id
, path
);
311 QStringList
QInotifyFileSystemWatcherEngine::removePaths(const QStringList
&paths
,
313 QStringList
*directories
)
315 QMutexLocker
locker(&mutex
);
317 QStringList p
= paths
;
318 QMutableListIterator
<QString
> it(p
);
319 while (it
.hasNext()) {
320 QString path
= it
.next();
321 int id
= pathToID
.take(path
);
322 QString x
= idToPath
.take(id
);
323 if (x
.isEmpty() || x
!= path
)
326 int wd
= id
< 0 ? -id
: id
;
327 // qDebug() << "removing watch for path" << path << "wd" << wd;
328 inotify_rm_watch(inotifyFd
, wd
);
332 directories
->removeAll(path
);
334 files
->removeAll(path
);
341 void QInotifyFileSystemWatcherEngine::stop()
343 QMetaObject::invokeMethod(this, "quit");
346 void QInotifyFileSystemWatcherEngine::readFromInotify()
348 QMutexLocker
locker(&mutex
);
350 // qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify";
353 ioctl(inotifyFd
, FIONREAD
, (char *) &buffSize
);
354 QVarLengthArray
<char, 4096> buffer(buffSize
);
355 buffSize
= read(inotifyFd
, buffer
.data(), buffSize
);
356 const char *at
= buffer
.data();
357 const char * const end
= at
+ buffSize
;
359 QMap
<int, inotify_event
> eventForId
;
361 const inotify_event
*event
= reinterpret_cast<const inotify_event
*>(at
);
363 if (eventForId
.contains(event
->wd
))
364 eventForId
[event
->wd
].mask
|= event
->mask
;
366 eventForId
.insert(event
->wd
, *event
);
368 at
+= sizeof(inotify_event
) + event
->len
;
371 QMap
<int, inotify_event
>::const_iterator it
= eventForId
.constBegin();
372 while (it
!= eventForId
.constEnd()) {
373 inotify_event event
= *it
;
376 // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask;
379 QString path
= idToPath
.value(id
);
380 if (path
.isEmpty()) {
381 // perhaps a directory?
383 path
= idToPath
.value(id
);
388 // qDebug() << "event for path" << path;
390 if ((event
.mask
& (IN_DELETE_SELF
| IN_MOVE_SELF
| IN_UNMOUNT
)) != 0) {
391 pathToID
.remove(path
);
393 inotify_rm_watch(inotifyFd
, event
.wd
);
396 emit
directoryChanged(path
, true);
398 emit
fileChanged(path
, true);
401 emit
directoryChanged(path
, false);
403 emit
fileChanged(path
, false);
410 #endif // QT_NO_FILESYSTEMWATCHER