diff --git a/src/servers/launch/BaseJob.cpp b/src/servers/launch/BaseJob.cpp index 61ed7a6c9d..e6e1f2204d 100644 --- a/src/servers/launch/BaseJob.cpp +++ b/src/servers/launch/BaseJob.cpp @@ -14,6 +14,7 @@ #include #include "Conditions.h" +#include "Events.h" BaseJob::BaseJob(const char* name) @@ -68,6 +69,35 @@ BaseJob::CheckCondition(ConditionContext& context) const } +const ::Event* +BaseJob::Event() const +{ + return fEvent; +} + + +::Event* +BaseJob::Event() +{ + return fEvent; +} + + +void +BaseJob::SetEvent(::Event* event) +{ + fEvent = event; + event->SetOwner(this); +} + + +bool +BaseJob::EventHasTriggered() const +{ + return Event() != NULL && Event()->Triggered(); +} + + const BStringList& BaseJob::Environment() const { diff --git a/src/servers/launch/BaseJob.h b/src/servers/launch/BaseJob.h index 6ebd1dbd8b..41d38f07a2 100644 --- a/src/servers/launch/BaseJob.h +++ b/src/servers/launch/BaseJob.h @@ -15,6 +15,7 @@ using namespace BSupportKit; class BMessage; class Condition; class ConditionContext; +class Event; class BaseJob : public BJob { @@ -29,6 +30,11 @@ public: void SetCondition(::Condition* condition); bool CheckCondition(ConditionContext& context) const; + const ::Event* Event() const; + ::Event* Event(); + void SetEvent(::Event* event); + bool EventHasTriggered() const; + const BStringList& Environment() const; BStringList& Environment(); const BStringList& EnvironmentSourceFiles() const; @@ -47,6 +53,7 @@ private: protected: ::Condition* fCondition; + ::Event* fEvent; BStringList fEnvironment; BStringList fSourceFiles; }; diff --git a/src/servers/launch/Events.cpp b/src/servers/launch/Events.cpp new file mode 100644 index 0000000000..3745fd0cb5 --- /dev/null +++ b/src/servers/launch/Events.cpp @@ -0,0 +1,428 @@ +/* + * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Events.h" + +#include + +#include +#include +#include +#include +#include + +#include "BaseJob.h" +#include "LaunchDaemon.h" +#include "Utility.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& Events(); + + const BMessenger& Target() const; + + virtual status_t Register(EventRegistrator& registrator) const; + virtual void Unregister(EventRegistrator& registrator) const; + + virtual void Trigger(); + + virtual BaseJob* Owner() const; + virtual void SetOwner(BaseJob* owner); + +protected: + void ToString(BString& string) const; + +protected: + BaseJob* fOwner; + BMessenger fTarget; + BObjectList fEvents; +}; + + +class OrEvent : public EventContainer { +public: + OrEvent(Event* parent, const BMessenger* target, + const BMessage& args); + OrEvent(BaseJob* owner, + const BMessenger& target); + + virtual BString ToString() const; +}; + + +class DemandEvent : public Event { +public: + DemandEvent(Event* parent); + + virtual status_t Register(EventRegistrator& registrator) const; + virtual void Unregister(EventRegistrator& registrator) const; + + virtual BString ToString() const; +}; + + +class FileCreatedEvent : public Event { +public: + FileCreatedEvent(Event* parent, + const BMessage& args); + + virtual status_t Register(EventRegistrator& registrator) const; + virtual void Unregister(EventRegistrator& registrator) const; + + virtual BString ToString() const; + +private: + BPath fPath; +}; + + +static Event* +create_event(Event* parent, const char* name, const BMessenger* target, + const BMessage& args) +{ + if (strcmp(name, "or") == 0) + 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); + + return NULL; +} + + +// #pragma mark - + + +Event::Event(Event* parent) + : + fParent(parent) +{ +} + + +Event::~Event() +{ +} + + +bool +Event::Triggered() const +{ + return fTriggered; +} + + +void +Event::Trigger() +{ + fTriggered = true; + if (fParent != NULL) + fParent->Trigger(); +} + + +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; +} + + +// #pragma mark - + + +EventContainer::EventContainer(Event* parent, const BMessenger* target, + const BMessage& args) + : + Event(parent), + fEvents(5, true) +{ + 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, true) +{ +} + + +void +EventContainer::AddEvent(Event* event) +{ + if (event != NULL) + fEvents.AddItem(event); +} + + +BObjectList& +EventContainer::Events() +{ + return fEvents; +} + + +const BMessenger& +EventContainer::Target() const +{ + return fTarget; +} + + +status_t +EventContainer::Register(EventRegistrator& registrator) const +{ + 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; + } + + return B_OK; +} + + +void +EventContainer::Unregister(EventRegistrator& registrator) const +{ + int32 count = fEvents.CountItems(); + for (int32 index = 0; index < count; index++) { + Event* event = fEvents.ItemAt(index); + event->Unregister(registrator); + } +} + + +void +EventContainer::Trigger() +{ + Event::Trigger(); + + if (Parent() == NULL && Owner() != NULL) { + BMessage message(kMsgEventTriggered); + 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::ToString(BString& string) const +{ + string += "["; + + for (int32 index = 0; index < fEvents.CountItems(); index++) { + if (index != 0) + string += ", "; + string += fEvents.ItemAt(index)->ToString(); + } + string += "]"; +} + + +// #pragma mark - or + + +OrEvent::OrEvent(Event* parent, const BMessenger* target, const BMessage& args) + : + EventContainer(parent, target, args) +{ +} + + +OrEvent::OrEvent(BaseJob* owner, const BMessenger& target) + : + EventContainer(owner, target) +{ +} + + +BString +OrEvent::ToString() const +{ + BString string = "or "; + EventContainer::ToString(string); + return string; +} + + +// #pragma mark - demand + + +DemandEvent::DemandEvent(Event* parent) + : + Event(parent) +{ +} + + +status_t +DemandEvent::Register(EventRegistrator& registrator) const +{ + return B_OK; +} + + +void +DemandEvent::Unregister(EventRegistrator& registrator) const +{ +} + + +BString +DemandEvent::ToString() const +{ + return "event"; +} + + +// #pragma mark - file_created + + +FileCreatedEvent::FileCreatedEvent(Event* parent, const BMessage& args) + : + Event(parent) +{ + fPath.SetTo(args.GetString("args", NULL)); +} + + +status_t +FileCreatedEvent::Register(EventRegistrator& registrator) const +{ + return B_ERROR; +} + + +void +FileCreatedEvent::Unregister(EventRegistrator& registrator) const +{ +} + + +BString +FileCreatedEvent::ToString() const +{ + BString string = "file_created "; + string << fPath.Path(); + return string; +} + + +// #pragma mark - + + +/*static*/ Event* +Events::FromMessage(const BMessenger& target, const BMessage& message) +{ + return create_event(NULL, "or", &target, message); +} + + +/*static*/ Event* +Events::AddOnDemand(Event* event) +{ + OrEvent* or = dynamic_cast(event); + if (or == NULL) { + EventContainer* container = dynamic_cast(event); + if (container == NULL) + return NULL; + + or = new OrEvent(container->Owner(), container->Target()); + } + if (or != event && event != NULL) + or->AddEvent(event); + + or->AddEvent(new DemandEvent(or)); + return or; +} + + +/*static*/ bool +Events::TriggerDemand(Event* event) +{ + if (event == NULL) + return false; + + if (EventContainer* container = dynamic_cast(event)) { + for (int32 index = 0; index < container->Events().CountItems(); + index++) { + Event* event = container->Events().ItemAt(index); + if (dynamic_cast(event) != NULL) { + event->Trigger(); + return true; + } + if (dynamic_cast(event) != NULL) { + if (TriggerDemand(event)) + return true; + } + } + } + return false; +} diff --git a/src/servers/launch/Events.h b/src/servers/launch/Events.h new file mode 100644 index 0000000000..2552cb348d --- /dev/null +++ b/src/servers/launch/Events.h @@ -0,0 +1,59 @@ +/* + * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef EVENTS_H +#define EVENTS_H + + +#include +#include + + +class BaseJob; +class Event; + + +class EventRegistrator { +public: + virtual status_t RegisterEvent(Event* event) = 0; + virtual void UnregisterEvent(Event* event) = 0; +}; + + +class Event { +public: + Event(Event* parent); + virtual ~Event(); + + virtual status_t Register( + EventRegistrator& registrator) const = 0; + virtual void Unregister( + EventRegistrator& registrator) const = 0; + + bool Triggered() const; + virtual void Trigger(); + + virtual BaseJob* Owner() const; + virtual void SetOwner(BaseJob* owner); + + Event* Parent() const; + + virtual BString ToString() const = 0; + +private: + Event* fParent; + bool fTriggered; +}; + + +class Events { +public: + static Event* FromMessage(const BMessenger& target, + const BMessage& message); + static Event* AddOnDemand(Event* event); + static bool TriggerDemand(Event* event); +}; + + +#endif // EVENTS_H diff --git a/src/servers/launch/Jamfile b/src/servers/launch/Jamfile index fe663a04cf..068a71c712 100644 --- a/src/servers/launch/Jamfile +++ b/src/servers/launch/Jamfile @@ -11,6 +11,7 @@ Server launch_daemon BaseJob.cpp Conditions.cpp + Events.cpp Job.cpp SettingsParser.cpp Target.cpp diff --git a/src/servers/launch/LaunchDaemon.cpp b/src/servers/launch/LaunchDaemon.cpp index 63a6245aae..976a8e8a71 100644 --- a/src/servers/launch/LaunchDaemon.cpp +++ b/src/servers/launch/LaunchDaemon.cpp @@ -4,6 +4,8 @@ */ +#include "LaunchDaemon.h" + #include #include @@ -31,6 +33,7 @@ #include "multiuser_utils.h" #include "Conditions.h" +#include "Events.h" #include "InitRealTimeClockJob.h" #include "InitSharedMemoryDirectoryJob.h" #include "InitTemporaryDirectoryJob.h" @@ -102,10 +105,13 @@ private: BMessage& message); void _InitJobs(); void _LaunchJobs(Target* target); + void _TriggerJob(Job* job); void _AddLaunchJob(Job* job); void _AddTarget(Target* target); void _SetCondition(BaseJob* job, const BMessage& message); + void _SetEvent(BaseJob* job, + const BMessage& message); void _SetEnvironment(BaseJob* job, const BMessage& message); @@ -261,7 +267,7 @@ LaunchDaemon::ReadyToRun() _InitJobs(); _LaunchJobs(NULL); - // Launch run targets + // Launch run targets (ignores events) for (int32 index = 0; index < fRunTargets.CountStrings(); index++) { Target* target = FindTarget(fRunTargets.StringAt(index)); if (target != NULL) @@ -314,7 +320,7 @@ LaunchDaemon::MessageReceived(BMessage* message) iterator->second.GetInt32("port", -1)); } - _AddLaunchJob(job); + _TriggerJob(job); } message->SendReply(&reply); break; @@ -419,6 +425,28 @@ LaunchDaemon::MessageReceived(BMessage* message) break; } + case kMsgEventTriggered: + { + // An internal event has been triggered. + // Check if its job can be launched now. + const char* name = message->GetString("owner"); + if (name == NULL) + break; + + Job* job = FindJob(name); + if (job != NULL && job->EventHasTriggered()) { + _AddLaunchJob(job); + break; + } + + Target* target = FindTarget(name); + if (target != NULL && target->EventHasTriggered()) { + _LaunchJobs(target); + break; + } + break; + } + default: BServer::MessageReceived(message); break; @@ -542,6 +570,7 @@ LaunchDaemon::_AddTargets(BMessage& message) } _SetCondition(target, targetMessage); + _SetEvent(target, targetMessage); _SetEnvironment(target, targetMessage); _AddJobs(target, targetMessage); } @@ -620,6 +649,7 @@ LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); _SetCondition(job, message); + _SetEvent(job, message); _SetEnvironment(job, message); BMessage portMessage; @@ -683,6 +713,9 @@ LaunchDaemon::_InitJobs() } +/*! Adds all jobs for the specified target (may be \c NULL) to the launch + queue, except those that are triggered by events. +*/ void LaunchDaemon::_LaunchJobs(Target* target) { @@ -692,12 +725,30 @@ LaunchDaemon::_LaunchJobs(Target* target) for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); iterator++) { Job* job = iterator->second; - if (job->Target() == target) - _AddLaunchJob(job); + if (job->Target() == target && job->Event() == NULL) + _TriggerJob(job); } } +void +LaunchDaemon::_TriggerJob(Job* job) +{ + if (job == NULL) + return; + + int32 count = job->Requirements().CountStrings(); + for (int32 index = 0; index < count; index++) { + Job* requirement = FindJob(job->Requirements().StringAt(index)); + if (requirement != NULL) + _TriggerJob(requirement); + } + + if (!Events::TriggerDemand(job->Event())) + _AddLaunchJob(job); +} + + void LaunchDaemon::_AddLaunchJob(Job* job) { @@ -738,6 +789,28 @@ LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) } +void +LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) +{ + Event* event = job->Event(); + bool updated = false; + + BMessage events; + if (message.FindMessage("on", &events) == B_OK) { + event = Events::FromMessage(this, events); + updated = true; + } + + if (message.GetBool("on_demand")) { + event = Events::AddOnDemand(event); + updated = true; + } + + if (updated) + job->SetEvent(event); +} + + void LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) { diff --git a/src/servers/launch/LaunchDaemon.h b/src/servers/launch/LaunchDaemon.h new file mode 100644 index 0000000000..ace8a9fbbc --- /dev/null +++ b/src/servers/launch/LaunchDaemon.h @@ -0,0 +1,15 @@ +/* + * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef LAUNCH_DAEMON_H +#define LAUNCH_DAEMON_H + + +#include + + +static const uint32 kMsgEventTriggered = 'ldet'; + + +#endif // LAUNCH_DAEMON_H diff --git a/src/servers/launch/SettingsParser.cpp b/src/servers/launch/SettingsParser.cpp index fcc631eba7..58f68d45ca 100644 --- a/src/servers/launch/SettingsParser.cpp +++ b/src/servers/launch/SettingsParser.cpp @@ -156,6 +156,7 @@ const static settings_template kJobTemplate[] = { {B_MESSAGE_TYPE, "on", kEventTemplate}, {B_MESSAGE_TYPE, "if", kConditionTemplate}, {B_BOOL_TYPE, "no_safemode", NULL}, + {B_BOOL_TYPE, "on_demand", NULL}, {B_MESSAGE_TYPE, "env", kEnvTemplate}, {0, NULL, NULL} };