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

224 lines
7.3 KiB
C++

#include <efsw/Debug.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/FileWatcherFSEvents.hpp>
#include <efsw/WatcherFSEvents.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
namespace efsw {
WatcherFSEvents::WatcherFSEvents() :
Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {}
WatcherFSEvents::~WatcherFSEvents() {
if ( NULL != FSStream ) {
FSEventStreamStop( FSStream );
FSEventStreamInvalidate( FSStream );
FSEventStreamRelease( FSStream );
}
efSAFE_DELETE( WatcherGen );
}
void WatcherFSEvents::init() {
CFStringRef CFDirectory =
CFStringCreateWithCString( NULL, Directory.c_str(), kCFStringEncodingUTF8 );
CFArrayRef CFDirectoryArray = CFArrayCreate( NULL, (const void**)&CFDirectory, 1, NULL );
Uint32 streamFlags = kFSEventStreamCreateFlagNone;
if ( FileWatcherFSEvents::isGranular() ) {
streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer |
efswFSEventStreamCreateFlagUseExtendedData |
efswFSEventStreamCreateFlagUseCFTypes;
} else {
WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive );
}
FSEventStreamContext ctx;
/* Initialize context */
ctx.version = 0;
ctx.info = this;
ctx.retain = NULL;
ctx.release = NULL;
ctx.copyDescription = NULL;
dispatch_queue_t queue = dispatch_queue_create( NULL, NULL );
FSStream =
FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx,
CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0., streamFlags );
FSEventStreamSetDispatchQueue( FSStream, queue );
FSEventStreamStart( FSStream );
CFRelease( CFDirectoryArray );
CFRelease( CFDirectory );
}
void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir,
const std::string& filename, Action action,
std::string oldFilename ) {
Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ),
FileSystem::precomposeFileName( filename ), action,
FileSystem::precomposeFileName( oldFilename ) );
}
void WatcherFSEvents::sendMissedFileActions( WatchID watchid,
const std::string& dir) {
Listener->handleMissedFileActions( watchid,
FileSystem::precomposeFileName( dir ) );
}
void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path,
std::string& dirPath, std::string& filePath, Uint64 inode ) {
if ( ( flags & efswFSEventStreamEventFlagItemCreated ) && FileInfo::exists( path ) &&
( !SanitizeEvents || FilesAdded.find( inode ) != FilesAdded.end() ) ) {
sendFileAction( ID, dirPath, filePath, Actions::Add );
if ( SanitizeEvents )
FilesAdded.insert( inode );
}
if ( flags & ModifiedFlags ) {
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
if ( ( flags & efswFSEventStreamEventFlagItemRemoved ) && !FileInfo::exists( path ) ) {
// Since i don't know the order, at least i try to keep the data consistent with the real
// state
sendFileAction( ID, dirPath, filePath, Actions::Delete );
if ( SanitizeEvents )
FilesAdded.erase( inode );
}
}
void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
size_t esize = events.size();
for ( size_t i = 0; i < esize; i++ ) {
FSEvent& event = events[i];
if ( event.Flags &
( kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped |
kFSEventStreamEventFlagMustScanSubDirs) ) {
efDEBUG( "Rescan/Drop event for watch: %s - flags: 0x%x\n", Directory.c_str(), event.Flags );
std::string dirPath = Directory;
FileSystem::dirRemoveSlashAtEnd( dirPath );
sendMissedFileActions(ID, dirPath );
continue;
}
if ( event.Flags &
( kFSEventStreamEventFlagEventIdsWrapped | kFSEventStreamEventFlagHistoryDone |
kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount |
kFSEventStreamEventFlagRootChanged ) ) {
continue;
}
if ( !Recursive ) {
/** In case that is not recursive the watcher, ignore the events from subfolders */
if ( event.Path.find_last_of( FileSystem::getOSSlash() ) != Directory.size() - 1 ) {
continue;
}
}
if ( FileWatcherFSEvents::isGranular() ) {
std::string dirPath( FileSystem::pathRemoveFileName( event.Path ) );
std::string filePath( FileSystem::fileNameFromPath( event.Path ) );
if ( event.Flags &
( efswFSEventStreamEventFlagItemCreated | efswFSEventStreamEventFlagItemRemoved |
efswFSEventStreamEventFlagItemRenamed ) ) {
if ( dirPath != Directory ) {
DirsChanged.insert( dirPath );
}
}
// This is a mess. But it's FSEvents faults, because shrinks events from the same file
// in one single event ( so there's no order for them ) For example a file could have
// been added modified and erased, but i can't know if first was erased and then added
// and modified, or added, then modified and then erased. I don't know what they were
// thinking by doing this...
efDEBUG( "Event in: %s - flags: 0x%x\n", event.Path.c_str(), event.Flags );
if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) {
if ( ( i + 1 < esize ) &&
( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) &&
( events[i + 1].inode == event.inode ) ) {
FSEvent& nEvent = events[i + 1];
std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) );
std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) );
if ( event.Path != nEvent.Path ) {
if ( dirPath == newDir ) {
if ( !FileInfo::exists( event.Path ) ||
0 == strcasecmp( event.Path.c_str(), nEvent.Path.c_str() ) ) {
sendFileAction( ID, dirPath, newFilepath, Actions::Moved,
filePath );
} else {
sendFileAction( ID, dirPath, filePath, Actions::Moved,
newFilepath );
}
} else {
sendFileAction( ID, dirPath, filePath, Actions::Delete );
sendFileAction( ID, newDir, newFilepath, Actions::Add );
if ( nEvent.Flags & ModifiedFlags ) {
sendFileAction( ID, newDir, newFilepath, Actions::Modified );
}
}
} else {
handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath, event.inode );
}
if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated |
efswFSEventStreamEventFlagItemRemoved |
efswFSEventStreamEventFlagItemRenamed ) ) {
if ( newDir != Directory ) {
DirsChanged.insert( newDir );
}
}
// Skip the renamed file
i++;
} else if ( FileInfo::exists( event.Path ) ) {
sendFileAction( ID, dirPath, filePath, Actions::Add );
if ( event.Flags & ModifiedFlags ) {
sendFileAction( ID, dirPath, filePath, Actions::Modified );
}
} else {
sendFileAction( ID, dirPath, filePath, Actions::Delete );
}
} else {
handleAddModDel( event.Flags, event.Path, dirPath, filePath, event.inode );
}
} else {
efDEBUG( "Directory: %s changed\n", event.Path.c_str() );
DirsChanged.insert( event.Path );
}
}
}
void WatcherFSEvents::process() {
std::unordered_set<std::string>::iterator it = DirsChanged.begin();
for ( ; it != DirsChanged.end(); it++ ) {
if ( !FileWatcherFSEvents::isGranular() ) {
WatcherGen->watchDir( ( *it ) );
} else {
sendFileAction( ID, FileSystem::pathRemoveFileName( ( *it ) ),
FileSystem::fileNameFromPath( ( *it ) ), Actions::Modified );
}
}
DirsChanged.clear();
}
} // namespace efsw
#endif