summaryrefslogtreecommitdiff
path: root/libopie2/opiecore/linux/ofilenotify.cpp
Unidiff
Diffstat (limited to 'libopie2/opiecore/linux/ofilenotify.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opiecore/linux/ofilenotify.cpp406
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"
30using 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
51static QIntDict<OFileNotification> notification_list;
52
53QSocketNotifier* OFileNotification::_sn;
54int OFileNotification::_fd = -1;
55
56#define INOTIFY_DEVICE "/dev/inotify"
57
58namespace Opie {
59namespace Core {
60
61//=================================================================================================
62// OFile
63//=================================================================================================
64
65OFile::OFile() : QObject( 0, 0 ), QFile()
66{
67 qDebug( "OFile()" );
68}
69
70OFile::OFile( const QString& name ) : QObject( 0, 0 ), QFile( name )
71{
72 qDebug( "OFile()" );
73}
74
75OFile::~OFile()
76{
77 qDebug( "~OFile()" );
78}
79
80void 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
107void OFile::disconnectNotify( const char* signal )
108{
109 qDebug( "OFile::disconnectNotify() signal = '%s'", signal );
110 QObject::disconnectNotify( signal );
111}
112
113int OFile::startWatch( int mode )
114{
115}
116
117//=================================================================================================
118// OFileNotificationEvent
119//=================================================================================================
120OFileNotificationEvent::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
127OFileNotificationEvent::~OFileNotificationEvent()
128{
129 qDebug( "~OFileNotificationEvent()" );
130}
131
132//=================================================================================================
133// OFileNotification
134//=================================================================================================
135OFileNotification::OFileNotification( QObject* parent, const char* name )
136 :QObject( parent, name ), _active( false ), _multi( true )
137{
138 qDebug( "OFileNotification::OFileNotification()" );
139}
140
141
142OFileNotification::~OFileNotification()
143{
144 stop();
145 qDebug( "OFileNotification::~OFileNotification()" );
146}
147
148
149bool OFileNotification::isActive() const
150{
151 return _active;
152}
153
154
155int 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
174int 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
204void 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
217OFileNotificationType OFileNotification::type() const
218{
219 return _type;
220}
221
222
223QString OFileNotification::path() const
224{
225 return _path;
226}
227
228
229bool 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
265bool 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
273void 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
305bool 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
322void 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//=================================================================================================
333ODirNotification::ODirNotification( QObject* parent, const char* name )
334 :QObject( parent, name )
335{
336 qDebug( "ODirNotification::ODirNotification()" );
337}
338
339
340ODirNotification::~ODirNotification()
341{
342 qDebug( "ODirNotification::~ODirNotification()" );
343}
344
345
346int 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