-rw-r--r-- | libopie2/opiecore/ofilenotify.cpp | 98 | ||||
-rw-r--r-- | libopie2/opiecore/ofilenotify.h | 4 |
2 files changed, 91 insertions, 11 deletions
diff --git a/libopie2/opiecore/ofilenotify.cpp b/libopie2/opiecore/ofilenotify.cpp index bcfb1aa..de4c63b 100644 --- a/libopie2/opiecore/ofilenotify.cpp +++ b/libopie2/opiecore/ofilenotify.cpp @@ -31,24 +31,25 @@ using namespace Opie::Core; /* OPIE */ /* QT */ #include <qobject.h> #include <qsignal.h> #include <qintdict.h> #include <qdir.h> /* STD */ #include <sys/types.h> #include <sys/stat.h> +#include <assert.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <unistd.h> static QIntDict<OFileNotification> notification_list; namespace Opie { namespace Core { OFileNotification::OFileNotification( QObject* parent, const char* name ) :QObject( parent, name ), _active( false ) @@ -65,55 +66,87 @@ OFileNotification::~OFileNotification() bool OFileNotification::isActive() const { return _active; } int OFileNotification::start( const QString& path, bool sshot, OFileNotificationType type ) { _path = QString::null; _fd = 0; if ( _active ) stop(); + QString dirpath; - int fd = ::open( (const char*) path, O_RDONLY ); + // check if path exists and whether it is a file or a directory, if it exists at all + int result = ::stat( (const char*) path, &_stat ); + if ( result == -1 ) + { + qWarning( "OFileNotification::start(): Can't stat '%s': %s.", (const char*) path, strerror( errno ) ); + return -1; + } + + // if it is not a directory, we need to find out in which directory the file is + bool isDirectory = S_ISDIR( _stat.st_mode ); + if ( !isDirectory ) + { + int slashpos; + slashpos = path.findRev( '/' ); + if ( slashpos > 0 ) + { + _path = path; + dirpath = path.left( slashpos ); + } + } + else /* isDirectory */ + { + qWarning( "FIXME FIXME FIXME = Directory Notification Not Yet Implemented!" ); + _path = path; + dirpath = path; + assert( 0 ); + } + + int fd = ::open( (const char*) dirpath, O_RDONLY ); if ( fd != -1 ) { if ( notification_list.isEmpty() ) { OFileNotification::registerSignalHandler(); } - int result = ::fcntl( fd, F_SETSIG, SIGRTMIN ); + + result = ::fcntl( fd, F_SETSIG, SIGRTMIN ); if ( result == -1 ) { - qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) path, strerror( errno ) ); + qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); return -1; } if ( !sshot ) (int) type |= (int) Multi; result = ::fcntl( fd, F_NOTIFY, type ); if ( result == -1 ) { - qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) path, strerror( errno ) ); + qWarning( "OFileNotification::start(): Can't subscribe to '%s': %s.", (const char*) dirpath, strerror( errno ) ); return -1; } - qDebug( "OFileNotification::start(): Subscribed for changes to %s (fd = %d, mask = 0x%0x)", (const char*) path, fd, type ); + qDebug( "OFileNotification::start(): Subscribed for changes to %s (fd = %d, mask = 0x%0x)", (const char*) dirpath, fd, type ); notification_list.insert( fd, this ); _type = type; - _path = path; _fd = fd; + _active = true; + ::memset( &_stat, 0, sizeof _stat ); + ::stat( _path, &_stat ); return fd; } else { - qWarning( "OFileNotification::start(): Error with path '%s': %s.", (const char*) path, strerror( errno ) ); + qWarning( "OFileNotification::start(): Error with path '%s': %s.", (const char*) dirpath, strerror( errno ) ); return -1; } } void OFileNotification::stop() { if ( !_active ) return; int result = ::fcntl( _fd, F_NOTIFY, 0 ); if ( result == -1 ) { @@ -132,52 +165,98 @@ void OFileNotification::stop() OFileNotificationType OFileNotification::type() const { return _type; } QString OFileNotification::path() const { return _path; } + int OFileNotification::fileno() const { return _fd; } -void OFileNotification::activate() + +bool OFileNotification::activate() +{ + if ( hasChanged() ) { emit triggered(); _signal.activate(); + return true; + } + else + return false; +} + + +bool OFileNotification::hasChanged() +{ + bool c = false; + + struct stat newstat; + ::memset( &newstat, 0, sizeof newstat ); + ::stat( _path, &newstat ); + + qDebug( "stat.atime = %0lx, newstat.atime = %0lx", (long)_stat.st_atime, (long)newstat.st_atime ); + qDebug( "stat.mtime = %0lx, newstat.mtime = %0lx", (long)_stat.st_mtime, (long)newstat.st_mtime ); + if ( !c && (_type & Access) && (long)_stat.st_atime < (long)newstat.st_atime ) + { + qDebug( "OFileNotification::hasChanged(): atime changed" ); + c = true; + } + if ( !c && (_type & Modify) && (long)_stat.st_mtime < (long)newstat.st_mtime ) + { + qDebug( "OFileNotification::hasChanged(): mtime changed" ); + c = true; + } + + return c; } void OFileNotification::singleShot( const QString& path, QObject* receiver, const char* member, OFileNotificationType type ) { OFileNotification* ofn = new OFileNotification(); ofn->_signal.connect( receiver, member ); ofn->start( path, true, type ); } void OFileNotification::__signalHandler( int sig, siginfo_t *si, void *data ) { qWarning( "OFileNotification::__signalHandler(): reached." ); int fd = si->si_fd; OFileNotification* fn = notification_list[fd]; if ( fn ) { - fn->activate(); + // check if it really was the file (dnotify triggers on directory granularity, not file granularity) + if ( !fn->activate() ) + { + qDebug( "OFileNotification::__signalHandler(): false alarm ;) Restarting the trigger (if it was single)..." ); + if ( !(fn->type() & Multi ) ) + { + int result = ::fcntl( fn->fileno(), F_NOTIFY, fn->type() ); + if ( result == -1 ) + { + qWarning( "OFileNotification::__signalHandler(): Can't restart the trigger: %s.", strerror( errno ) ); + } + } + return; + } #if 1 if ( !(fn->type() & Multi) ) { qDebug( "OFileNotification::__signalHandler(): '%d' was singleShot. Removing from list.", fd ); notification_list.remove( fd ); if ( notification_list.isEmpty() ) { OFileNotification::unregisterSignalHandler(); } } #endif } @@ -200,22 +279,21 @@ bool OFileNotification::registerSignalHandler() return false; } qDebug( "OFileNotification::registerSignalHandler(): done" ); } void OFileNotification::unregisterSignalHandler() { struct sigaction act; act.sa_sigaction = ( void (*)(int, siginfo_t*, void*) ) SIG_DFL; ::sigemptyset( &act.sa_mask ); if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) - if ( ::sigaction( SIGRTMIN, &act, NULL ) == -1 ) { qWarning( "OFileNotification::unregisterSignalHandler(): couldn't deregister signal handler: %s", strerror( errno ) ); } qDebug( "OFileNotification::unregisterSignalHandler(): done" ); } } } diff --git a/libopie2/opiecore/ofilenotify.h b/libopie2/opiecore/ofilenotify.h index 3bc141d..5315896 100644 --- a/libopie2/opiecore/ofilenotify.h +++ b/libopie2/opiecore/ofilenotify.h @@ -66,30 +66,32 @@ class OFileNotification : public QObject int start( const QString& path, bool sshot = false, OFileNotificationType type = Modify ); void stop(); OFileNotificationType type() const; QString path() const; int fileno() const; bool isActive() const; signals: void triggered(); protected: - void activate(); + bool activate(); + bool hasChanged(); static bool registerSignalHandler(); static void unregisterSignalHandler(); static void __signalHandler( int sig, siginfo_t *si, void *data ); private: QString _path; OFileNotificationType _type; QSignal _signal; int _fd; bool _active; + struct stat _stat; }; } } #endif |