Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

ofilenotify.cpp

Go to the documentation of this file.
00001 /*
00002                             This file is part of the Opie Project
00003              =.             Copyright (C) 2004-2005 Michael 'Mickey' Lauer <mickey@Vanille.de>
00004            .=l.             Copyright (C) The Opie Team <opie-devel@handhelds.org>
00005           .>+-=
00006 _;:,     .>    :=|.         This program is free software; you can
00007 .> <`_,   >  .   <=         redistribute it and/or  modify it under
00008 :`=1 )Y*s>-.--   :          the terms of the GNU Library General Public
00009 .="- .-=="i,     .._        License as published by the Free Software
00010 - .   .-<_>     .<>         Foundation; version 2 of the License.
00011     ._= =}       :
00012    .%`+i>       _;_.
00013    .i_,=:_.      -<s.       This program is distributed in the hope that
00014     +  .  -:.       =       it will be useful,  but WITHOUT ANY WARRANTY;
00015    : ..    .:,     . . .    without even the implied warranty of
00016    =_        +     =;=|`    MERCHANTABILITY or FITNESS FOR A
00017  _.=:.       :    :=>`:     PARTICULAR PURPOSE. See the GNU
00018 ..}^=.=       =       ;     Library General Public License for more
00019 ++=   -.     .`     .:      details.
00020 :     =  ...= . :.=-
00021 -.   .:....=;==+<;          You should have received a copy of the GNU
00022  -_. . .   )=.  =           Library General Public License along with
00023    --        :-=`           this library; see the file COPYING.LIB.
00024                             If not, write to the Free Software Foundation,
00025                             Inc., 59 Temple Place - Suite 330,
00026                             Boston, MA 02111-1307, USA.
00027 */
00028 
00029 #include "ofilenotify.h"
00030 using namespace Opie::Core;
00031 
00032 /* OPIE */
00033 
00034 /* QT */
00035 #include <qobject.h>
00036 #include <qsocketnotifier.h>
00037 #include <qsignal.h>
00038 #include <qintdict.h>
00039 #include <qdir.h>
00040 
00041 /* STD */
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 // OFile
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     void accessed( const QString& );
00097     void modified( const QString& );
00098     void attributed( const QString& );
00099     void closed( const QString&, bool );
00100     void opened( const QString& );
00101     void deleted( const QString& );
00102     void unmounted( const QString& );
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 // OFileNotificationEvent
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 // OFileNotification
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     // check if path exists and is a regular file
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     //FIXME: Should we really deliver QueueOverflow and/or Ignore to user level code?
00240 
00241     // dumb signal
00242     _signal.activate();
00243 
00244     // generic signal
00245     emit triggered( _path, e->mask(), e->name() );
00246 
00247     // specialized signals
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 // ODirNotification
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; // only set it once - for the top level call
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; // only set it once - for the top level call
00388         if ( _depth == -123 ) _depth = recurse; // only set it once - for the top level call
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 } // namespace Ui
00449 
00450 } // namespace Opie

Generated on Sat Nov 5 16:16:07 2005 for OPIE by  doxygen 1.4.2