Diffstat (limited to 'libopie2/opiecore/linux/ofilenotify.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | libopie2/opiecore/linux/ofilenotify.cpp | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/libopie2/opiecore/linux/ofilenotify.cpp b/libopie2/opiecore/linux/ofilenotify.cpp new file mode 100644 index 0000000..36ec6bf --- a/dev/null +++ b/libopie2/opiecore/linux/ofilenotify.cpp | |||
@@ -0,0 +1,406 @@ | |||
1 | /* | ||
2 | This file is part of the Opie Project | ||
3 | =. Copyright (C) 2004-2005 Michael 'Mickey' Lauer <mickey@Vanille.de> | ||
4 | .=l. Copyright (C) The Opie Team <opie-devel@handhelds.org> | ||
5 | .>+-= | ||
6 | _;:, .> :=|. This program is free software; you can | ||
7 | .> <`_, > . <= redistribute it and/or modify it under | ||
8 | :`=1 )Y*s>-.-- : the terms of the GNU Library General Public | ||
9 | .="- .-=="i, .._ License as published by the Free Software | ||
10 | - . .-<_> .<> Foundation; version 2 of the License. | ||
11 | ._= =} : | ||
12 | .%`+i> _;_. | ||
13 | .i_,=:_. -<s. This program is distributed in the hope that | ||
14 | + . -:. = it will be useful, but WITHOUT ANY WARRANTY; | ||
15 | : .. .:, . . . without even the implied warranty of | ||
16 | =_ + =;=|` MERCHANTABILITY or FITNESS FOR A | ||
17 | _.=:. : :=>`: PARTICULAR PURPOSE. See the GNU | ||
18 | ..}^=.= = ; Library General Public License for more | ||
19 | ++= -. .` .: details. | ||
20 | : = ...= . :.=- | ||
21 | -. .:....=;==+<; You should have received a copy of the GNU | ||
22 | -_. . . )=. = Library General Public License along with | ||
23 | -- :-=` this library; see the file COPYING.LIB. | ||
24 | If not, write to the Free Software Foundation, | ||
25 | Inc., 59 Temple Place - Suite 330, | ||
26 | Boston, MA 02111-1307, USA. | ||
27 | */ | ||
28 | |||
29 | #include "ofilenotify.h" | ||
30 | using namespace Opie::Core; | ||
31 | |||
32 | /* OPIE */ | ||
33 | |||
34 | /* QT */ | ||
35 | #include <qobject.h> | ||
36 | #include <qsocketnotifier.h> | ||
37 | #include <qsignal.h> | ||
38 | #include <qintdict.h> | ||
39 | #include <qdir.h> | ||
40 | |||
41 | /* STD */ | ||
42 | #include <sys/types.h> | ||
43 | #include <sys/stat.h> | ||
44 | #include <sys/ioctl.h> | ||
45 | #include <fcntl.h> | ||
46 | #include <assert.h> | ||
47 | #include <string.h> | ||
48 | #include <errno.h> | ||
49 | #include <unistd.h> | ||
50 | |||
51 | static QIntDict<OFileNotification> notification_list; | ||
52 | |||
53 | QSocketNotifier* OFileNotification::_sn; | ||
54 | int OFileNotification::_fd = -1; | ||
55 | |||
56 | #define INOTIFY_DEVICE "/dev/inotify" | ||
57 | |||
58 | namespace Opie { | ||
59 | namespace Core { | ||
60 | |||
61 | //================================================================================================= | ||
62 | // OFile | ||
63 | //================================================================================================= | ||
64 | |||
65 | OFile::OFile() : QObject( 0, 0 ), QFile() | ||
66 | { | ||
67 | qDebug( "OFile()" ); | ||
68 | } | ||
69 | |||
70 | OFile::OFile( const QString& name ) : QObject( 0, 0 ), QFile( name ) | ||
71 | { | ||
72 | qDebug( "OFile()" ); | ||
73 | } | ||
74 | |||
75 | OFile::~OFile() | ||
76 | { | ||
77 | qDebug( "~OFile()" ); | ||
78 | } | ||
79 | |||
80 | void OFile::connectNotify( const char *signal ) | ||
81 | { | ||
82 | QString s = normalizeSignalSlot( signal+1 ); | ||
83 | qDebug( "OFile::connectNotify() signal = '%s'", (const char*) s ); | ||
84 | |||
85 | if ( s.startsWith( "accessed" ) ) | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | |||
91 | |||
92 | |||
93 | QObject::connectNotify( signal ); | ||
94 | |||
95 | /* | ||
96 | void accessed( const QString& ); | ||
97 | void modified( const QString& ); | ||
98 | void attributed( const QString& ); | ||
99 | void closed( const QString&, bool ); | ||
100 | void opened( const QString& ); | ||
101 | void deleted( const QString& ); | ||
102 | void unmounted( const QString& ); | ||
103 | */ | ||
104 | |||
105 | } | ||
106 | |||
107 | void OFile::disconnectNotify( const char* signal ) | ||
108 | { | ||
109 | qDebug( "OFile::disconnectNotify() signal = '%s'", signal ); | ||
110 | QObject::disconnectNotify( signal ); | ||
111 | } | ||
112 | |||
113 | int OFile::startWatch( int mode ) | ||
114 | { | ||
115 | } | ||
116 | |||
117 | //================================================================================================= | ||
118 | // OFileNotificationEvent | ||
119 | //================================================================================================= | ||
120 | OFileNotificationEvent::OFileNotificationEvent( OFileNotification* parent, int wd, unsigned int mask, unsigned int cookie, const QString& name ) | ||
121 | :_parent( parent ), _wd( wd ), _mask( mask ), _cookie( cookie ), _name( name ) | ||
122 | { | ||
123 | qDebug( "OFileNotificationEvent()" ); | ||
124 | } | ||
125 | |||
126 | |||
127 | OFileNotificationEvent::~OFileNotificationEvent() | ||
128 | { | ||
129 | qDebug( "~OFileNotificationEvent()" ); | ||
130 | } | ||
131 | |||
132 | //================================================================================================= | ||
133 | // OFileNotification | ||
134 | //================================================================================================= | ||
135 | OFileNotification::OFileNotification( QObject* parent, const char* name ) | ||
136 | :QObject( parent, name ), _active( false ), _multi( true ) | ||
137 | { | ||
138 | qDebug( "OFileNotification::OFileNotification()" ); | ||
139 | } | ||
140 | |||
141 | |||
142 | OFileNotification::~OFileNotification() | ||
143 | { | ||
144 | stop(); | ||
145 | qDebug( "OFileNotification::~OFileNotification()" ); | ||
146 | } | ||
147 | |||
148 | |||
149 | bool OFileNotification::isActive() const | ||
150 | { | ||
151 | return _active; | ||
152 | } | ||
153 | |||
154 | |||
155 | int OFileNotification::watch( const QString& path, bool sshot, OFileNotificationType type ) | ||
156 | { | ||
157 | // check if path exists and is a regular file | ||
158 | struct stat s; | ||
159 | if ( ::stat( (const char*) path, &s ) == -1 ) | ||
160 | { | ||
161 | qWarning( "OFileNotification::watch(): Can't watch '%s': %s.", (const char*) path, strerror( errno ) ); | ||
162 | return -1; | ||
163 | } | ||
164 | if ( !S_ISREG( s.st_mode ) ) | ||
165 | { | ||
166 | qWarning( "OFileNotification::watch(): Can't watch '%s': %s.", (const char*) path, "not a regular file" ); | ||
167 | return -1; | ||
168 | } | ||
169 | |||
170 | return startWatching( path, sshot, type ); | ||
171 | } | ||
172 | |||
173 | |||
174 | int OFileNotification::startWatching( const QString& path, bool sshot, OFileNotificationType type ) | ||
175 | { | ||
176 | if ( notification_list.isEmpty() ) | ||
177 | { | ||
178 | OFileNotification::registerEventHandler(); | ||
179 | } | ||
180 | |||
181 | struct inotify_watch_request iwr; | ||
182 | ::memset( &iwr, 0, sizeof iwr ); | ||
183 | iwr.name = const_cast<char*>( (const char*) path ); | ||
184 | iwr.mask = type; | ||
185 | |||
186 | _wd = ::ioctl( OFileNotification::_fd, INOTIFY_WATCH, &iwr ); | ||
187 | |||
188 | if ( _wd < 0 ) | ||
189 | { | ||
190 | qWarning( "OFileNotification::watch(): inotify can't watch '%s': %s.", (const char*) path, strerror( errno ) ); | ||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | notification_list.insert( _wd, this ); | ||
195 | _path = path; | ||
196 | _multi = !sshot; | ||
197 | _type = type; | ||
198 | _active = true; | ||
199 | qDebug( "OFileNotification::watch(): watching '%s' [wd=%d].", (const char*) path, _wd ); | ||
200 | return _wd; | ||
201 | } | ||
202 | |||
203 | |||
204 | void OFileNotification::stop() | ||
205 | { | ||
206 | notification_list.remove( _wd ); | ||
207 | _path = QString::null; | ||
208 | _wd = 0; | ||
209 | _active = false; | ||
210 | if ( notification_list.isEmpty() ) | ||
211 | { | ||
212 | OFileNotification::unregisterEventHandler(); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | |||
217 | OFileNotificationType OFileNotification::type() const | ||
218 | { | ||
219 | return _type; | ||
220 | } | ||
221 | |||
222 | |||
223 | QString OFileNotification::path() const | ||
224 | { | ||
225 | return _path; | ||
226 | } | ||
227 | |||
228 | |||
229 | bool OFileNotification::activate( const OFileNotificationEvent* e ) | ||
230 | { | ||
231 | qDebug( "OFileNotification::activate(): e = ( %s, %d, 0x%08x, %d, %s )", (const char*) _path, e->descriptor(), e->mask(), e->cookie(), (const char*) e->name() ); | ||
232 | |||
233 | // dumb signal | ||
234 | _signal.activate(); | ||
235 | |||
236 | // generic signal | ||
237 | emit triggered( _path, e->mask(), e->name() ); | ||
238 | |||
239 | // specialized signals | ||
240 | switch ( e->mask() ) | ||
241 | { | ||
242 | case Access: emit accessed( _path ); break; | ||
243 | case Modify: emit modified( _path ); break; | ||
244 | case Attrib: emit attributed( _path); break; | ||
245 | case CloseWrite: emit closed( _path, true ); break; | ||
246 | case CloseNoWrite: emit closed( _path, false ); break; | ||
247 | case Open: emit opened( _path ); break; | ||
248 | case MovedFrom: emit movedFrom( _path, e->name() ); break; | ||
249 | case MovedTo: emit movedTo( _path, e->name() ); break; | ||
250 | case DeleteSubdir: emit deletedSubdir( _path, e->name() ); break; | ||
251 | case DeleteFile: emit deletedFile( _path, e->name() ); break; | ||
252 | case CreateSubdir: emit createdSubdir( _path, e->name() ); break; | ||
253 | case CreateFile: emit createdFile( _path, e->name() ); break; | ||
254 | case DeleteSelf: emit deleted( _path ); break; | ||
255 | case Unmount: emit unmounted( _path ); break; | ||
256 | default: assert( 0 ); | ||
257 | } | ||
258 | |||
259 | if ( !_multi ) stop(); | ||
260 | |||
261 | return true; | ||
262 | } | ||
263 | |||
264 | |||
265 | bool OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) | ||
266 | { | ||
267 | OFileNotification* ofn = new OFileNotification(); | ||
268 | ofn->_signal.connect( receiver, member ); | ||
269 | return ofn->watch( path, true, type ) != -1; | ||
270 | } | ||
271 | |||
272 | |||
273 | void OFileNotification::inotifyEventHandler() | ||
274 | { | ||
275 | qDebug( "OFileNotification::inotifyEventHandler(): reached." ); | ||
276 | |||
277 | char buffer[16384]; | ||
278 | ssize_t buffer_i; | ||
279 | struct inotify_event *pevent, *event; | ||
280 | ssize_t r; | ||
281 | size_t event_size; | ||
282 | int count = 0; | ||
283 | |||
284 | r = ::read(_fd, buffer, 16384); | ||
285 | |||
286 | if ( r <= 0 ) | ||
287 | return; | ||
288 | |||
289 | buffer_i = 0; | ||
290 | while ( buffer_i < r ) | ||
291 | { | ||
292 | pevent = (struct inotify_event *)&buffer[buffer_i]; | ||
293 | event_size = sizeof(struct inotify_event) + pevent->len; | ||
294 | OFileNotificationEvent* e = new OFileNotificationEvent( notification_list[ pevent->wd ], pevent->wd, pevent->mask, | ||
295 | pevent->cookie, pevent->len ? pevent->name : 0 ); | ||
296 | e->activate(); | ||
297 | buffer_i += event_size; | ||
298 | count++; | ||
299 | } | ||
300 | |||
301 | qDebug( "OFileNotification::inotifyEventHandler(): processed %d events", count ); | ||
302 | } | ||
303 | |||
304 | |||
305 | bool OFileNotification::registerEventHandler() | ||
306 | { | ||
307 | OFileNotification::_fd = ::open( INOTIFY_DEVICE, O_RDONLY ); | ||
308 | if ( OFileNotification::_fd < 0 ) | ||
309 | { | ||
310 | qWarning( "OFileNotification::registerEventHandler(): couldn't register event handler: %s", strerror( errno ) ); | ||
311 | return false; | ||
312 | } | ||
313 | |||
314 | OFileNotification::_sn = new QSocketNotifier( _fd, QSocketNotifier::Read, this, "inotify event" ); | ||
315 | connect( OFileNotification::_sn, SIGNAL( activated(int) ), this, SLOT( inotifyEventHandler() ) ); | ||
316 | |||
317 | qDebug( "OFileNotification::registerEventHandler(): done" ); | ||
318 | return true; | ||
319 | } | ||
320 | |||
321 | |||
322 | void OFileNotification::unregisterEventHandler() | ||
323 | { | ||
324 | if ( _sn ) delete _sn; | ||
325 | if ( OFileNotification::_fd ) | ||
326 | ::close( OFileNotification::_fd ); | ||
327 | qDebug( "OFileNotification::unregisterEventHandler(): done" ); | ||
328 | } | ||
329 | |||
330 | //================================================================================================= | ||
331 | // ODirNotification | ||
332 | //================================================================================================= | ||
333 | ODirNotification::ODirNotification( QObject* parent, const char* name ) | ||
334 | :QObject( parent, name ) | ||
335 | { | ||
336 | qDebug( "ODirNotification::ODirNotification()" ); | ||
337 | } | ||
338 | |||
339 | |||
340 | ODirNotification::~ODirNotification() | ||
341 | { | ||
342 | qDebug( "ODirNotification::~ODirNotification()" ); | ||
343 | } | ||
344 | |||
345 | |||
346 | int ODirNotification::watch( const QString& path, bool sshot, OFileNotificationType type, int recurse ) | ||
347 | { | ||
348 | qDebug( "ODirNotification::watch( %s, %d, 0x%08x, %d )", (const char*) path, sshot, type, recurse ); | ||
349 | |||
350 | if ( recurse == 0 ) | ||
351 | { | ||
352 | OFileNotification* fn = new OFileNotification( this, "ODirNotification delegate" ); | ||
353 | int result = fn->startWatching( path, sshot, type ); | ||
354 | if ( result != -1 ) | ||
355 | { | ||
356 | connect( fn, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ), this, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ) ); | ||
357 | connect( fn, SIGNAL( accessed( const QString& ) ), this, SIGNAL( accessed( const QString& ) ) ); | ||
358 | connect( fn, SIGNAL( modified( const QString& ) ), this, SIGNAL( modified( const QString& ) ) ); | ||
359 | connect( fn, SIGNAL( attributed( const QString& ) ), this, SIGNAL( attributed( const QString& ) ) ); | ||
360 | connect( fn, SIGNAL( closed( const QString&, bool ) ), this, SIGNAL( closed( const QString&, bool ) ) ); | ||
361 | connect( fn, SIGNAL( opened( const QString& ) ), this, SIGNAL( opened( const QString& ) ) ); | ||
362 | connect( fn, SIGNAL( movedTo( const QString&, const QString& ) ), this, SIGNAL( movedTo( const QString&, const QString& ) ) ); | ||
363 | connect( fn, SIGNAL( movedFrom( const QString&, const QString& ) ), this, SIGNAL( movedFrom( const QString&, const QString& ) ) ); | ||
364 | connect( fn, SIGNAL( deletedSubdir( const QString&, const QString& ) ), this, SIGNAL( deletedSubdir( const QString&, const QString& ) ) ); | ||
365 | connect( fn, SIGNAL( deletedFile( const QString&, const QString& ) ), this, SIGNAL( deletedFile( const QString&, const QString& ) ) );; | ||
366 | connect( fn, SIGNAL( createdSubdir( const QString&, const QString& ) ), this, SIGNAL( createdSubdir( const QString&, const QString& ) ) ); | ||
367 | connect( fn, SIGNAL( createdFile( const QString&, const QString& ) ), this, SIGNAL( createdFile( const QString&, const QString& ) ) ); | ||
368 | connect( fn, SIGNAL( deleted( const QString& ) ), this, SIGNAL( deleted( const QString& ) ) ); | ||
369 | connect( fn, SIGNAL( unmounted( const QString& ) ), this, SIGNAL( unmounted( const QString& ) ) ); | ||
370 | } | ||
371 | return result; | ||
372 | } | ||
373 | else | ||
374 | { | ||
375 | |||
376 | return 1; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | |||
381 | // void ODirNotification::subdirCreated( const QString& name ) | ||
382 | |||
383 | |||
384 | /* | ||
385 | Love-Trowbridge recursive directory scanning algorithm: | ||
386 | |||
387 | Step 1. Start at initial directory foo. Add watch. | ||
388 | |||
389 | Step 2. Setup handlers for watch created in Step 1. | ||
390 | Specifically, ensure that a directory created | ||
391 | in foo will result in a handled CREATE_SUBDIR | ||
392 | event. | ||
393 | |||
394 | Step 3. Read the contents of foo. | ||
395 | |||
396 | Step 4. For each subdirectory of foo read in step 3, repeat | ||
397 | step 1. | ||
398 | |||
399 | Step 5. For any CREATE_SUBDIR event on bar, if a watch is | ||
400 | not yet created on bar, repeat step 1 on bar. | ||
401 | */ | ||
402 | |||
403 | |||
404 | } // namespace Ui | ||
405 | |||
406 | } // namespace Opie | ||