* Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "Events.h"
#include <stdio.h>
#include <Entry.h>
#include <LaunchRoster.h>
#include <Message.h>
#include <ObjectList.h>
#include <Path.h>
#include <StringList.h>
#include "BaseJob.h"
#include "FileWatcher.h"
#include "LaunchDaemon.h"
#include "NetworkWatcher.h"
#include "Utility.h"
#include "VolumeWatcher.h"
class EventContainer : public Event {
protected:
EventContainer(Event* parent,
const BMessenger* target,
const BMessage& args);
EventContainer(BaseJob* owner,
const BMessenger& target);
public:
void AddEvent(Event* event);
BObjectList<Event, true>& Events();
const BMessenger& Target() const;
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual void Trigger(Event* origin);
virtual BaseJob* Owner() const;
virtual void SetOwner(BaseJob* owner);
protected:
void AddEventsToString(BString& string) const;
protected:
BaseJob* fOwner;
BMessenger fTarget;
BObjectList<Event, true> fEvents;
bool fRegistered;
};
class OrEvent : public EventContainer {
public:
OrEvent(Event* parent, const BMessenger* target,
const BMessage& args);
OrEvent(BaseJob* owner,
const BMessenger& target);
virtual void ResetTrigger();
virtual BString ToString() const;
};
class StickyEvent : public Event {
public:
StickyEvent(Event* parent);
virtual ~StickyEvent();
virtual void ResetSticky();
virtual void ResetTrigger();
};
class DemandEvent : public Event {
public:
DemandEvent(Event* parent);
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const;
};
class ExternalEvent : public Event {
public:
ExternalEvent(Event* parent, const char* name,
const BMessage& args);
const BString& Name() const;
bool Resolve(uint32 flags);
void ResetSticky();
virtual void ResetTrigger();
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const;
private:
BString fName;
BStringList fArguments;
uint32 fFlags;
bool fResolved;
};
class FileCreatedEvent : public Event, FileListener {
public:
FileCreatedEvent(Event* parent,
const BMessage& args);
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const;
virtual void FileCreated(const char* path);
private:
BPath fPath;
};
class VolumeMountedEvent : public Event, public VolumeListener {
public:
VolumeMountedEvent(Event* parent,
const BMessage& args);
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const;
virtual void VolumeMounted(dev_t device);
virtual void VolumeUnmounted(dev_t device);
};
class NetworkAvailableEvent : public StickyEvent, public NetworkListener {
public:
NetworkAvailableEvent(Event* parent,
const BMessage& args);
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const;
virtual void NetworkAvailabilityChanged(bool available);
};
static Event*
create_event(Event* parent, const char* name, const BMessenger* target,
const BMessage& args)
{
if (strcmp(name, "or") == 0) {
if (args.IsEmpty())
return NULL;
return new OrEvent(parent, target, args);
}
if (strcmp(name, "demand") == 0)
return new DemandEvent(parent);
if (strcmp(name, "file_created") == 0)
return new FileCreatedEvent(parent, args);
if (strcmp(name, "volume_mounted") == 0)
return new VolumeMountedEvent(parent, args);
if (strcmp(name, "network_available") == 0)
return new NetworkAvailableEvent(parent, args);
return new ExternalEvent(parent, name, args);
}
Event::Event(Event* parent)
:
fParent(parent),
fTriggered(false)
{
}
Event::~Event()
{
}
bool
Event::Triggered() const
{
return fTriggered;
}
void
Event::Trigger(Event* origin)
{
fTriggered = true;
if (fParent != NULL)
fParent->Trigger(origin);
}
void
Event::ResetTrigger()
{
fTriggered = false;
}
BaseJob*
Event::Owner() const
{
if (fParent != NULL)
return fParent->Owner();
return NULL;
}
void
Event::SetOwner(BaseJob* owner)
{
if (fParent != NULL)
fParent->SetOwner(owner);
}
Event*
Event::Parent() const
{
return fParent;
}
EventContainer::EventContainer(Event* parent, const BMessenger* target,
const BMessage& args)
:
Event(parent),
fEvents(5),
fRegistered(false)
{
if (target != NULL)
fTarget = *target;
char* name;
type_code type;
int32 count;
for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type,
&count) == B_OK; index++) {
BMessage message;
for (int32 messageIndex = 0; args.FindMessage(name, messageIndex,
&message) == B_OK; messageIndex++) {
AddEvent(create_event(this, name, target, message));
}
}
}
EventContainer::EventContainer(BaseJob* owner, const BMessenger& target)
:
Event(NULL),
fOwner(owner),
fTarget(target),
fEvents(5),
fRegistered(false)
{
}
void
EventContainer::AddEvent(Event* event)
{
if (event != NULL)
fEvents.AddItem(event);
}
BObjectList<Event, true>&
EventContainer::Events()
{
return fEvents;
}
const BMessenger&
EventContainer::Target() const
{
return fTarget;
}
status_t
EventContainer::Register(EventRegistrator& registrator)
{
if (fRegistered)
return B_OK;
int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) {
Event* event = fEvents.ItemAt(index);
status_t status = event->Register(registrator);
if (status != B_OK)
return status;
}
fRegistered = true;
return B_OK;
}
void
EventContainer::Unregister(EventRegistrator& registrator)
{
int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) {
Event* event = fEvents.ItemAt(index);
event->Unregister(registrator);
}
}
void
EventContainer::Trigger(Event* origin)
{
Event::Trigger(origin);
if (Parent() == NULL && Owner() != NULL) {
BMessage message(kMsgEventTriggered);
message.AddPointer("event", origin);
message.AddString("owner", Owner()->Name());
fTarget.SendMessage(&message);
}
}
BaseJob*
EventContainer::Owner() const
{
return fOwner;
}
void
EventContainer::SetOwner(BaseJob* owner)
{
Event::SetOwner(owner);
fOwner = owner;
}
void
EventContainer::AddEventsToString(BString& string) const
{
string += "[";
for (int32 index = 0; index < fEvents.CountItems(); index++) {
if (index != 0)
string += ", ";
string += fEvents.ItemAt(index)->ToString();
}
string += "]";
}
OrEvent::OrEvent(Event* parent, const BMessenger* target, const BMessage& args)
:
EventContainer(parent, target, args)
{
}
OrEvent::OrEvent(BaseJob* owner, const BMessenger& target)
:
EventContainer(owner, target)
{
}
void
OrEvent::ResetTrigger()
{
fTriggered = false;
int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) {
Event* event = fEvents.ItemAt(index);
event->ResetTrigger();
fTriggered |= event->Triggered();
}
}
BString
OrEvent::ToString() const
{
BString string = "or ";
EventContainer::AddEventsToString(string);
return string;
}
StickyEvent::StickyEvent(Event* parent)
:
Event(parent)
{
}
StickyEvent::~StickyEvent()
{
}
void
StickyEvent::ResetSticky()
{
Event::ResetTrigger();
}
void
StickyEvent::ResetTrigger()
{
}
DemandEvent::DemandEvent(Event* parent)
:
Event(parent)
{
}
status_t
DemandEvent::Register(EventRegistrator& registrator)
{
return B_OK;
}
void
DemandEvent::Unregister(EventRegistrator& registrator)
{
}
BString
DemandEvent::ToString() const
{
return "demand";
}
ExternalEvent::ExternalEvent(Event* parent, const char* name,
const BMessage& args)
:
Event(parent),
fName(name),
fFlags(0),
fResolved(false)
{
const char* argument;
for (int32 index = 0; args.FindString("args", index, &argument) == B_OK;
index++) {
fArguments.Add(argument);
}
}
const BString&
ExternalEvent::Name() const
{
return fName;
}
bool
ExternalEvent::Resolve(uint32 flags)
{
if (fResolved)
return false;
fResolved = true;
fFlags = flags;
return true;
}
void
ExternalEvent::ResetSticky()
{
if ((fFlags & B_STICKY_EVENT) != 0)
Event::ResetTrigger();
}
void
ExternalEvent::ResetTrigger()
{
if ((fFlags & B_STICKY_EVENT) == 0)
Event::ResetTrigger();
}
status_t
ExternalEvent::Register(EventRegistrator& registrator)
{
return registrator.RegisterExternalEvent(this, Name().String(), fArguments);
}
void
ExternalEvent::Unregister(EventRegistrator& registrator)
{
registrator.UnregisterExternalEvent(this, Name().String());
}
BString
ExternalEvent::ToString() const
{
return fName;
}
FileCreatedEvent::FileCreatedEvent(Event* parent, const BMessage& args)
:
Event(parent)
{
fPath.SetTo(args.GetString("args", NULL));
}
status_t
FileCreatedEvent::Register(EventRegistrator& registrator)
{
return FileWatcher::Register(this, fPath);
}
void
FileCreatedEvent::Unregister(EventRegistrator& registrator)
{
FileWatcher::Unregister(this, fPath);
}
BString
FileCreatedEvent::ToString() const
{
BString string = "file_created ";
string << fPath.Path();
return string;
}
void
FileCreatedEvent::FileCreated(const char* path)
{
if (strcmp(fPath.Path(), path) == 0)
Trigger(this);
}
VolumeMountedEvent::VolumeMountedEvent(Event* parent, const BMessage& args)
:
Event(parent)
{
}
status_t
VolumeMountedEvent::Register(EventRegistrator& registrator)
{
VolumeWatcher::Register(this);
return B_OK;
}
void
VolumeMountedEvent::Unregister(EventRegistrator& registrator)
{
VolumeWatcher::Unregister(this);
}
BString
VolumeMountedEvent::ToString() const
{
return "volume_mounted";
}
void
VolumeMountedEvent::VolumeMounted(dev_t device)
{
Trigger(this);
}
void
VolumeMountedEvent::VolumeUnmounted(dev_t device)
{
}
NetworkAvailableEvent::NetworkAvailableEvent(Event* parent,
const BMessage& args)
:
StickyEvent(parent)
{
}
status_t
NetworkAvailableEvent::Register(EventRegistrator& registrator)
{
NetworkWatcher::Register(this);
return B_OK;
}
void
NetworkAvailableEvent::Unregister(EventRegistrator& registrator)
{
NetworkWatcher::Unregister(this);
}
BString
NetworkAvailableEvent::ToString() const
{
return "network_available";
}
void
NetworkAvailableEvent::NetworkAvailabilityChanged(bool available)
{
if (available)
Trigger(this);
else
ResetSticky();
}
Event*
Events::FromMessage(const BMessenger& target, const BMessage& message)
{
return create_event(NULL, "or", &target, message);
}
Event*
Events::AddOnDemand(const BMessenger& target, Event* event)
{
OrEvent* orEvent = dynamic_cast<OrEvent*>(event);
if (orEvent == NULL) {
EventContainer* container = dynamic_cast<EventContainer*>(event);
if (container != NULL)
orEvent = new OrEvent(container->Owner(), container->Target());
else
orEvent = new OrEvent(NULL, target);
}
if (orEvent != event && event != NULL)
orEvent->AddEvent(event);
orEvent->AddEvent(new DemandEvent(orEvent));
return orEvent;
}
Event*
Events::ResolveExternalEvent(Event* event, const char* name, uint32 flags)
{
if (event == NULL)
return NULL;
if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
for (int32 index = 0; index < container->Events().CountItems();
index++) {
Event* event = ResolveExternalEvent(container->Events().ItemAt(index), name, flags);
if (event != NULL)
return event;
}
} else if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
if (external->Name() == name && external->Resolve(flags))
return external;
}
return NULL;
}
void
Events::TriggerExternalEvent(Event* event)
{
if (event == NULL)
return;
ExternalEvent* external = dynamic_cast<ExternalEvent*>(event);
if (external == NULL)
return;
external->Trigger(external);
}
void
Events::ResetStickyExternalEvent(Event* event)
{
if (event == NULL)
return;
ExternalEvent* external = dynamic_cast<ExternalEvent*>(event);
if (external == NULL)
return;
external->ResetSticky();
}
\param testOnly If \c true, the deman will not actually be triggered,
it will only be checked if it could.
\return \c true, if there is a demand event, and it has been
triggered by this call. \c false if not.
*/
bool
Events::TriggerDemand(Event* event, bool testOnly)
{
if (event == NULL || event->Triggered())
return false;
if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
for (int32 index = 0; index < container->Events().CountItems();
index++) {
Event* childEvent = container->Events().ItemAt(index);
if (dynamic_cast<DemandEvent*>(childEvent) != NULL) {
if (testOnly)
return true;
childEvent->Trigger(childEvent);
break;
}
if (dynamic_cast<EventContainer*>(childEvent) != NULL) {
if (TriggerDemand(childEvent, testOnly))
break;
}
}
}
return event->Triggered();
}