00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "ofilenotify.h"
00030 using namespace Opie::Core;
00031
00032
00033
00034
00035 #include <qobject.h>
00036 #include <qsocketnotifier.h>
00037 #include <qsignal.h>
00038 #include <qintdict.h>
00039 #include <qdir.h>
00040
00041
00042 #include <sys/types.h>
00043 #include <sys/stat.h>
00044 #include <sys/ioctl.h>
00045 #include <fcntl.h>
00046 #include <assert.h>
00047 #include <string.h>
00048 #include <errno.h>
00049 #include <unistd.h>
00050
00051 static QIntDict<OFileNotification> notification_list;
00052
00053 QSocketNotifier* OFileNotification::_sn;
00054 int OFileNotification::_fd = -1;
00055
00056 #define INOTIFY_DEVICE "/dev/inotify"
00057
00058 namespace Opie {
00059 namespace Core {
00060
00061
00062
00063
00064
00065 OFile::OFile() : QObject( 0, 0 ), QFile()
00066 {
00067 qDebug( "OFile()" );
00068 }
00069
00070 OFile::OFile( const QString& name ) : QObject( 0, 0 ), QFile( name )
00071 {
00072 qDebug( "OFile()" );
00073 }
00074
00075 OFile::~OFile()
00076 {
00077 qDebug( "~OFile()" );
00078 }
00079
00080 void OFile::connectNotify( const char *signal )
00081 {
00082 QString s = normalizeSignalSlot( signal+1 );
00083 qDebug( "OFile::connectNotify() signal = '%s'", (const char*) s );
00084
00085 if ( s.startsWith( "accessed" ) )
00086
00087
00088
00089
00090
00091
00092
00093 QObject::connectNotify( signal );
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105 }
00106
00107 void OFile::disconnectNotify( const char* signal )
00108 {
00109 qDebug( "OFile::disconnectNotify() signal = '%s'", signal );
00110 QObject::disconnectNotify( signal );
00111 }
00112
00113 int OFile::startWatch( int mode )
00114 {
00115 }
00116
00117
00118
00119
00120 OFileNotificationEvent::OFileNotificationEvent( OFileNotification* parent, int wd, unsigned int mask, unsigned int cookie, const QString& name )
00121 :_parent( parent ), _wd( wd ), _mask( mask ), _cookie( cookie ), _name( name )
00122 {
00123 qDebug( "OFileNotificationEvent()" );
00124 }
00125
00126
00127 OFileNotificationEvent::~OFileNotificationEvent()
00128 {
00129 qDebug( "~OFileNotificationEvent()" );
00130 }
00131
00132
00133
00134
00135 OFileNotification::OFileNotification( QObject* parent, const char* name )
00136 :QObject( parent, name ), _active( false ), _multi( true )
00137 {
00138 qDebug( "OFileNotification::OFileNotification()" );
00139 }
00140
00141
00142 OFileNotification::~OFileNotification()
00143 {
00144 stop();
00145 qDebug( "OFileNotification::~OFileNotification()" );
00146 }
00147
00148
00149 bool OFileNotification::isActive() const
00150 {
00151 return _active;
00152 }
00153
00154
00155 int OFileNotification::watch( const QString& path, bool sshot, OFileNotificationType type )
00156 {
00157
00158 struct stat s;
00159 if ( ::stat( (const char*) path, &s ) == -1 )
00160 {
00161 qWarning( "OFileNotification::watch(): Can't watch '%s': %s.", (const char*) path, strerror( errno ) );
00162 return -1;
00163 }
00164 if ( !S_ISREG( s.st_mode ) )
00165 {
00166 qWarning( "OFileNotification::watch(): Can't watch '%s': %s.", (const char*) path, "not a regular file" );
00167 return -1;
00168 }
00169
00170 return startWatching( path, sshot, type );
00171 }
00172
00173
00174 int OFileNotification::startWatching( const QString& path, bool sshot, OFileNotificationType type )
00175 {
00176 if ( notification_list.isEmpty() )
00177 {
00178 OFileNotification::registerEventHandler();
00179 }
00180
00181 struct inotify_watch_request iwr;
00182 ::memset( &iwr, 0, sizeof iwr );
00183 iwr.name = const_cast<char*>( (const char*) path );
00184 iwr.mask = type;
00185
00186 _wd = ::ioctl( OFileNotification::_fd, INOTIFY_WATCH, &iwr );
00187
00188 if ( _wd < 0 )
00189 {
00190 qWarning( "OFileNotification::watch(): inotify can't watch '%s': %s.", (const char*) path, strerror( errno ) );
00191 return -1;
00192 }
00193
00194 notification_list.insert( _wd, this );
00195 _path = path;
00196 _multi = !sshot;
00197 _type = type;
00198 _active = true;
00199 qDebug( "OFileNotification::watch(): watching '%s' [wd=%d].", (const char*) path, _wd );
00200 return _wd;
00201 }
00202
00203
00204 void OFileNotification::stop()
00205 {
00206 notification_list.remove( _wd );
00207 _path = QString::null;
00208 _wd = 0;
00209 _active = false;
00210 if ( notification_list.isEmpty() )
00211 {
00212 OFileNotification::unregisterEventHandler();
00213 }
00214 }
00215
00216
00217 OFileNotificationType OFileNotification::type() const
00218 {
00219 return _type;
00220 }
00221
00222
00223 QString OFileNotification::path() const
00224 {
00225 return _path;
00226 }
00227
00228
00229 bool OFileNotification::isSingleShot() const
00230 {
00231 return !_multi;
00232 }
00233
00234
00235 bool OFileNotification::activate( const OFileNotificationEvent* e )
00236 {
00237 qDebug( "OFileNotification::activate(): e = ( %s, %d, 0x%08x, %d, %s )", (const char*) _path, e->descriptor(), e->mask(), e->cookie(), (const char*) e->name() );
00238
00239
00240
00241
00242 _signal.activate();
00243
00244
00245 emit triggered( _path, e->mask(), e->name() );
00246
00247
00248 switch ( e->mask() )
00249 {
00250 case Access: emit accessed( _path ); break;
00251 case Modify: emit modified( _path ); break;
00252 case Attrib: emit attributed( _path); break;
00253 case CloseWrite: emit closed( _path, true ); break;
00254 case CloseNoWrite: emit closed( _path, false ); break;
00255 case Open: emit opened( _path ); break;
00256 case MovedFrom: emit movedFrom( _path, e->name() ); break;
00257 case MovedTo: emit movedTo( _path, e->name() ); break;
00258 case DeleteSubdir: emit deletedSubdir( _path, e->name() ); break;
00259 case DeleteFile: emit deletedFile( _path, e->name() ); break;
00260 case CreateSubdir: emit createdSubdir( _path, e->name() ); break;
00261 case CreateFile: emit createdFile( _path, e->name() ); break;
00262 case DeleteSelf: emit deleted( _path ); break;
00263 case Unmount: emit unmounted( _path ); break;
00264 case _QueueOverflow: qFatal( "OFileNotification::activate() - Inotify Event Queue Overload!" ); break;
00265 case _Ignored: qWarning( "OFileNotification::activate() - Further Events for '%s' will be ignored", (const char*) _path ); break;
00266 default: assert( 0 );
00267 }
00268
00269 delete e;
00270
00271 if ( !_multi ) stop();
00272
00273 return true;
00274 }
00275
00276
00277 bool OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type )
00278 {
00279 OFileNotification* ofn = new OFileNotification();
00280 ofn->_signal.connect( receiver, member );
00281 return ofn->watch( path, true, type ) != -1;
00282 }
00283
00284
00285 void OFileNotification::inotifyEventHandler()
00286 {
00287 qDebug( "OFileNotification::inotifyEventHandler(): reached." );
00288
00289 char buffer[16384];
00290 ssize_t buffer_i;
00291 struct inotify_event *pevent, *event;
00292 ssize_t r;
00293 size_t event_size;
00294 int count = 0;
00295
00296 r = ::read(_fd, buffer, 16384);
00297
00298 if ( r <= 0 )
00299 return;
00300
00301 buffer_i = 0;
00302 while ( buffer_i < r )
00303 {
00304 pevent = (struct inotify_event *)&buffer[buffer_i];
00305 event_size = sizeof(struct inotify_event) + pevent->len;
00306 OFileNotificationEvent* e = new OFileNotificationEvent( notification_list[ pevent->wd ], pevent->wd, pevent->mask,
00307 pevent->cookie, pevent->len ? pevent->name : 0 );
00308 e->activate();
00309 buffer_i += event_size;
00310 count++;
00311 }
00312
00313 qDebug( "OFileNotification::inotifyEventHandler(): processed %d events", count );
00314 }
00315
00316
00317 bool OFileNotification::registerEventHandler()
00318 {
00319 OFileNotification::_fd = ::open( INOTIFY_DEVICE, O_RDONLY );
00320 if ( OFileNotification::_fd < 0 )
00321 {
00322 qWarning( "OFileNotification::registerEventHandler(): couldn't register event handler: %s", strerror( errno ) );
00323 return false;
00324 }
00325
00326 OFileNotification::_sn = new QSocketNotifier( _fd, QSocketNotifier::Read );
00327 connect( OFileNotification::_sn, SIGNAL( activated(int) ), this, SLOT( inotifyEventHandler() ) );
00328
00329 qDebug( "OFileNotification::registerEventHandler(): done" );
00330 return true;
00331 }
00332
00333
00334 void OFileNotification::unregisterEventHandler()
00335 {
00336 if ( _sn ) delete _sn;
00337 if ( OFileNotification::_fd )
00338 ::close( OFileNotification::_fd );
00339 qDebug( "OFileNotification::unregisterEventHandler(): done" );
00340 }
00341
00342
00343
00344
00345 ODirNotification::ODirNotification( QObject* parent, const char* name )
00346 :QObject( parent, name ), _topfilenotification( 0 ), _type( Nothing ), _depth( -123 )
00347 {
00348 qDebug( "ODirNotification::ODirNotification()" );
00349 }
00350
00351
00352 ODirNotification::~ODirNotification()
00353 {
00354 qDebug( "ODirNotification::~ODirNotification()" );
00355 }
00356
00376 int ODirNotification::watch( const QString& path, bool sshot, OFileNotificationType type, int recurse )
00377 {
00378 if ( _type == Nothing ) _type = type;
00379 OFileNotificationType subtype = ( recurse != 0 ) ? (OFileNotificationType) int( _type | CreateSubdir ) : _type;
00380 qDebug( "ODirNotification::watch( %s, %d, 0x%08x, %d )", (const char*) path, sshot, subtype, recurse );
00381 OFileNotification* fn = new OFileNotification( this, "ODirNotification delegate" );
00382
00383 int result = fn->startWatching( path, sshot, subtype );
00384 if ( result != -1 )
00385 {
00386
00387 if ( !_topfilenotification ) _topfilenotification = fn;
00388 if ( _depth == -123 ) _depth = recurse;
00389
00390 connect( fn, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ), this, SIGNAL( triggered( const QString&, unsigned int, const QString& ) ) );
00391 connect( fn, SIGNAL( accessed( const QString& ) ), this, SIGNAL( accessed( const QString& ) ) );
00392 connect( fn, SIGNAL( modified( const QString& ) ), this, SIGNAL( modified( const QString& ) ) );
00393 connect( fn, SIGNAL( attributed( const QString& ) ), this, SIGNAL( attributed( const QString& ) ) );
00394 connect( fn, SIGNAL( closed( const QString&, bool ) ), this, SIGNAL( closed( const QString&, bool ) ) );
00395 connect( fn, SIGNAL( opened( const QString& ) ), this, SIGNAL( opened( const QString& ) ) );
00396 connect( fn, SIGNAL( movedTo( const QString&, const QString& ) ), this, SIGNAL( movedTo( const QString&, const QString& ) ) );
00397 connect( fn, SIGNAL( movedFrom( const QString&, const QString& ) ), this, SIGNAL( movedFrom( const QString&, const QString& ) ) );
00398 connect( fn, SIGNAL( deletedSubdir( const QString&, const QString& ) ), this, SIGNAL( deletedSubdir( const QString&, const QString& ) ) );
00399 connect( fn, SIGNAL( deletedFile( const QString&, const QString& ) ), this, SIGNAL( deletedFile( const QString&, const QString& ) ) );;
00400 connect( fn, SIGNAL( createdSubdir( const QString&, const QString& ) ), this, SIGNAL( createdSubdir( const QString&, const QString& ) ) );
00401 connect( fn, SIGNAL( createdFile( const QString&, const QString& ) ), this, SIGNAL( createdFile( const QString&, const QString& ) ) );
00402 connect( fn, SIGNAL( deleted( const QString& ) ), this, SIGNAL( deleted( const QString& ) ) );
00403 connect( fn, SIGNAL( unmounted( const QString& ) ), this, SIGNAL( unmounted( const QString& ) ) );
00404
00405 if ( recurse != 0 )
00406 {
00407 connect( fn, SIGNAL( createdSubdir( const QString&, const QString& ) ), this, SLOT( subdirCreated( const QString&, const QString& ) ) );
00408
00409 QDir directory( path );
00410 QStringList subdirs = directory.entryList( QDir::Dirs );
00411
00412 for ( QStringList::Iterator it = subdirs.begin(); it != subdirs.end(); ++it )
00413 {
00414 if ( (*it) == "." || (*it) == ".." ) continue;
00415 QString subpath = QString( "%1/%2" ).arg( path ).arg( *it );
00416 int subresult = watch( subpath, sshot, subtype, recurse-1 );
00417 if ( subresult == -1 )
00418 {
00419 qDebug( "ODirNotification::watch(): subresult for '%s' was -1. Interrupting", (const char*) (*it) );
00420 return -1;
00421 }
00422 }
00423 }
00424 }
00425 else return -1;
00426 }
00427
00428
00429 void ODirNotification::subdirCreated( const QString& dir, const QString& subdir )
00430 {
00431 qDebug( "*** ODirNotification::subdirCreated '%s/%s'", (const char*) dir, (const char*) subdir );
00432 QString newdir = dir;
00433 if ( newdir.startsWith( _topfilenotification->path() ) )
00434 {
00435 newdir.replace( _topfilenotification->path(), "" );
00436 int level = newdir.contains( '/' );
00437 qDebug( "*** dirpart = '%s' ==> level = %d", (const char*) newdir, level );
00438
00439 if ( _depth == -1 || _depth > level )
00440 {
00441 watch( QString( "%1/%2" ).arg( dir ).arg( subdir ), _topfilenotification->isSingleShot(), _topfilenotification->type(), _depth == -1 ? -1 : _depth-level-1 );
00442 }
00443
00444 }
00445 }
00446
00447
00448 }
00449
00450 }