joomer-efsw-file-monitoring/src/efsw/DirWatcherGeneric.cpp

389 lines
10 KiB
C++

#include <efsw/Debug.hpp>
#include <efsw/DirWatcherGeneric.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/String.hpp>
namespace efsw {
DirWatcherGeneric::DirWatcherGeneric( DirWatcherGeneric* parent, WatcherGeneric* ws,
const std::string& directory, bool recursive,
bool reportNewFiles ) :
Parent( parent ), Watch( ws ), Recursive( recursive ), Deleted( false ) {
resetDirectory( directory );
if ( !reportNewFiles ) {
DirSnap.scan();
} else {
DirectorySnapshotDiff Diff = DirSnap.scan();
if ( Diff.changed() ) {
FileInfoList::iterator it;
DiffIterator( FilesCreated ) {
handleAction( ( *it ).Filepath, Actions::Add );
}
}
}
}
DirWatcherGeneric::~DirWatcherGeneric() {
/// If the directory was deleted mark the files as deleted
if ( Deleted ) {
DirectorySnapshotDiff Diff = DirSnap.scan();
if ( !DirSnap.exists() ) {
FileInfoList::iterator it;
DiffIterator( FilesDeleted ) {
handleAction( ( *it ).Filepath, Actions::Delete );
}
DiffIterator( DirsDeleted ) {
handleAction( ( *it ).Filepath, Actions::Delete );
}
}
}
DirWatchMap::iterator it = Directories.begin();
for ( ; it != Directories.end(); ++it ) {
if ( Deleted ) {
/// If the directory was deleted, mark the flag for file deletion
it->second->Deleted = true;
}
efSAFE_DELETE( it->second );
}
}
void DirWatcherGeneric::resetDirectory( std::string directory ) {
std::string dir( directory );
/// Is this a recursive watch?
if ( Watch->Directory != directory ) {
if ( !( directory.size() &&
( directory.at( 0 ) == FileSystem::getOSSlash() ||
directory.at( directory.size() - 1 ) == FileSystem::getOSSlash() ) ) ) {
/// Get the real directory
if ( NULL != Parent ) {
std::string parentPath( Parent->DirSnap.DirectoryInfo.Filepath );
FileSystem::dirAddSlashAtEnd( parentPath );
FileSystem::dirAddSlashAtEnd( directory );
dir = parentPath + directory;
} else {
efDEBUG( "resetDirectory(): Parent is NULL. Fatal error." );
}
}
}
DirSnap.setDirectoryInfo( dir );
}
void DirWatcherGeneric::handleAction( const std::string& filename, unsigned long action,
std::string oldFilename ) {
Watch->Listener->handleFileAction( Watch->ID, DirSnap.DirectoryInfo.Filepath,
FileSystem::fileNameFromPath( filename ), (Action)action,
oldFilename );
}
void DirWatcherGeneric::addChilds( bool reportNewFiles ) {
if ( Recursive ) {
/// Create the subdirectories watchers
std::string dir;
for ( FileInfoMap::iterator it = DirSnap.Files.begin(); it != DirSnap.Files.end(); it++ ) {
if ( it->second.isDirectory() && it->second.isReadable() &&
!FileSystem::isRemoteFS( it->second.Filepath ) ) {
/// Check if the directory is a symbolic link
std::string curPath;
std::string link( FileSystem::getLinkRealPath( it->second.Filepath, curPath ) );
dir = it->first;
if ( "" != link ) {
/// Avoid adding symlinks directories if it's now enabled
if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) {
continue;
}
/// If it's a symlink check if the realpath exists as a watcher, or
/// if the path is outside the current dir
if ( Watch->WatcherImpl->pathInWatches( link ) ||
Watch->pathInWatches( link ) ||
!Watch->WatcherImpl->linkAllowed( curPath, link ) ) {
continue;
} else {
dir = link;
}
} else {
if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) {
continue;
}
}
if ( reportNewFiles ) {
handleAction( dir, Actions::Add );
}
Directories[dir] =
new DirWatcherGeneric( this, Watch, dir, Recursive, reportNewFiles );
Directories[dir]->addChilds( reportNewFiles );
}
}
}
}
void DirWatcherGeneric::watch( bool reportOwnChange ) {
DirectorySnapshotDiff Diff = DirSnap.scan();
if ( reportOwnChange && Diff.DirChanged && NULL != Parent ) {
Watch->Listener->handleFileAction(
Watch->ID, FileSystem::pathRemoveFileName( DirSnap.DirectoryInfo.Filepath ),
FileSystem::fileNameFromPath( DirSnap.DirectoryInfo.Filepath ), Actions::Modified );
}
if ( Diff.changed() ) {
FileInfoList::iterator it;
MovedList::iterator mit;
/// Files
DiffIterator( FilesCreated ) {
handleAction( ( *it ).Filepath, Actions::Add );
}
DiffIterator( FilesModified ) {
handleAction( ( *it ).Filepath, Actions::Modified );
}
DiffIterator( FilesDeleted ) {
handleAction( ( *it ).Filepath, Actions::Delete );
}
DiffMovedIterator( FilesMoved ) {
handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
}
/// Directories
DiffIterator( DirsCreated ) {
createDirectory( ( *it ).Filepath );
}
DiffIterator( DirsModified ) {
handleAction( ( *it ).Filepath, Actions::Modified );
}
DiffIterator( DirsDeleted ) {
handleAction( ( *it ).Filepath, Actions::Delete );
removeDirectory( ( *it ).Filepath );
}
DiffMovedIterator( DirsMoved ) {
handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first );
moveDirectory( ( *mit ).first, ( *mit ).second.Filepath );
}
}
/// Process the subdirectories looking for changes
for ( DirWatchMap::iterator dit = Directories.begin(); dit != Directories.end(); ++dit ) {
/// Just watch
dit->second->watch();
}
}
void DirWatcherGeneric::watchDir( std::string& dir ) {
DirWatcherGeneric* watcher = Watch->WatcherImpl->mFileWatcher->allowOutOfScopeLinks()
? findDirWatcher( dir )
: findDirWatcherFast( dir );
if ( NULL != watcher ) {
watcher->watch( true );
}
}
DirWatcherGeneric* DirWatcherGeneric::findDirWatcherFast( std::string dir ) {
// remove the common base ( dir should always start with the same base as the watcher )
efASSERT( !dir.empty() );
efASSERT( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() );
efASSERT( DirSnap.DirectoryInfo.Filepath ==
dir.substr( 0, DirSnap.DirectoryInfo.Filepath.size() ) );
if ( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() ) {
dir = dir.substr( DirSnap.DirectoryInfo.Filepath.size() - 1 );
}
if ( dir.size() == 1 ) {
efASSERT( dir[0] == FileSystem::getOSSlash() );
return this;
}
size_t level = 0;
std::vector<std::string> dirv = String::split( dir, FileSystem::getOSSlash(), false );
DirWatcherGeneric* watcher = this;
while ( level < dirv.size() ) {
// search the dir level in the current watcher
DirWatchMap::iterator it = watcher->Directories.find( dirv[level] );
// found? continue with the next level
if ( it != watcher->Directories.end() ) {
watcher = it->second;
level++;
} else {
// couldn't found the folder level?
// directory not watched
return NULL;
}
}
return watcher;
}
DirWatcherGeneric* DirWatcherGeneric::findDirWatcher( std::string dir ) {
if ( DirSnap.DirectoryInfo.Filepath == dir ) {
return this;
} else {
DirWatcherGeneric* watcher = NULL;
for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) {
watcher = it->second->findDirWatcher( dir );
if ( NULL != watcher ) {
return watcher;
}
}
}
return NULL;
}
DirWatcherGeneric* DirWatcherGeneric::createDirectory( std::string newdir ) {
FileSystem::dirRemoveSlashAtEnd( newdir );
newdir = FileSystem::fileNameFromPath( newdir );
DirWatcherGeneric* dw = NULL;
/// Check if the directory is a symbolic link
std::string parentPath( DirSnap.DirectoryInfo.Filepath );
FileSystem::dirAddSlashAtEnd( parentPath );
std::string dir( parentPath + newdir );
FileSystem::dirAddSlashAtEnd( dir );
FileInfo fi( dir );
if ( !fi.isDirectory() || !fi.isReadable() || FileSystem::isRemoteFS( dir ) ) {
return NULL;
}
std::string curPath;
std::string link( FileSystem::getLinkRealPath( dir, curPath ) );
bool skip = false;
if ( "" != link ) {
/// Avoid adding symlinks directories if it's now enabled
if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) {
skip = true;
}
/// If it's a symlink check if the realpath exists as a watcher, or
/// if the path is outside the current dir
if ( Watch->WatcherImpl->pathInWatches( link ) || Watch->pathInWatches( link ) ||
!Watch->WatcherImpl->linkAllowed( curPath, link ) ) {
skip = true;
} else {
dir = link;
}
} else {
if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) {
skip = true;
}
}
if ( !skip ) {
handleAction( newdir, Actions::Add );
/// Creates the new directory watcher of the subfolder and check for new files
dw = new DirWatcherGeneric( this, Watch, dir, Recursive );
dw->addChilds();
dw->watch();
/// Add it to the list of directories
Directories[newdir] = dw;
}
return dw;
}
void DirWatcherGeneric::removeDirectory( std::string dir ) {
FileSystem::dirRemoveSlashAtEnd( dir );
dir = FileSystem::fileNameFromPath( dir );
DirWatcherGeneric* dw = NULL;
DirWatchMap::iterator dit;
/// Folder deleted
/// Search the folder, it should exists
dit = Directories.find( dir );
if ( dit != Directories.end() ) {
dw = dit->second;
/// Flag it as deleted so it fire the event for every file inside deleted
dw->Deleted = true;
/// Delete the DirWatcherGeneric
efSAFE_DELETE( dw );
/// Remove the directory from the map
Directories.erase( dit->first );
}
}
void DirWatcherGeneric::moveDirectory( std::string oldDir, std::string newDir ) {
FileSystem::dirRemoveSlashAtEnd( oldDir );
oldDir = FileSystem::fileNameFromPath( oldDir );
FileSystem::dirRemoveSlashAtEnd( newDir );
newDir = FileSystem::fileNameFromPath( newDir );
DirWatcherGeneric* dw = NULL;
DirWatchMap::iterator dit;
/// Directory existed?
dit = Directories.find( oldDir );
if ( dit != Directories.end() ) {
dw = dit->second;
/// Remove the directory from the map
Directories.erase( dit->first );
Directories[newDir] = dw;
dw->resetDirectory( newDir );
}
}
bool DirWatcherGeneric::pathInWatches( std::string path ) {
if ( DirSnap.DirectoryInfo.Filepath == path ) {
return true;
}
for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) {
if ( it->second->pathInWatches( path ) ) {
return true;
}
}
return false;
}
} // namespace efsw