* Implemented PathMonitor class - untested, but compiles.
* The API is just a proposal at this time, please comment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21020 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
ec8bd525ca
commit
f30198a051
40
headers/private/storage/PathMonitor.h
Normal file
40
headers/private/storage/PathMonitor.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _PATH_MONITOR_H
|
||||
#define _PATH_MONITOR_H
|
||||
|
||||
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
|
||||
// additional flags (combined with those in NodeMonitor.h)
|
||||
#define B_WATCH_FILES_ONLY 0x0100
|
||||
#define B_WATCH_RECURSIVELY 0x0200
|
||||
|
||||
#define B_PATH_MONITOR '_PMN'
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
class PathHandler;
|
||||
|
||||
class BPathMonitor {
|
||||
public:
|
||||
BPathMonitor();
|
||||
BPathMonitor(const char* path, uint32 flags, BMessenger target);
|
||||
~BPathMonitor();
|
||||
|
||||
status_t InitCheck() const;
|
||||
status_t SetTo(const char* path, uint32 flags, BMessenger target);
|
||||
status_t SetTarget(BMessenger target);
|
||||
void Unset();
|
||||
|
||||
private:
|
||||
PathHandler* fHandler;
|
||||
status_t fStatus;
|
||||
};
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
#endif // _PATH_MONITOR_H
|
493
src/kits/storage/PathMonitor.cpp
Normal file
493
src/kits/storage/PathMonitor.cpp
Normal file
@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
*/
|
||||
|
||||
|
||||
#include <PathMonitor.h>
|
||||
|
||||
#include <Application.h>
|
||||
#include <Directory.h>
|
||||
#include <Entry.h>
|
||||
#include <Handler.h>
|
||||
#include <Looper.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace BPrivate;
|
||||
using namespace std;
|
||||
|
||||
|
||||
#define WATCH_NODE_FLAG_MASK 0x00ff
|
||||
|
||||
typedef set<node_ref> DirectorySet;
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
class PathHandler : public BHandler {
|
||||
public:
|
||||
PathHandler(const char* path, uint32 flags, BMessenger target);
|
||||
virtual ~PathHandler();
|
||||
|
||||
status_t InitCheck() const;
|
||||
void SetTarget(BMessenger target);
|
||||
void Quit();
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
private:
|
||||
bool _IsContained(const node_ref& nodeRef) const;
|
||||
bool _IsContained(BEntry& entry) const;
|
||||
bool _HasDirectory(const node_ref& nodeRef) const;
|
||||
void _NotifyTarget(BMessage* message) const;
|
||||
status_t _AddDirectory(BEntry& entry);
|
||||
status_t _RemoveDirectory(const node_ref& nodeRef);
|
||||
status_t _RemoveDirectory(BEntry& entry);
|
||||
|
||||
BPath fPath;
|
||||
int32 fPathLength;
|
||||
BMessenger fTarget;
|
||||
uint32 fFlags;
|
||||
status_t fStatus;
|
||||
bool fOwnsLooper;
|
||||
DirectorySet fDirectories;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
|
||||
{
|
||||
entry_ref ref;
|
||||
ref.device = nodeRef.device;
|
||||
ref.directory = nodeRef.node;
|
||||
|
||||
status_t status = ref.set_name(name);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return entry.SetTo(&ref, true);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
operator<(const node_ref& a, const node_ref& b)
|
||||
{
|
||||
if (a.device < b.device)
|
||||
return true;
|
||||
if (a.device == b.device && a.node < b.node)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
PathHandler::PathHandler(const char* path, uint32 flags, BMessenger target)
|
||||
: BHandler(path),
|
||||
fPath(path, NULL, true),
|
||||
fTarget(target),
|
||||
fFlags(flags),
|
||||
fOwnsLooper(false)
|
||||
{
|
||||
fPathLength = strlen(fPath.Path());
|
||||
|
||||
BPath first(path);
|
||||
node_ref nodeRef;
|
||||
|
||||
while (true) {
|
||||
// try to find the first part of the path that exists
|
||||
BDirectory directory;
|
||||
fStatus = directory.SetTo(first.Path());
|
||||
if (fStatus == B_OK) {
|
||||
fStatus = directory.GetNodeRef(&nodeRef);
|
||||
if (fStatus == B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (first.GetParent(&first) != B_OK) {
|
||||
fStatus = B_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fStatus < B_OK)
|
||||
return;
|
||||
|
||||
if (be_app != NULL) {
|
||||
be_app->AddHandler(this);
|
||||
} else {
|
||||
// TODO: only have a single global looper!
|
||||
BLooper* looper = new BLooper("PathMonitor looper");
|
||||
looper->Run();
|
||||
looper->AddHandler(this);
|
||||
fOwnsLooper = true;
|
||||
}
|
||||
|
||||
fStatus = watch_node(&nodeRef, flags & WATCH_NODE_FLAG_MASK, this);
|
||||
}
|
||||
|
||||
|
||||
PathHandler::~PathHandler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PathHandler::InitCheck() const
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathHandler::SetTarget(BMessenger target)
|
||||
{
|
||||
fTarget = target;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathHandler::Quit()
|
||||
{
|
||||
BMessenger me(this);
|
||||
me.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathHandler::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case B_NODE_MONITOR:
|
||||
{
|
||||
int32 opcode;
|
||||
if (message->FindInt32("opcode", &opcode) != B_OK)
|
||||
return;
|
||||
|
||||
switch (opcode) {
|
||||
case B_ENTRY_CREATED:
|
||||
{
|
||||
const char* name;
|
||||
node_ref nodeRef;
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("directory", &nodeRef.node) != B_OK
|
||||
|| message->FindString("name", &name) != B_OK)
|
||||
break;
|
||||
|
||||
BEntry entry;
|
||||
if (set_entry(nodeRef, name, entry) != B_OK)
|
||||
break;
|
||||
|
||||
bool notify = true;
|
||||
|
||||
if (entry.IsDirectory()) {
|
||||
// a new directory to watch for us
|
||||
if (_AddDirectory(entry) != B_OK
|
||||
|| (fFlags & B_WATCH_FILES_ONLY) != 0)
|
||||
notify = false;
|
||||
}
|
||||
|
||||
if (notify && _IsContained(entry)) {
|
||||
message->AddBool("added", true);
|
||||
_NotifyTarget(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_ENTRY_MOVED:
|
||||
{
|
||||
// has the entry been moved into a monitored directory or has
|
||||
// it been removed from one?
|
||||
const char* name;
|
||||
node_ref nodeRef;
|
||||
uint64 fromNode;
|
||||
uint64 node;
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("to directory", &nodeRef.node) != B_OK
|
||||
|| message->FindInt64("from directory", (int64 *)&fromNode) != B_OK
|
||||
|| message->FindInt64("node", (int64 *)&node) != B_OK
|
||||
|| message->FindString("name", &name) != B_OK)
|
||||
break;
|
||||
|
||||
BEntry entry;
|
||||
if (set_entry(nodeRef, name, entry) != B_OK)
|
||||
break;
|
||||
|
||||
bool wasAdded = false;
|
||||
bool wasRemoved = false;
|
||||
bool notify = true;
|
||||
|
||||
if (_HasDirectory(nodeRef)) {
|
||||
// something has been added to our watched directories
|
||||
|
||||
// test, if the source directory is one of ours as well
|
||||
nodeRef.node = fromNode;
|
||||
bool hasFromDirectory = _HasDirectory(nodeRef);
|
||||
|
||||
if (entry.IsDirectory()) {
|
||||
if (!hasFromDirectory) {
|
||||
// there is a new directory to watch for us
|
||||
_AddDirectory(entry);
|
||||
wasAdded = true;
|
||||
}
|
||||
if ((fFlags & B_WATCH_FILES_ONLY) != 0)
|
||||
notify = false;
|
||||
} else if (!hasFromDirectory) {
|
||||
// file has been added
|
||||
wasAdded = true;
|
||||
}
|
||||
} else {
|
||||
// and entry has been removed from our directories
|
||||
wasRemoved = true;
|
||||
|
||||
if (entry.IsDirectory()) {
|
||||
_RemoveDirectory(entry);
|
||||
if ((fFlags & B_WATCH_FILES_ONLY) != 0)
|
||||
notify = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (notify && _IsContained(entry)) {
|
||||
if (wasAdded)
|
||||
message->AddBool("added", true);
|
||||
if (wasRemoved)
|
||||
message->AddBool("removed", true);
|
||||
|
||||
_NotifyTarget(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_ENTRY_REMOVED:
|
||||
{
|
||||
node_ref nodeRef;
|
||||
uint64 directoryNode;
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
|
||||
|| message->FindInt64("node", &nodeRef.node) != B_OK)
|
||||
break;
|
||||
|
||||
bool notify = true;
|
||||
|
||||
if (_HasDirectory(nodeRef)) {
|
||||
// the directory has been removed, so we remove it as well
|
||||
_RemoveDirectory(nodeRef);
|
||||
if ((fFlags & B_WATCH_FILES_ONLY) != 0)
|
||||
notify = false;
|
||||
}
|
||||
|
||||
nodeRef.node = directoryNode;
|
||||
if (notify && _IsContained(nodeRef)) {
|
||||
message->AddBool("removed", true);
|
||||
_NotifyTarget(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
_NotifyTarget(message);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_QUIT_REQUESTED:
|
||||
{
|
||||
BLooper* looper = Looper();
|
||||
|
||||
stop_watching(this);
|
||||
looper->RemoveHandler(this);
|
||||
delete this;
|
||||
|
||||
if (fOwnsLooper)
|
||||
looper->Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
BHandler::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PathHandler::_IsContained(const node_ref& nodeRef) const
|
||||
{
|
||||
BDirectory directory(&nodeRef);
|
||||
if (directory.InitCheck() != B_OK)
|
||||
return false;
|
||||
|
||||
BEntry entry;
|
||||
if (directory.GetEntry(&entry) != B_OK)
|
||||
return false;
|
||||
|
||||
return _IsContained(entry);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PathHandler::_IsContained(BEntry& entry) const
|
||||
{
|
||||
BPath path;
|
||||
if (entry.GetPath(&path) != B_OK)
|
||||
return false;
|
||||
|
||||
return strncmp(path.Path(), fPath.Path(), fPathLength) == 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PathHandler::_HasDirectory(const node_ref& nodeRef) const
|
||||
{
|
||||
DirectorySet::const_iterator iterator = fDirectories.find(nodeRef);
|
||||
return iterator != fDirectories.end();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PathHandler::_NotifyTarget(BMessage* message) const
|
||||
{
|
||||
BMessage update(*message);
|
||||
update.what = B_PATH_MONITOR;
|
||||
fTarget.SendMessage(&update);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PathHandler::_AddDirectory(BEntry& entry)
|
||||
{
|
||||
node_ref nodeRef;
|
||||
status_t status = entry.GetNodeRef(&nodeRef);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// check if we are already know this directory
|
||||
|
||||
if (_HasDirectory(nodeRef))
|
||||
return B_OK;
|
||||
|
||||
uint32 flags;
|
||||
if (_IsContained(entry))
|
||||
flags = fFlags & WATCH_NODE_FLAG_MASK;
|
||||
else
|
||||
flags = B_WATCH_DIRECTORY;
|
||||
|
||||
status = watch_node(&nodeRef, flags, this);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
fDirectories.insert(nodeRef);
|
||||
|
||||
#if 0
|
||||
BEntry parent;
|
||||
if (entry.GetParent(&parent) == B_OK
|
||||
&& !_IsContained(parent)) {
|
||||
// TODO: remove parent from watched directories
|
||||
}
|
||||
#endif
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PathHandler::_RemoveDirectory(const node_ref& nodeRef)
|
||||
{
|
||||
DirectorySet::iterator iterator = fDirectories.find(nodeRef);
|
||||
if (iterator == fDirectories.end())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
fDirectories.erase(iterator);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PathHandler::_RemoveDirectory(BEntry& entry)
|
||||
{
|
||||
node_ref nodeRef;
|
||||
status_t status = entry.GetNodeRef(&nodeRef);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return _RemoveDirectory(nodeRef);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
BPathMonitor::BPathMonitor()
|
||||
:
|
||||
fHandler(NULL),
|
||||
fStatus(B_NO_INIT)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BPathMonitor::BPathMonitor(const char* path, uint32 flags, BMessenger target)
|
||||
:
|
||||
fHandler(NULL),
|
||||
fStatus(B_NO_INIT)
|
||||
{
|
||||
SetTo(path, flags, target);
|
||||
}
|
||||
|
||||
|
||||
BPathMonitor::~BPathMonitor()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BPathMonitor::InitCheck() const
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BPathMonitor::SetTo(const char* path, uint32 flags, BMessenger target)
|
||||
{
|
||||
Unset();
|
||||
|
||||
fHandler = new PathHandler(path, flags, target);
|
||||
status_t status = fHandler->InitCheck();
|
||||
if (status < B_OK)
|
||||
Unset();
|
||||
|
||||
return fStatus = status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BPathMonitor::SetTarget(BMessenger target)
|
||||
{
|
||||
if (fStatus < B_OK)
|
||||
return B_NO_INIT;
|
||||
|
||||
fHandler->SetTarget(target);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BPathMonitor::Unset()
|
||||
{
|
||||
if (fHandler != NULL) {
|
||||
fHandler->Quit();
|
||||
fHandler = NULL;
|
||||
}
|
||||
fStatus = B_NO_INIT;
|
||||
}
|
||||
|
||||
//} // namespace BPrivate
|
Loading…
Reference in New Issue
Block a user