2011-04-06 13:48:02 +04:00
/*
* Copyright ( C ) by Duncan Mac - Vicar P . < duncan @ kde . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*/
2011-02-17 02:21:45 +03:00
2011-03-16 16:50:34 +03:00
// event masks
2012-03-01 19:13:50 +04:00
# include <stdint.h>
2011-03-16 16:50:34 +03:00
2011-02-17 02:21:45 +03:00
# include <QFileInfo>
# include <QFlags>
# include <QDebug>
# include <QDir>
# include <QMutexLocker>
# include <QStringList>
2011-03-18 15:54:32 +03:00
# include <QTimer>
2011-02-17 02:21:45 +03:00
2012-03-01 19:13:50 +04:00
# include "mirall/folder.h"
2011-03-16 16:50:34 +03:00
# include "mirall/inotify.h"
2011-02-17 02:21:45 +03:00
# include "mirall/folderwatcher.h"
2011-03-21 00:43:03 +03:00
# include "mirall/fileutils.h"
2011-02-17 02:21:45 +03:00
2012-03-01 19:13:50 +04:00
# ifdef USE_WATCHER
# include <sys/inotify.h>
# endif
2011-03-16 16:50:34 +03:00
static const uint32_t standard_event_mask =
2012-03-01 19:13:50 +04:00
# ifdef USE_WATCHER
2011-03-19 23:18:43 +03:00
IN_CLOSE_WRITE | IN_ATTRIB | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | IN_ONLYDIR | IN_DONT_FOLLOW ;
2012-03-01 19:13:50 +04:00
# else
0 ;
# endif
2011-03-21 00:38:33 +03:00
/* minimum amount of seconds between two
events to consider it a new event */
2012-02-20 19:45:27 +04:00
# define DEFAULT_EVENT_INTERVAL_MSEC 1000
2011-03-21 00:38:33 +03:00
2011-02-17 02:21:45 +03:00
namespace Mirall {
2011-03-18 15:54:32 +03:00
FolderWatcher : : FolderWatcher ( const QString & root , QObject * parent )
: QObject ( parent ) ,
2011-03-27 04:26:41 +04:00
_eventsEnabled ( true ) ,
2012-02-20 19:45:27 +04:00
_eventInterval ( DEFAULT_EVENT_INTERVAL_MSEC ) ,
2011-03-21 00:38:33 +03:00
_root ( root ) ,
2011-03-23 01:03:43 +03:00
_processTimer ( new QTimer ( this ) ) ,
2011-04-08 12:48:22 +04:00
_lastMask ( 0 ) ,
_initialSyncDone ( false )
2011-02-17 02:21:45 +03:00
{
2011-04-04 20:41:14 +04:00
// this is not the best place for this
2012-02-29 19:25:38 +04:00
addIgnore ( " .unison* " ) ;
2011-04-07 21:04:07 +04:00
addIgnore ( " *csync_timediff.ctmp* " ) ;
2012-02-29 18:25:16 +04:00
addIgnore ( " .*.sw? " ) ; // vi swap files
2012-02-29 19:25:38 +04:00
addIgnore ( " .*.*.sw? " ) ;
2012-02-29 18:25:16 +04:00
# ifdef USE_WATCHER
2011-03-23 01:03:43 +03:00
_processTimer - > setSingleShot ( true ) ;
2011-03-28 01:29:45 +04:00
QObject : : connect ( _processTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( slotProcessTimerTimeout ( ) ) ) ;
2011-03-23 01:03:43 +03:00
2011-03-17 09:13:30 +03:00
_inotify = new INotify ( standard_event_mask ) ;
2011-03-18 15:54:32 +03:00
slotAddFolderRecursive ( root ) ;
2011-03-19 23:18:43 +03:00
QObject : : connect ( _inotify , SIGNAL ( notifyEvent ( int , int , const QString & ) ) ,
SLOT ( slotINotifyEvent ( int , int , const QString & ) ) ) ;
2012-02-29 18:25:16 +04:00
# endif
2011-04-04 20:41:14 +04:00
// do a first synchronization to get changes while
// the application was not running
setProcessTimer ( ) ;
2011-02-17 02:21:45 +03:00
}
FolderWatcher : : ~ FolderWatcher ( )
{
}
2011-03-21 00:18:38 +03:00
QString FolderWatcher : : root ( ) const
{
return _root ;
}
2011-04-04 20:41:14 +04:00
void FolderWatcher : : addIgnore ( const QString & pattern )
{
_ignores . append ( pattern ) ;
}
2011-03-27 04:26:41 +04:00
bool FolderWatcher : : eventsEnabled ( ) const
{
return _eventsEnabled ;
}
void FolderWatcher : : setEventsEnabled ( bool enabled )
{
2011-03-28 01:29:45 +04:00
qDebug ( ) < < " * event notification " < < ( enabled ? " enabled " : " disabled " ) ;
2011-03-27 04:26:41 +04:00
_eventsEnabled = enabled ;
if ( _eventsEnabled ) {
// schedule a queue cleanup for accumulated events
2012-02-16 01:36:52 +04:00
if ( _pendingPathes . empty ( ) )
2011-03-27 04:26:41 +04:00
return ;
2011-03-28 01:29:45 +04:00
setProcessTimer ( ) ;
2011-03-27 04:26:41 +04:00
}
else
{
// if we are disabling events, clear any ongoing timer
if ( _processTimer - > isActive ( ) )
_processTimer - > stop ( ) ;
}
}
2011-04-05 14:16:24 +04:00
void FolderWatcher : : clearPendingEvents ( )
{
if ( _processTimer - > isActive ( ) )
_processTimer - > stop ( ) ;
2012-02-16 01:36:52 +04:00
_pendingPathes . clear ( ) ;
2011-04-05 14:16:24 +04:00
}
2011-03-27 04:26:41 +04:00
int FolderWatcher : : eventInterval ( ) const
{
return _eventInterval ;
}
void FolderWatcher : : setEventInterval ( int seconds )
{
_eventInterval = seconds ;
}
2011-03-18 03:14:45 +03:00
QStringList FolderWatcher : : folders ( ) const
{
2012-02-29 18:25:16 +04:00
# ifdef USE_WATCHER
2011-03-18 03:14:45 +03:00
return _inotify - > directories ( ) ;
2012-02-29 18:25:16 +04:00
# endif
2011-03-18 03:14:45 +03:00
}
2011-03-18 15:54:32 +03:00
void FolderWatcher : : slotAddFolderRecursive ( const QString & path )
2011-02-17 02:21:45 +03:00
{
2011-03-30 00:23:42 +04:00
int subdirs = 0 ;
2011-03-28 13:54:10 +04:00
qDebug ( ) < < " (+) Watcher: " < < path ;
2012-02-29 18:25:16 +04:00
# ifdef USE_WATCHER
2012-02-29 19:25:38 +04:00
2011-03-19 23:18:43 +03:00
_inotify - > addPath ( path ) ;
2011-03-17 09:13:30 +03:00
QStringList watchedFolders ( _inotify - > directories ( ) ) ;
2012-02-29 19:25:38 +04:00
// qDebug() << "currently watching " << watchedFolders;
2011-03-21 00:43:03 +03:00
QStringListIterator subfoldersIt ( FileUtils : : subFoldersList ( path , FileUtils : : SubFolderRecursive ) ) ;
2011-02-17 02:21:45 +03:00
while ( subfoldersIt . hasNext ( ) ) {
2012-02-29 19:25:38 +04:00
QString subfolder = subfoldersIt . next ( ) ;
// qDebug() << " (**) subfolder: " << subfolder;
QDir folder ( subfolder ) ;
2011-02-17 02:21:45 +03:00
if ( folder . exists ( ) & & ! watchedFolders . contains ( folder . path ( ) ) ) {
2011-03-30 00:23:42 +04:00
subdirs + + ;
2011-04-04 20:41:14 +04:00
// check that it does not match the ignore list
foreach ( QString pattern , _ignores ) {
QRegExp regexp ( pattern ) ;
regexp . setPatternSyntax ( QRegExp : : Wildcard ) ;
2012-02-29 19:25:38 +04:00
if ( regexp . exactMatch ( folder . path ( ) ) ) {
2011-04-04 20:41:14 +04:00
qDebug ( ) < < " * Not adding " < < folder . path ( ) ;
continue ;
}
}
2011-03-17 09:13:30 +03:00
_inotify - > addPath ( folder . path ( ) ) ;
2011-02-17 02:21:45 +03:00
}
2011-03-18 03:14:45 +03:00
else
2011-03-30 00:23:42 +04:00
qDebug ( ) < < " `-> discarded: " < < folder . path ( ) ;
2011-02-17 02:21:45 +03:00
}
2011-03-30 00:23:42 +04:00
if ( subdirs > 0 )
qDebug ( ) < < " `-> and " < < subdirs < < " subdirectories " ;
2012-02-29 18:25:16 +04:00
# else
2012-02-29 19:25:38 +04:00
qDebug ( ) < < " ** Watcher is not compiled in! " ;
2012-02-29 18:25:16 +04:00
# endif
2011-03-18 15:54:32 +03:00
}
2011-02-17 02:21:45 +03:00
2011-03-19 23:18:43 +03:00
void FolderWatcher : : slotINotifyEvent ( int mask , int cookie , const QString & path )
2011-03-18 15:54:32 +03:00
{
2011-03-28 01:29:45 +04:00
int lastMask = _lastMask ;
QString lastPath = _lastPath ;
_lastMask = mask ;
_lastPath = path ;
2012-02-29 18:25:16 +04:00
if ( ! eventsEnabled ( ) ) return ;
2012-03-01 19:13:50 +04:00
# ifdef USE_WATCHER
2012-02-29 18:25:16 +04:00
qDebug ( ) < < " ** Inotify Event " < < mask < < " on " < < path ;
2011-03-28 01:29:45 +04:00
// cancel close write events that come after create
2011-03-28 13:54:10 +04:00
if ( lastMask = = IN_CREATE & & mask = = IN_CLOSE_WRITE
& & lastPath = = path ) {
return ;
}
2011-03-28 01:29:45 +04:00
2011-03-21 00:38:33 +03:00
if ( IN_IGNORED & mask ) {
2011-03-28 01:29:45 +04:00
//qDebug() << "IGNORE event";
2011-03-21 00:38:33 +03:00
return ;
}
2011-03-28 13:54:10 +04:00
if ( IN_Q_OVERFLOW & mask ) {
2011-03-28 01:29:45 +04:00
//qDebug() << "OVERFLOW";
2011-03-28 13:54:10 +04:00
}
2011-02-17 02:21:45 +03:00
2011-03-18 15:54:32 +03:00
if ( mask & IN_CREATE ) {
2011-03-28 13:54:10 +04:00
//qDebug() << cookie << " CREATE: " << path;
2011-03-18 15:54:32 +03:00
if ( QFileInfo ( path ) . isDir ( ) ) {
2011-04-04 20:41:14 +04:00
//setEventsEnabled(false);
2011-03-18 15:54:32 +03:00
slotAddFolderRecursive ( path ) ;
2011-04-04 20:41:14 +04:00
//setEventsEnabled(true);
2011-03-18 15:54:32 +03:00
}
}
else if ( mask & IN_DELETE ) {
2011-03-28 01:29:45 +04:00
//qDebug() << cookie << " DELETE: " << path;
2011-04-04 20:41:14 +04:00
if ( _inotify - > directories ( ) . contains ( path ) & &
QFileInfo ( path ) . isDir ( ) ) ;
2011-03-28 13:54:10 +04:00
qDebug ( ) < < " (-) Watcher: " < < path ;
2011-04-04 20:41:14 +04:00
_inotify - > removePath ( path ) ;
2011-03-18 15:54:32 +03:00
}
2011-03-19 23:18:43 +03:00
else if ( mask & IN_CLOSE_WRITE ) {
2011-03-28 01:29:45 +04:00
//qDebug() << cookie << " WRITABLE CLOSED: " << path;
2011-03-18 15:54:32 +03:00
}
else if ( mask & IN_MOVE ) {
2011-03-28 01:29:45 +04:00
//qDebug() << cookie << " MOVE: " << path;
2011-03-18 15:54:32 +03:00
}
else {
2011-03-28 01:29:45 +04:00
//qDebug() << cookie << " OTHER " << mask << " :" << path;
2011-03-18 15:54:32 +03:00
}
2011-03-21 00:38:33 +03:00
2011-04-04 20:41:14 +04:00
foreach ( QString pattern , _ignores ) {
QRegExp regexp ( pattern ) ;
regexp . setPatternSyntax ( QRegExp : : Wildcard ) ;
2012-02-29 19:25:38 +04:00
2011-04-04 20:41:14 +04:00
if ( regexp . exactMatch ( path ) ) {
2012-02-29 19:25:38 +04:00
qDebug ( ) < < " * Discarded " < < path ;
return ;
}
QFileInfo fInfo ( path ) ;
if ( regexp . exactMatch ( fInfo . fileName ( ) ) ) {
qDebug ( ) < < " * Discarded " < < path ;
return ;
}
if ( fInfo . isHidden ( ) ) {
qDebug ( ) < < " * Discarded as is hidden! " ;
2011-04-04 20:41:14 +04:00
return ;
}
}
2012-02-16 01:36:52 +04:00
if ( ! _pendingPathes . contains ( path ) ) {
_pendingPathes [ path ] = 0 ;
}
_pendingPathes [ path ] = _pendingPathes [ path ] + mask ;
#if 0
_pendingPaths [ path ]
2011-03-27 04:26:41 +04:00
_pendingPaths . append ( path ) ;
2012-03-01 19:13:50 +04:00
# endif
2012-02-16 01:36:52 +04:00
# endif
2011-04-07 00:35:08 +04:00
setProcessTimer ( ) ;
2011-03-28 01:29:45 +04:00
}
2011-03-23 01:03:43 +03:00
2011-03-28 01:29:45 +04:00
void FolderWatcher : : slotProcessTimerTimeout ( )
{
2011-04-07 00:35:08 +04:00
qDebug ( ) < < " * Processing of event queue for " < < root ( ) ;
2012-02-16 01:36:52 +04:00
if ( ! _pendingPathes . empty ( ) | | ! _initialSyncDone ) {
QStringList notifyPaths = _pendingPathes . keys ( ) ;
_pendingPathes . clear ( ) ;
2011-04-07 00:35:08 +04:00
//qDebug() << lastEventTime << eventTime;
qDebug ( ) < < " * Notify " < < notifyPaths . size ( ) < < " changed items for " < < root ( ) ;
emit folderChanged ( notifyPaths ) ;
2011-04-08 12:48:22 +04:00
_initialSyncDone = true ;
2011-04-07 00:35:08 +04:00
}
2011-04-04 20:41:14 +04:00
}
2011-03-28 01:29:45 +04:00
void FolderWatcher : : setProcessTimer ( )
{
if ( ! _processTimer - > isActive ( ) ) {
2012-02-16 01:36:52 +04:00
qDebug ( ) < < " * Pending events for " < < root ( ) < < " will be processed after events stop for " < < eventInterval ( ) < < " seconds ( " < < QTime : : currentTime ( ) . addSecs ( eventInterval ( ) ) . toString ( " HH:mm:ss " ) < < " ). " < < _pendingPathes . size ( ) < < " events until now ) " ;
2011-03-28 01:29:45 +04:00
}
2012-02-20 19:45:27 +04:00
_processTimer - > start ( eventInterval ( ) ) ;
2011-03-28 01:29:45 +04:00
}
2011-02-17 02:21:45 +03:00
}