mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-21 06:45:39 -06:00
FSMon : moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@4930 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
be7b18a871
commit
fc1307e915
12 changed files with 1493 additions and 15 deletions
|
|
@ -1,4 +1,4 @@
|
|||
description "Bazaar: Docking example showing basic usage. Author: James Thomas (mrjt)";
|
||||
description "Bazaar: Docking example showing basic usage. Author: James Thomas (mrjt)\377";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
|
|
|
|||
190
bazaar/FSMon/FSMon.h
Normal file
190
bazaar/FSMon/FSMon.h
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#ifndef _FSMon_FSMon_h
|
||||
#define _FSMon_FSMon_h
|
||||
|
||||
#include <Core/Core.h>
|
||||
|
||||
#ifdef flagGUI
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
#endif
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
class FSMon
|
||||
{
|
||||
public:
|
||||
// flags stating type of changes
|
||||
typedef enum
|
||||
{
|
||||
FSM_NOP = 0x00,
|
||||
FSM_Created = 0x01,
|
||||
FSM_Deleted = 0x02,
|
||||
FSM_Moved = 0x04,
|
||||
FSM_FolderCreated = 0x08,
|
||||
FSM_FolderDeleted = 0x10,
|
||||
FSM_FolderMoved = 0x20,
|
||||
FSM_Modified = 0x40,
|
||||
FSM_AttribChange = 0x80
|
||||
} Flags;
|
||||
|
||||
struct Info : public Moveable<Info>
|
||||
{
|
||||
// path of changed file
|
||||
String path;
|
||||
|
||||
// new file path for renamed/moved files
|
||||
String newPath;
|
||||
|
||||
// type of change flags
|
||||
dword flags;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
|
||||
// Enable/Disable process privileges.
|
||||
BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable);
|
||||
|
||||
// check if path is a folder or a file
|
||||
bool isFolder(String const &path);
|
||||
|
||||
// struct containing info needed for ReadDirectoryChangesW
|
||||
#define READ_DIR_CHANGE_BUFFER_SIZE 8192
|
||||
struct CHANGESINFO
|
||||
{
|
||||
HANDLE hDir;
|
||||
byte buffer[READ_DIR_CHANGE_BUFFER_SIZE];
|
||||
OVERLAPPED overlapped;
|
||||
|
||||
bool cancelling;
|
||||
};
|
||||
Array<CHANGESINFO>monitoredInfo;
|
||||
|
||||
// error code
|
||||
DWORD errCode;
|
||||
|
||||
// get error string from code
|
||||
String GetErrorStr(HRESULT err);
|
||||
|
||||
// completion port for async I/O
|
||||
HANDLE completionPort;
|
||||
|
||||
// keys for watched folders -- need to be passed to completion port
|
||||
LONG lastDescriptor;
|
||||
|
||||
// scans result buffer for FILE_NOTIFY_INFORMATION records
|
||||
// and process them
|
||||
void ProcessNotify(FILE_NOTIFY_INFORMATION *buf, String const &path, bool second);
|
||||
|
||||
// monitored descriptors
|
||||
Index<LONG> monitoredDescriptors;
|
||||
|
||||
#else
|
||||
// error code
|
||||
int errCode;
|
||||
|
||||
// get error string from code
|
||||
String GetErrorStr(int err);
|
||||
|
||||
int iNotifyHandle;
|
||||
|
||||
// scans a newly created folder to look for files
|
||||
// being created BEFORE notify handler was in place
|
||||
// void ScanCreatedFolder(String path);
|
||||
|
||||
// recursively add or remove monitors for paths
|
||||
bool AddWatch(String const &path);
|
||||
bool RemoveWatch(String const &path);
|
||||
|
||||
// event handling selector
|
||||
void EventsSelector(uint32 mask, String const &path, String const &newPath);
|
||||
|
||||
// monitored descriptors
|
||||
Index<int> monitoredDescriptors;
|
||||
|
||||
#endif
|
||||
|
||||
// flag stating that we want to be notified only on file close
|
||||
// this spares many notifications and the caveats of working on
|
||||
// opened file, but don't work for files always opened
|
||||
// (example : big database files)
|
||||
bool notifyOnClose;
|
||||
|
||||
// error string
|
||||
String errMsg;
|
||||
|
||||
// array of errors paths and codes filled by
|
||||
// recursive watcher -- if can be useful to
|
||||
// allow some errors when watching
|
||||
VectorMap<String, int> errMap;
|
||||
|
||||
// mutex for thread locking
|
||||
Mutex fsmMutex;
|
||||
|
||||
// a secondary mutex, used for locking on folder creation
|
||||
// this one avoids waiting when not needed
|
||||
Mutex fsmMutex2;
|
||||
|
||||
// the checking thread
|
||||
volatile bool threadRunning;
|
||||
Thread fsmThread;
|
||||
|
||||
// changed files/folders list
|
||||
Vector<Info> changed;
|
||||
|
||||
// monitored paths
|
||||
Index<String> monitoredPaths;
|
||||
|
||||
// actually opened files -- may be handy
|
||||
// for a sync application and for locking purposes
|
||||
Index<String>openedFiles;
|
||||
|
||||
// sets error code message from errno
|
||||
void SetError(int err) { errCode = err; errMsg = GetErrorStr(err); }
|
||||
|
||||
// monitoring callback (runs in a separate thread)
|
||||
volatile bool shutDown;
|
||||
void monitorCb(void);
|
||||
|
||||
// callback to call event handler in maint thread
|
||||
// (via PostCallback) when using GUI
|
||||
void callHandlerCb(void);
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
|
||||
typedef FSMon CLASSNAME;
|
||||
|
||||
// constructor
|
||||
FSMon(bool notifyOnClose = false);
|
||||
|
||||
// destructor
|
||||
~FSMon();
|
||||
|
||||
// add a monitored path
|
||||
bool Add(String const &path);
|
||||
|
||||
// remove a monitored path
|
||||
bool Remove(String const &path);
|
||||
|
||||
// query for changed files/folders
|
||||
Vector<Info> GetChanged(void);
|
||||
|
||||
// gets actually opened files
|
||||
Index<String>GetOpenedFiles(void);
|
||||
|
||||
// check error contidion and get error message
|
||||
bool IsError(void) { return errCode != 0; }
|
||||
bool GetErrorCode(void) { return errCode; }
|
||||
String GetErrorMsg(void) { return errMsg; }
|
||||
VectorMap<String, int> GetErrorMap(void);
|
||||
|
||||
// callback to signal happened event
|
||||
// avoids polling, if needed
|
||||
Callback EventHandler;
|
||||
};
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
|
||||
#endif
|
||||
7
bazaar/FSMon/FSMon.upp
Normal file
7
bazaar/FSMon/FSMon.upp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
description "Filesystem Monitor Package\377";
|
||||
|
||||
file
|
||||
FSMon.h,
|
||||
FSMonPosix.cpp,
|
||||
FSMonWin.cpp;
|
||||
|
||||
541
bazaar/FSMon/FSMonPosix.cpp
Normal file
541
bazaar/FSMon/FSMonPosix.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
#include "FSMon.h"
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
// code for POSIX platform
|
||||
#ifdef PLATFORM_POSIX
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
// sets error code message from errno
|
||||
String FSMon::GetErrorStr(int err)
|
||||
{
|
||||
return strerror(err);
|
||||
}
|
||||
|
||||
|
||||
// constructor
|
||||
FSMon::FSMon(bool nOnClose)
|
||||
{
|
||||
errCode = 0;
|
||||
errMsg = "";
|
||||
|
||||
notifyOnClose = nOnClose;
|
||||
|
||||
iNotifyHandle = inotify_init1(IN_NONBLOCK);
|
||||
|
||||
if(iNotifyHandle == -1)
|
||||
{
|
||||
SetError(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// start monitor thread
|
||||
shutDown = false;
|
||||
fsmThread.Start(THISBACK(monitorCb));
|
||||
}
|
||||
|
||||
// destructor
|
||||
FSMon::~FSMon()
|
||||
{
|
||||
// stops monitoring thread
|
||||
shutDown = true;
|
||||
while(shutDown)
|
||||
;
|
||||
|
||||
if(iNotifyHandle >= 0)
|
||||
{
|
||||
// close notify handle
|
||||
// that should un-monitor all paths...
|
||||
close(iNotifyHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
struct inotify_event {
|
||||
int wd; // Watch descriptor
|
||||
uint32_t mask; // Mask of events
|
||||
uint32_t cookie; // Unique cookie associating related events (for rename(2))
|
||||
uint32_t len; // Size of name field
|
||||
char name[]; // Optional null-terminated name
|
||||
};
|
||||
*/
|
||||
|
||||
// scans a newly created folder to look for files
|
||||
// being created BEFORE notify handler was in place
|
||||
/*
|
||||
void FSMon::ScanCreatedFolder(String path)
|
||||
{
|
||||
FindFile ff(AppendFileName(path, "*"));
|
||||
while(ff)
|
||||
{
|
||||
if(ff.IsFolder())
|
||||
ScanCreatedFolder(ff.GetPath());
|
||||
else
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = FSM_Created;
|
||||
info.path = ff.GetPath();
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
}
|
||||
ff.Next();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// callback to call event handler in maint thread
|
||||
// (via PostCallback) when using GUI
|
||||
void FSMon::callHandlerCb(void)
|
||||
{
|
||||
#ifdef flagGUI
|
||||
PostCallback(EventHandler);
|
||||
#else
|
||||
EventHandler();
|
||||
#endif
|
||||
}
|
||||
|
||||
// event handling selector
|
||||
void FSMon::EventsSelector(uint32 mask, String const &path, String const &newPath)
|
||||
{
|
||||
int iMask = 0;
|
||||
|
||||
// flag stating event related to folder, not file
|
||||
bool isFolder = mask & IN_ISDIR;
|
||||
|
||||
// mask field is a bitmask with OR-ed values, so we can't use a switch
|
||||
// we just check each value in a given order
|
||||
if(mask & IN_CLOSE_WRITE)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
// remove file from opened list
|
||||
int iOpened = openedFiles.Find(path);
|
||||
if(iOpened >= 0)
|
||||
openedFiles.Remove(iOpened);
|
||||
|
||||
// we propagate IN_CLOSE_WRITE event only if
|
||||
// we want to be notified just after file closing
|
||||
// otherwise, the change is always propagated by IN_MODIFY
|
||||
if(notifyOnClose)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = FSM_Modified;
|
||||
info.path = path;
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_CLOSE_NOWRITE)
|
||||
{
|
||||
// just update the opened files list
|
||||
int iOpened = openedFiles.Find(path);
|
||||
if(iOpened >= 0)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
openedFiles.Remove(iOpened);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_OPEN)
|
||||
{
|
||||
// just update the opened files list
|
||||
if(openedFiles.Find(path) < 0)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
openedFiles.Add(path);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_CREATE)
|
||||
{
|
||||
// signal file/path creation
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = (isFolder ? FSM_FolderCreated : FSM_Created);
|
||||
info.path = path;
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
// if a folder was created, we shall first setup a monitor
|
||||
// in it, then ensure that in the meanwhile no subitems have been created
|
||||
if(isFolder)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex2){
|
||||
AddWatch(path);
|
||||
}
|
||||
// ScanCreatedFolder(path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_DELETE)
|
||||
{
|
||||
// signal file removal
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = isFolder ? FSM_FolderDeleted : FSM_Deleted;
|
||||
info.path = path;
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
// for folders, we shall de-monitor all contained ones
|
||||
if(isFolder)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
for(int iFolder = monitoredPaths.GetCount() - 1; iFolder >= 0; iFolder--)
|
||||
{
|
||||
if(path.EndsWith(monitoredPaths[iFolder]))
|
||||
{
|
||||
monitoredPaths.Pop();
|
||||
int desc = monitoredDescriptors.Pop();
|
||||
inotify_rm_watch(iNotifyHandle, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_MODIFY)
|
||||
{
|
||||
// if we want just notifies on close, do nothing
|
||||
if(!notifyOnClose)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = FSM_Modified;
|
||||
info.path = path;
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// following one was pre-handled by thread to differentiate
|
||||
// between true moves inside monitored folders or create/delete
|
||||
// if coming/going outside
|
||||
if(mask & IN_MOVE)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = isFolder ? FSM_FolderMoved : FSM_Moved;
|
||||
info.path = path;
|
||||
info.newPath = newPath;
|
||||
callHandlerCb();
|
||||
|
||||
// if we moved a folder, we shall update stored info
|
||||
// with new ones -- we assume that monitors will stay in place...
|
||||
if(isFolder)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
Index<String> paths;
|
||||
for(int i = 0; i < monitoredPaths.GetCount(); i++)
|
||||
{
|
||||
String oldPth = monitoredPaths[i];
|
||||
if(oldPth.StartsWith(path))
|
||||
oldPth = newPath + oldPth.Mid(path.GetCount());
|
||||
paths.Add(oldPth);
|
||||
}
|
||||
monitoredPaths = paths;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(mask & IN_ATTRIB)
|
||||
{
|
||||
// add the attribute-modify event
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
changed.Add();
|
||||
Info &info = changed.Top();
|
||||
info.flags = FSM_AttribChange;
|
||||
info.path = path;
|
||||
info.newPath.Clear();
|
||||
callHandlerCb();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// monitoring callback (runs in a separate thread)
|
||||
void FSMon::monitorCb(void)
|
||||
{
|
||||
const size_t BASE_BUFSIZE = offsetof(struct inotify_event, name);
|
||||
|
||||
byte *bigBuf;
|
||||
while(!shutDown)
|
||||
{
|
||||
// check if data is available from inotify
|
||||
size_t nBytes;
|
||||
if(ioctl(iNotifyHandle, FIONREAD, &nBytes) < 0)
|
||||
{
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
if(nBytes == 0 || (int)nBytes == -1)
|
||||
{
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
// buffer can be filled by many events at once; the variable-lenght
|
||||
// name overcomplicates stuffs, as usual
|
||||
// we can't also read partial data, because read returns error if buffer is too small
|
||||
// so, we have to read full available data and split records later
|
||||
bigBuf = (byte *)malloc(nBytes);
|
||||
size_t res = read(iNotifyHandle, bigBuf, nBytes);
|
||||
if(res != nBytes)
|
||||
{
|
||||
free(bigBuf);
|
||||
Sleep(100);
|
||||
continue;
|
||||
}
|
||||
struct inotify_event *buf = (struct inotify_event *)bigBuf;
|
||||
while((byte *)buf - bigBuf < (int)nBytes)
|
||||
{
|
||||
int wd = buf->wd;
|
||||
uint32_t mask = buf->mask;
|
||||
String name = (buf->len ? buf->name : "");
|
||||
buf = (struct inotify_event *)((byte *)buf + BASE_BUFSIZE + buf->len);
|
||||
|
||||
// skip ignored events
|
||||
if(mask & IN_IGNORED)
|
||||
continue;
|
||||
|
||||
// get path from descriptor
|
||||
int idx = monitoredDescriptors.Find(wd);
|
||||
if(idx < 0)
|
||||
continue;
|
||||
String path = AppendFileName(monitoredPaths[idx], name);
|
||||
|
||||
// special handling for IN_MOVED events
|
||||
// an IN_MOVED_FROM must be followed by an IN_MOVE__TO
|
||||
// if moving between monitored folders. Well, I hope nothing
|
||||
// happens between them..... So, if we don't find the IN_MOVED_TO
|
||||
// we assume it's a move outside monitored folders, so a IN_DELETE
|
||||
// OTOH, if we find an IN_MOVED_TO without corresponding IN_MOVED_FROM
|
||||
// we assume e move from outside, so an IN_CREATE
|
||||
if(mask & IN_MOVED_FROM && (byte *)buf - bigBuf < (int)nBytes && buf->mask & IN_MOVED_TO)
|
||||
{
|
||||
// it's a true move
|
||||
String newName = (buf->len ? buf->name : "");
|
||||
EventsSelector(IN_MOVE | (mask & IN_ISDIR ? IN_ISDIR : 0), path, AppendFileName(monitoredPaths[idx], newName));
|
||||
}
|
||||
else if(mask & IN_MOVED_FROM)
|
||||
{
|
||||
// it's a delete
|
||||
EventsSelector(IN_DELETE | (mask & IN_ISDIR ? IN_ISDIR : 0), path, "");
|
||||
}
|
||||
else if(mask & IN_MOVED_TO)
|
||||
{
|
||||
// it's a create
|
||||
EventsSelector(IN_CREATE | (mask & IN_ISDIR ? IN_ISDIR : 0), path, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal event handling
|
||||
EventsSelector(mask, path, "");
|
||||
}
|
||||
}
|
||||
free(bigBuf);
|
||||
}
|
||||
shutDown = false;
|
||||
}
|
||||
|
||||
/* inofify MASKS
|
||||
IN_ACCESS File was accessed (read) (*).
|
||||
IN_ATTRIB Metadata changed, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*).
|
||||
IN_CLOSE_WRITE File opened for writing was closed (*).
|
||||
IN_CLOSE_NOWRITE File not opened for writing was closed (*).
|
||||
IN_CREATE File/directory created in watched directory (*).
|
||||
IN_DELETE File/directory deleted from watched directory (*).
|
||||
IN_DELETE_SELF Watched file/directory was itself deleted.
|
||||
IN_MODIFY File was modified (*).
|
||||
IN_MOVE_SELF Watched file/directory was itself moved.
|
||||
IN_MOVED_FROM File moved out of watched directory (*).
|
||||
IN_MOVED_TO File moved into watched directory (*).
|
||||
IN_OPEN File was opened (*).
|
||||
*/
|
||||
// recursively add or remove monitors for paths
|
||||
// try to monitor all he can, even if there are some errors
|
||||
// returns false if some error is found
|
||||
bool FSMon::AddWatch(String const &path)
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
// add a monitor to current path if not already there
|
||||
if(monitoredPaths.Find(path) >= 0)
|
||||
return true;
|
||||
|
||||
int desc = inotify_add_watch(
|
||||
iNotifyHandle,
|
||||
path,
|
||||
IN_ATTRIB |
|
||||
IN_OPEN | // this one just to see if file is busy
|
||||
IN_CLOSE_WRITE |
|
||||
IN_CLOSE_NOWRITE |
|
||||
IN_CREATE |
|
||||
IN_DELETE |
|
||||
// IN_DELETE_SELF | // we handle this one in IN_DELETE anyways
|
||||
IN_MODIFY |
|
||||
// IN_MOVE_SELF | // we handle this one in IN_MOVE anyways
|
||||
IN_MOVED_FROM | // without IN_MOVE_TO, is a DELETE from watched folders
|
||||
IN_MOVED_TO
|
||||
);
|
||||
|
||||
// error ?
|
||||
if(desc < 0)
|
||||
{
|
||||
errMap.Add(path, errno);
|
||||
SetError(errno);
|
||||
res = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
monitoredPaths.Add(path);
|
||||
monitoredDescriptors.Add(desc);
|
||||
}
|
||||
|
||||
// look for all subfolders
|
||||
FindFile ff(AppendFileName(path, "*"));
|
||||
while(ff)
|
||||
{
|
||||
if(ff.IsFolder())
|
||||
res &= AddWatch(ff.GetPath());
|
||||
ff.Next();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool FSMon::RemoveWatch(String const &path)
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
int pDescIdx = monitoredPaths.Find(path);
|
||||
if(pDescIdx >= 0)
|
||||
{
|
||||
if(inotify_rm_watch(iNotifyHandle, monitoredDescriptors[pDescIdx]) < 0)
|
||||
{
|
||||
errMap.Add(path, errno);
|
||||
SetError(errno);
|
||||
res = false;
|
||||
}
|
||||
// look for all subfolders
|
||||
FindFile ff(AppendFileName(path, "*"));
|
||||
while(ff)
|
||||
{
|
||||
if(ff.IsFolder())
|
||||
res &= RemoveWatch(ff.GetPath());
|
||||
ff.Next();
|
||||
}
|
||||
|
||||
// remove from list of monitored paths
|
||||
monitoredPaths.Remove(pDescIdx);
|
||||
monitoredDescriptors.Remove(pDescIdx);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// add a monitored path
|
||||
bool FSMon::Add(String const &path)
|
||||
{
|
||||
if(IsError())
|
||||
return false;
|
||||
|
||||
// check wether folder exists
|
||||
if(!DirectoryExists(path))
|
||||
return false;
|
||||
|
||||
// clears map of errors
|
||||
errMap.Clear();
|
||||
|
||||
// we don't want monitor again an already monitored path
|
||||
// so we do a quick check about it
|
||||
if(monitoredPaths.Find(path) >= 0)
|
||||
return true;
|
||||
|
||||
// if we're monitoring an external path, we should unmonitor
|
||||
// it and remonitor the external one; as doing so we could loose
|
||||
// events, we just monitor the external path, that do not harm
|
||||
return AddWatch(path);
|
||||
}
|
||||
|
||||
// remove a monitored path
|
||||
bool FSMon::Remove(String const &path)
|
||||
{
|
||||
if(IsError())
|
||||
return false;
|
||||
|
||||
// check wether folder exists
|
||||
// if not, return success anyways
|
||||
if(!DirectoryExists(path))
|
||||
return true;
|
||||
|
||||
// clears map of errors
|
||||
errMap.Clear();
|
||||
|
||||
// do the recursive un-monitoring
|
||||
return RemoveWatch(path);
|
||||
|
||||
}
|
||||
|
||||
// query for changed files/folders
|
||||
Vector<FSMon::Info> FSMon::GetChanged(void)
|
||||
{
|
||||
Vector<Info> info;
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
info = changed;
|
||||
changed.Clear();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// gets actually opened files
|
||||
Index<String> FSMon::GetOpenedFiles(void)
|
||||
{
|
||||
Index<String> of;
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
of <<= openedFiles;
|
||||
}
|
||||
return of;
|
||||
}
|
||||
|
||||
VectorMap<String, int> FSMon::GetErrorMap(void)
|
||||
{
|
||||
VectorMap<String, int> res = errMap;
|
||||
errMap.Clear();
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
561
bazaar/FSMon/FSMonWin.cpp
Normal file
561
bazaar/FSMon/FSMonWin.cpp
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
#include "FSMon.h"
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
|
||||
// Enable/Disable process privileges.
|
||||
BOOL FSMon::EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable)
|
||||
{
|
||||
// Assume function fails
|
||||
BOOL fOk = FALSE;
|
||||
|
||||
// Try to open this process's access token
|
||||
HANDLE hToken;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
|
||||
{
|
||||
// privilege
|
||||
TOKEN_PRIVILEGES tp = { 1 };
|
||||
|
||||
if (LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid))
|
||||
{
|
||||
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
|
||||
fOk = (GetLastError() == ERROR_SUCCESS);
|
||||
}
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
return(fOk);
|
||||
}
|
||||
|
||||
// check if path is a folder or a file
|
||||
bool FSMon::isFolder(String const &path)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributes(path);
|
||||
return static_cast<bool>((dwAttrib != 0xffffffff && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)));
|
||||
}
|
||||
|
||||
// constructor
|
||||
FSMon::FSMon(bool nOnClose)
|
||||
{
|
||||
// must create completion port on first use
|
||||
completionPort = NULL;
|
||||
|
||||
// reset completion keys generator
|
||||
// skip 0 descriptor, we never know if 0 is returned...
|
||||
lastDescriptor = 1;
|
||||
|
||||
// those are needed for ReadDirectoryChangesW
|
||||
EnablePrivilege(SE_BACKUP_NAME, true);
|
||||
EnablePrivilege(SE_RESTORE_NAME, true);
|
||||
EnablePrivilege(SE_CHANGE_NOTIFY_NAME, true);
|
||||
|
||||
shutDown = false;
|
||||
threadRunning = false;
|
||||
}
|
||||
|
||||
// destructor
|
||||
FSMon::~FSMon()
|
||||
{
|
||||
// if thread not started, no need to shutdown
|
||||
if(threadRunning)
|
||||
{
|
||||
// cancels all pending IO operations
|
||||
// can't use the CancelIoEx (available from Vista+)
|
||||
// so we first cancel IO for current thread
|
||||
// then we send a packet that causes worker thread to
|
||||
// cancel its part
|
||||
for(int i = 0; i < monitoredInfo.GetCount(); i++)
|
||||
{
|
||||
CancelIo(monitoredInfo[i].hDir);
|
||||
// no way to pass valid parameters with PostQueuedCompletionStatus(),
|
||||
// so we use a 'cancelling' flag inside Info recors
|
||||
INTERLOCKED_(fsmMutex2){
|
||||
monitoredInfo[i].cancelling = true;
|
||||
}
|
||||
PostQueuedCompletionStatus(completionPort, 0, monitoredDescriptors[i], NULL);
|
||||
while(true)
|
||||
{
|
||||
bool cancelling;
|
||||
INTERLOCKED_(fsmMutex2){
|
||||
cancelling = monitoredInfo[i].cancelling;
|
||||
}
|
||||
if(!cancelling)
|
||||
break;
|
||||
else
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
// wait some time just to allow thread to cancel all pending I/O
|
||||
Sleep(10);
|
||||
|
||||
// signal thread shutdown
|
||||
shutDown = true;
|
||||
|
||||
// send a completion packet to completion port
|
||||
// in order make it return
|
||||
PostQueuedCompletionStatus(completionPort, 0, 0, NULL);
|
||||
|
||||
while(shutDown)
|
||||
;
|
||||
threadRunning = false;
|
||||
}
|
||||
|
||||
// close handles and completion port
|
||||
for(int i = 0; i < monitoredInfo.GetCount(); i++)
|
||||
CloseHandle(monitoredInfo[i].hDir);
|
||||
CloseHandle(completionPort);
|
||||
}
|
||||
|
||||
// get error string from code
|
||||
String FSMon::GetErrorStr(HRESULT err)
|
||||
{
|
||||
LPTSTR errorText;
|
||||
String res;
|
||||
|
||||
FormatMessage(
|
||||
// use system message tables to retrieve error text
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
// allocate buffer on local heap for error text
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
// Important! will fail otherwise, since we're not
|
||||
// (and CANNOT) pass insertion parameters
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM
|
||||
err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&errorText, // output
|
||||
0, // minimum size for output buffer
|
||||
NULL); // arguments - see note
|
||||
|
||||
if(errorText)
|
||||
{
|
||||
res = errorText;
|
||||
LocalFree(errorText);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// add a monitored path
|
||||
bool FSMon::Add(String const &path)
|
||||
{
|
||||
bool res = true;
|
||||
for(int iType = 0; iType < 2; iType++)
|
||||
{
|
||||
//open the directory to watch
|
||||
HANDLE hDir = CreateFile(
|
||||
path,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE ,//| FILE_SHARE_DELETE, <-- removing FILE_SHARE_DELETE prevents the user or someone else from renaming or deleting the watched directory. This is a good thing to prevent.
|
||||
NULL, //security attributes
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | //<- the required priviliges for this flag are: SE_BACKUP_NAME and SE_RESTORE_NAME. CPrivilegeEnabler takes care of that.
|
||||
FILE_FLAG_OVERLAPPED, // for async mode
|
||||
NULL
|
||||
);
|
||||
|
||||
if (hDir == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
//create a IO completion port/or associate this key with
|
||||
//the existing IO completion port
|
||||
completionPort = CreateIoCompletionPort(
|
||||
hDir,
|
||||
// if completionPort is NULL, hDir is associated with a NEW completion port,
|
||||
// if completionPort is NON-NULL, hDir is associated with the existing completion
|
||||
// port that the handle completionPort references
|
||||
completionPort,
|
||||
// the completion 'key'... this ptr is returned from GetQueuedCompletionStatus() when one of the events in the dwChangesToWatchFor filter takes place
|
||||
lastDescriptor,
|
||||
0
|
||||
);
|
||||
if(!completionPort)
|
||||
{
|
||||
SetError(GetLastError());
|
||||
CloseHandle(hDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
// completion port created, we can now store monitored path and its key
|
||||
// and update key generator
|
||||
|
||||
// must lock here to avoid working threat to access
|
||||
// removed stuffs
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
monitoredPaths.Add(path);
|
||||
monitoredDescriptors.Add(lastDescriptor++);
|
||||
monitoredInfo.Add();
|
||||
}
|
||||
|
||||
CHANGESINFO &info = monitoredInfo.Top();
|
||||
info.hDir = hDir;
|
||||
info.cancelling = false;
|
||||
|
||||
// overlapped MUST be zero filled, otherwise ReadDirectoryChangesW fails
|
||||
memset(&info.overlapped, 0, sizeof(OVERLAPPED));
|
||||
|
||||
// an now, if not already done, we must start working thread
|
||||
if(!threadRunning)
|
||||
{
|
||||
shutDown = false;
|
||||
fsmThread.Start(THISBACK(monitorCb));
|
||||
threadRunning = true;
|
||||
}
|
||||
|
||||
// thread is started, we shall add the monitored folder with ReadDirectoryChangesW
|
||||
DWORD bufLen;
|
||||
if (!ReadDirectoryChangesW(
|
||||
info.hDir,
|
||||
info.buffer,
|
||||
READ_DIR_CHANGE_BUFFER_SIZE,
|
||||
true, // we want subdirs events
|
||||
iType ?
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SECURITY |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME
|
||||
:
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
&bufLen, //this var not set when using asynchronous mechanisms...
|
||||
&info.overlapped,
|
||||
NULL)) //no completion routine!
|
||||
{
|
||||
SetError(GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// just in case of 1 of 2 calls went bad... we must have an EVEN
|
||||
// numbero of elements in arrays
|
||||
if(monitoredPaths.GetCount() & 1)
|
||||
{
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
monitoredPaths.Drop();
|
||||
monitoredDescriptors.Drop();
|
||||
monitoredInfo.Drop();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FSMon::Remove(String const &path)
|
||||
{
|
||||
int idx = monitoredPaths.Find(path);
|
||||
if(idx < 0)
|
||||
return false;
|
||||
idx &= 0xFFFFFFFFFFFFFFFE;
|
||||
|
||||
for(int iType = 0; iType < 2; iType++)
|
||||
{
|
||||
CHANGESINFO &info = monitoredInfo[idx + iType];
|
||||
|
||||
// camcels io for corresponding path
|
||||
// as usual, we can't use CancelIoEx because we want it working on XP
|
||||
// so we cancel io in current thread and fake the behaviour
|
||||
// sending a cancel packet to worker thread
|
||||
|
||||
// no way to pass valid parameters with PostQueuedCompletionStatus(),
|
||||
// so we use a 'cancelling' flag inside Info recors
|
||||
INTERLOCKED_(fsmMutex2) {
|
||||
info.cancelling = true;
|
||||
}
|
||||
PostQueuedCompletionStatus(completionPort, 0, monitoredDescriptors[idx + iType], NULL);
|
||||
CancelIo(info.hDir);
|
||||
|
||||
// wait for tread get the cancelling packet and reset the flag...
|
||||
while(true)
|
||||
{
|
||||
bool cancelling;
|
||||
INTERLOCKED_(fsmMutex2){
|
||||
cancelling = info.cancelling;
|
||||
}
|
||||
if(!cancelling)
|
||||
break;
|
||||
else
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
// free dir handle
|
||||
CloseHandle(info.hDir);
|
||||
|
||||
}
|
||||
// must lock here to avoid working thread to access
|
||||
// removed stuffs
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
monitoredPaths.Remove(idx, 2);
|
||||
monitoredInfo.Remove(idx, 2);
|
||||
monitoredDescriptors.Remove(idx, 2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// scans result buffer for FILE_NOTIFY_INFORMATION records
|
||||
// and process them
|
||||
/*
|
||||
typedef struct _FILE_NOTIFY_INFORMATION {
|
||||
DWORD NextEntryOffset;
|
||||
DWORD Action;
|
||||
DWORD FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
|
||||
|
||||
To separate the events from originating flags, we need a couple of listeners
|
||||
Flags and corresponding catched events :
|
||||
|
||||
LISTENER 1
|
||||
INPUT FLAG CATCHED EVENTS MAPPED TO
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME FILE_ACTION_ADDED FSM_Created
|
||||
FILE_ACTION_REMOVED FSM_Deleted
|
||||
FILE_ACTION_RENAMED_OLD_NAME FSM_Moved
|
||||
FILE_ACTION_RENAMED_NEW_NAME
|
||||
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME FILE_ACTION_ADDED FSM_FolderCreated
|
||||
FILE_ACTION_REMOVED FSM_FolderDeleted
|
||||
FILE_ACTION_RENAMED_OLD_NAME FSM_Moved
|
||||
FILE_ACTION_RENAMED_NEW_NAME
|
||||
|
||||
FILE_NOTIFY_CHANGE_SIZE FILE_ACTION_MODIFIED FSM_Modified
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE FILE_ACTION_MODIFIED FSM_Modified
|
||||
|
||||
LISTENER 2
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES FILE_ACTION_MODIFIED FSM_AttribChange
|
||||
FILE_NOTIFY_CHANGE_SECURITY FILE_ACTION_MODIFIED FSM_AttribChange
|
||||
FILE_NOTIFY_CHANGE_LAST_ACCESS FILE_ACTION_MODIFIED NOT CATCHED - USELESS
|
||||
FILE_NOTIFY_CHANGE_CREATION FILE_ACTION_MODIFIED NOT CATCHED - USELESS
|
||||
|
||||
*/
|
||||
void FSMon::ProcessNotify(FILE_NOTIFY_INFORMATION *buf, String const &path, bool second)
|
||||
{
|
||||
do
|
||||
{
|
||||
switch(buf->Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED :
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
Info &info = changed.Add();
|
||||
info.flags = second ? FSM_FolderCreated : FSM_Created;
|
||||
WString ws = WString(buf->FileName, buf->FileNameLength / sizeof(WCHAR));
|
||||
info.path = AppendFileName(path, ws.ToString());
|
||||
}
|
||||
callHandlerCb();
|
||||
break;
|
||||
case FILE_ACTION_REMOVED :
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
Info &info = changed.Add();
|
||||
info.flags = second ? FSM_FolderDeleted : FSM_Deleted;
|
||||
WString ws = WString(buf->FileName, buf->FileNameLength / sizeof(WCHAR));
|
||||
info.path = AppendFileName(path, ws.ToString());
|
||||
}
|
||||
callHandlerCb();
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED :
|
||||
{
|
||||
// skip changes on folders... wathever they can mean
|
||||
WString ws = WString(buf->FileName, buf->FileNameLength / sizeof(WCHAR));
|
||||
String p = AppendFileName(path, ws.ToString());
|
||||
if(isFolder(p) && !second)
|
||||
break;
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
Info &info = changed.Add();
|
||||
info.flags = second ? FSM_AttribChange : FSM_Modified;
|
||||
info.path = p;
|
||||
}
|
||||
callHandlerCb();
|
||||
}
|
||||
break;
|
||||
case FILE_ACTION_RENAMED_OLD_NAME :
|
||||
{
|
||||
WString ws = WString(buf->FileName, buf->FileNameLength / sizeof(WCHAR));
|
||||
String oldPath = path + ws.ToString();
|
||||
if(!buf->NextEntryOffset)
|
||||
break;
|
||||
buf = (FILE_NOTIFY_INFORMATION *)((byte *)buf + buf->NextEntryOffset);
|
||||
if(buf->Action != FILE_ACTION_RENAMED_NEW_NAME)
|
||||
{
|
||||
break;
|
||||
}
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
Info &info = changed.Add();
|
||||
info.flags = second ? FSM_FolderMoved : FSM_Moved;
|
||||
info.path = oldPath;
|
||||
ws = WString(buf->FileName, buf->FileNameLength / sizeof(WCHAR));
|
||||
info.newPath = AppendFileName(path, ws.ToString());
|
||||
}
|
||||
callHandlerCb();
|
||||
break;
|
||||
}
|
||||
case FILE_ACTION_RENAMED_NEW_NAME :
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
// go to next record
|
||||
buf = (FILE_NOTIFY_INFORMATION *)((byte *)buf + buf->NextEntryOffset);
|
||||
}
|
||||
while(buf->NextEntryOffset);
|
||||
}
|
||||
|
||||
// monitoring callback (runs in a separate thread)
|
||||
void FSMon::monitorCb(void)
|
||||
{
|
||||
DWORD numBytes;
|
||||
LONG descriptor;
|
||||
LPOVERLAPPED overlapped;
|
||||
|
||||
// completion port is ok, start monitoring it
|
||||
while(!shutDown)
|
||||
{
|
||||
// Retrieve the directory info for this directory
|
||||
// through the io port's completion key
|
||||
if(!GetQueuedCompletionStatus(
|
||||
completionPort,
|
||||
&numBytes,
|
||||
(ULONG *)&descriptor,
|
||||
&overlapped,
|
||||
INFINITE // remember to send a completion packet to shut down thread !!
|
||||
))
|
||||
{
|
||||
// error state, if 'overlapped' is NULL, the call timed out
|
||||
// with no event, otherwise we got some error
|
||||
if(!overlapped)
|
||||
{
|
||||
// this would mean timeout, but shouldn't happen
|
||||
// even if called with a null overlapped, it sets up one
|
||||
// so this can't be used for cancel packets
|
||||
// we use numBytes parameter, using value of 1 which should
|
||||
// be never valid
|
||||
}
|
||||
else
|
||||
{
|
||||
// here we land on errors or on CancelIo operations
|
||||
int err = GetLastError();
|
||||
if(err == ERROR_OPERATION_ABORTED)
|
||||
{
|
||||
// we land here if a CancelIo was called on this handle
|
||||
// just do nothing... we don't have to respawn the I/O
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
int idx = monitoredDescriptors.Find(descriptor);
|
||||
if(idx >= 0)
|
||||
CancelIo(monitoredInfo[idx].hDir);
|
||||
}
|
||||
}
|
||||
else
|
||||
SetError(err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// just quick exit if shutting down
|
||||
if(shutDown)
|
||||
break;
|
||||
|
||||
// get path data from descriptor
|
||||
// we use a second mutex here, it just lock adding/removing paths
|
||||
INTERLOCKED_(fsmMutex2)
|
||||
{
|
||||
int idx = monitoredDescriptors.Find(descriptor);
|
||||
if(idx >= 0)
|
||||
{
|
||||
// check flag if we wanna cancel...
|
||||
CHANGESINFO &info = monitoredInfo[idx];
|
||||
if(info.cancelling)
|
||||
{
|
||||
CancelIo(info.hDir);
|
||||
info.cancelling = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
String path = monitoredPaths[idx];
|
||||
|
||||
// we got an event, just parse it and fill received event structures
|
||||
ProcessNotify((FILE_NOTIFY_INFORMATION *)info.buffer, path, (idx & 1));
|
||||
|
||||
// differentiate between the 2 handlers depending on index
|
||||
// EVEN indexes : CREATE/DELETE/RENAME FOR FILES, MODIFY FOR ALL
|
||||
// ODD indexes : CREATE/DELETE/RENAME FOR FOLDERS, ATTRIBUTES FOR ALL
|
||||
|
||||
// re-post the request, we don't want to loose events here....
|
||||
DWORD bufLen;
|
||||
if (!ReadDirectoryChangesW(
|
||||
info.hDir,
|
||||
info.buffer,
|
||||
READ_DIR_CHANGE_BUFFER_SIZE,
|
||||
true, // we want subdirs events
|
||||
(idx & 1) ?
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SECURITY |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME
|
||||
:
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
&bufLen, //this var not set when using asynchronous mechanisms...
|
||||
&info.overlapped,
|
||||
NULL)) //no completion routine!
|
||||
SetError(GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// reset shutdown flag to signal tread exiting
|
||||
shutDown = false;
|
||||
}
|
||||
|
||||
// callback to call event handler in maint thread
|
||||
// (via PostCallback) when using GUI
|
||||
void FSMon::callHandlerCb(void)
|
||||
{
|
||||
#ifdef flagGUI
|
||||
if(EventHandler)
|
||||
PostCallback(EventHandler);
|
||||
#else
|
||||
EventHandler();
|
||||
#endif
|
||||
}
|
||||
|
||||
// query for changed files/folders
|
||||
Vector<FSMon::Info> FSMon::GetChanged(void)
|
||||
{
|
||||
Vector<Info> info;
|
||||
INTERLOCKED_(fsmMutex)
|
||||
{
|
||||
info = changed;
|
||||
changed.Clear();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// gets actually opened files
|
||||
Index<String> FSMon::GetOpenedFiles(void)
|
||||
{
|
||||
// no way on windows to catch open/close on files
|
||||
// without a dedicated filter
|
||||
return Index<String>();
|
||||
}
|
||||
|
||||
// check error contidion and get error message
|
||||
VectorMap<String, int> FSMon::GetErrorMap(void)
|
||||
{
|
||||
// just return last error -- on windows we can't have
|
||||
// sequences of errors
|
||||
VectorMap<String, int> res;
|
||||
res.Add("", (int)errCode);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
3
bazaar/FSMon/init
Normal file
3
bazaar/FSMon/init
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef _FSMon_icpp_init_stub
|
||||
#define _FSMon_icpp_init_stub
|
||||
#endif
|
||||
33
bazaar/FSMonTest/FSMonTest.h
Normal file
33
bazaar/FSMonTest/FSMonTest.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef _FSMonTest_FSMonTest_h
|
||||
#define _FSMonTest_FSMonTest_h
|
||||
|
||||
#include <FSMon/FSMon.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
#define LAYOUTFILE <FSMonTest/FSMonTest.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
|
||||
|
||||
class FSMonTest : public WithFSMonTestLayout<TopWindow>
|
||||
{
|
||||
private:
|
||||
void Log(String const &s);
|
||||
|
||||
void startCb(void);
|
||||
void stopCb(void);
|
||||
void quitCb(void);
|
||||
|
||||
FSMon fsmMon;
|
||||
|
||||
void monitorCb(void);
|
||||
|
||||
protected:
|
||||
public:
|
||||
typedef FSMonTest CLASSNAME;
|
||||
|
||||
FSMonTest();
|
||||
};
|
||||
|
||||
#endif
|
||||
7
bazaar/FSMonTest/FSMonTest.lay
Normal file
7
bazaar/FSMonTest/FSMonTest.lay
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
LAYOUT(FSMonTestLayout, 504, 288)
|
||||
ITEM(LineEdit, logEdit, LeftPosZ(8, 488).TopPosZ(4, 256))
|
||||
ITEM(Button, stopBtn, SetLabel(t_("Stop")).LeftPosZ(76, 60).TopPosZ(264, 20))
|
||||
ITEM(Button, quitBtn, SetLabel(t_("Quit")).LeftPosZ(436, 60).TopPosZ(264, 20))
|
||||
ITEM(Button, startBtn, SetLabel(t_("Start")).LeftPosZ(12, 60).TopPosZ(264, 20))
|
||||
END_LAYOUT
|
||||
|
||||
14
bazaar/FSMonTest/FSMonTest.upp
Normal file
14
bazaar/FSMonTest/FSMonTest.upp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
description "Test FSMon Filesystem Monitor Package\377";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
FSMon;
|
||||
|
||||
file
|
||||
FSMonTest.lay,
|
||||
FSMonTest.h,
|
||||
main.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI MT";
|
||||
|
||||
5
bazaar/FSMonTest/init
Normal file
5
bazaar/FSMonTest/init
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _FSMonTest_icpp_init_stub
|
||||
#define _FSMonTest_icpp_init_stub
|
||||
#include "CtrlLib/init"
|
||||
#include "FSMon/init"
|
||||
#endif
|
||||
82
bazaar/FSMonTest/main.cpp
Normal file
82
bazaar/FSMonTest/main.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#include "FSMonTest.h"
|
||||
|
||||
void FSMonTest::Log(String const &s)
|
||||
{
|
||||
logEdit.Set(logEdit.Get() + s + "\n");
|
||||
logEdit.SetCursor(INT_MAX);
|
||||
}
|
||||
|
||||
void FSMonTest::startCb(void)
|
||||
{
|
||||
String path = AppendFileName(GetHomeDirectory(), "FSMonTest_A");
|
||||
Log("Starting monitor for '" + path + "'");
|
||||
if(!DirectoryExists(path))
|
||||
RealizeDirectory(path);
|
||||
fsmMon.Add(path);
|
||||
}
|
||||
|
||||
void FSMonTest::stopCb(void)
|
||||
{
|
||||
Log("Stopping\n");
|
||||
String path = AppendFileName(GetHomeDirectory(), "FSMonTest_A");
|
||||
fsmMon.Remove(path);
|
||||
}
|
||||
|
||||
void FSMonTest::quitCb(void)
|
||||
{
|
||||
stopCb();
|
||||
Break();
|
||||
}
|
||||
|
||||
void FSMonTest::monitorCb()
|
||||
{
|
||||
Vector<FSMon::Info> infos = fsmMon.GetChanged();
|
||||
for(int iChange = 0; iChange < infos.GetCount(); iChange++)
|
||||
{
|
||||
FSMon::Info const &info = infos[iChange];
|
||||
switch(info.flags)
|
||||
{
|
||||
case FSMon::FSM_NOP :
|
||||
Log(String("NO-OP EVENT"));
|
||||
break;
|
||||
case FSMon::FSM_Created :
|
||||
Log(String("Creating file '") + info.path + "'");
|
||||
break;
|
||||
case FSMon::FSM_Deleted :
|
||||
Log(String("Deleting file '") + info.path + "'");
|
||||
break;
|
||||
case FSMon::FSM_Moved :
|
||||
Log(String("Moving file '") + info.path + "' to '" + info.newPath + "'");
|
||||
break;
|
||||
case FSMon::FSM_FolderCreated :
|
||||
Log(String("Creating folder '") + info.path + "'");
|
||||
break;
|
||||
case FSMon::FSM_FolderDeleted :
|
||||
Log(String("Deleting folder '") + info.path + "'");
|
||||
break;
|
||||
case FSMon::FSM_FolderMoved :
|
||||
Log(String("Moving folder '") + info.path + "' to '" + info.newPath + "'");
|
||||
break;
|
||||
case FSMon::FSM_Modified :
|
||||
Log(String("Modifying file '") + info.path + "'");
|
||||
break;
|
||||
case FSMon::FSM_AttribChange :
|
||||
Log(String("Modifying file attributes for '") + info.path + "'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FSMonTest::FSMonTest()
|
||||
{
|
||||
CtrlLayout(*this, "Window title");
|
||||
fsmMon.EventHandler = THISBACK(monitorCb);
|
||||
startBtn <<= THISBACK(startCb);
|
||||
stopBtn <<= THISBACK(stopCb);
|
||||
quitBtn <<= THISBACK(quitCb);
|
||||
}
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
FSMonTest().Run();
|
||||
}
|
||||
|
|
@ -1,44 +1,78 @@
|
|||
uses(LINUX) OCE/Visualization/Xw;
|
||||
uses(WIN32) OCE/Visualization/WNT;
|
||||
noblitz;
|
||||
|
||||
uses
|
||||
OCE/ModelingData,
|
||||
FTGL;
|
||||
options(WIN32) -D__InterfaceGraphic_DLL;
|
||||
|
||||
uses(LINUX) OCE/Visualization/Xw;
|
||||
|
||||
uses(WIN32) OCE/Visualization/WNT;
|
||||
|
||||
options
|
||||
-I../,
|
||||
-I../oce/inc,
|
||||
-DHAVE_FTGL_NEWER212,
|
||||
-DOCE_BUILD_STATIC_LIB;
|
||||
|
||||
options(WIN32) -D__InterfaceGraphic_DLL;
|
||||
|
||||
options(POSIX) -DHAVE_DIRENT_H;
|
||||
|
||||
options(POSIX) -DHAVE_DLFCN_H;
|
||||
|
||||
options(POSIX) -DOCE_HAVE_FSTREAM;
|
||||
|
||||
options(POSIX) -DHAVE_NETDB_H;
|
||||
|
||||
options(POSIX) -DOCE_HAVE_IOSTREAM;
|
||||
|
||||
options(POSIX) -DOCE_HAVE_IOMANIP;
|
||||
|
||||
options(POSIX) -DOCE_HAVE_LIMITS_H;
|
||||
|
||||
options(POSIX) -DHAVE_PWD_H;
|
||||
|
||||
options(POSIX) -DHAVE_SIGNAL_H;
|
||||
|
||||
options(POSIX) -DHAVE_STATFS;
|
||||
|
||||
options(POSIX) -DHAVE_STDLIB_H;
|
||||
|
||||
options(POSIX) -DHAVE_STRING_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_IPC_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_MMAN_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_PARAM_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_SEM_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_SOCKET_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_STAT_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_TIME_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_TIMES_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_TYPES_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_UNISTD_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_UTSNAME_H;
|
||||
|
||||
options(POSIX) -DHAVE_SYS_VFS_H;
|
||||
|
||||
options(POSIX) -DHAVE_UNISTD_H;
|
||||
|
||||
options(WIN32) -DWNT;
|
||||
|
||||
options(WIN32) -DHAVE_NO_DLL;
|
||||
|
||||
include
|
||||
../oce/inc;
|
||||
|
||||
file
|
||||
../oce/src/MeshVS/MeshVS_ColorHasher.cxx
|
||||
options() -I../oce/src/MeshVS
|
||||
|
|
@ -3233,3 +3267,4 @@ file
|
|||
../oce/src/Voxel/Voxel_Writer.cxx
|
||||
options() -I../oce/src/Voxel
|
||||
options() -I../oce/drv/Voxel;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue