launch_daemon: Added support for external events.
* Other apps can register events, and the launch_daemon can act on them when they are triggered.
This commit is contained in:
parent
2c756e16e0
commit
d94e9c97d2
@ -22,12 +22,15 @@ namespace BPrivate {
|
||||
#define B_LAUNCH_DAEMON_PORT_NAME "system:launch_daemon"
|
||||
|
||||
|
||||
// message constants
|
||||
// Message constants
|
||||
enum {
|
||||
B_GET_LAUNCH_DATA = 'lnda',
|
||||
B_LAUNCH_TARGET = 'lntg',
|
||||
B_LAUNCH_SESSION = 'lnse',
|
||||
B_REGISTER_SESSION_DAEMON = 'lnrs',
|
||||
B_REGISTER_LAUNCH_EVENT = 'lnre',
|
||||
B_UNREGISTER_LAUNCH_EVENT = 'lnue',
|
||||
B_NOTIFY_LAUNCH_EVENT = 'lnne',
|
||||
};
|
||||
|
||||
|
||||
|
@ -30,12 +30,21 @@ public:
|
||||
|
||||
status_t StartSession(const char* login);
|
||||
|
||||
status_t RegisterEvent(const BMessenger& source,
|
||||
const char* name);
|
||||
status_t UnregisterEvent(const BMessenger& source,
|
||||
const char* name);
|
||||
status_t NotifyEvent(const BMessenger& source,
|
||||
const char* name);
|
||||
|
||||
class Private;
|
||||
|
||||
private:
|
||||
friend class Private;
|
||||
|
||||
void _InitMessenger();
|
||||
status_t _UpdateEvent(uint32 what,
|
||||
const BMessenger& source, const char* name);
|
||||
|
||||
private:
|
||||
BMessenger fMessenger;
|
||||
|
@ -208,6 +208,27 @@ BLaunchRoster::StartSession(const char* login)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BLaunchRoster::RegisterEvent(const BMessenger& source, const char* name)
|
||||
{
|
||||
return _UpdateEvent(B_REGISTER_LAUNCH_EVENT, source, name);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BLaunchRoster::UnregisterEvent(const BMessenger& source, const char* name)
|
||||
{
|
||||
return _UpdateEvent(B_UNREGISTER_LAUNCH_EVENT, source, name);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BLaunchRoster::NotifyEvent(const BMessenger& source, const char* name)
|
||||
{
|
||||
return _UpdateEvent(B_NOTIFY_LAUNCH_EVENT, source, name);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BLaunchRoster::_InitMessenger()
|
||||
{
|
||||
@ -219,3 +240,33 @@ BLaunchRoster::_InitMessenger()
|
||||
B_PREFERRED_TOKEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BLaunchRoster::_UpdateEvent(uint32 what, const BMessenger& source,
|
||||
const char* name)
|
||||
{
|
||||
if (be_app == NULL || name == NULL || name[0] == '\0')
|
||||
return B_BAD_VALUE;
|
||||
|
||||
BMessage request(what);
|
||||
status_t status = request.AddInt32("user", getuid());
|
||||
if (status == B_OK)
|
||||
status = request.AddMessenger("source", source);
|
||||
if (status == B_OK)
|
||||
status = request.AddString("owner", be_app->Signature());
|
||||
if (status == B_OK)
|
||||
status = request.AddString("name", name);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// send the request
|
||||
BMessage result;
|
||||
status = fMessenger.SendMessage(&request, &result);
|
||||
|
||||
// evaluate the reply
|
||||
if (status == B_OK)
|
||||
status = result.what;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -73,6 +73,26 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class ExternalEvent : public Event {
|
||||
public:
|
||||
ExternalEvent(Event* parent, const char* name,
|
||||
const BMessage& args);
|
||||
|
||||
const BString& Name() const;
|
||||
bool Resolve();
|
||||
|
||||
virtual status_t Register(EventRegistrator& registrator) const;
|
||||
virtual void Unregister(EventRegistrator& registrator) const;
|
||||
|
||||
virtual BString ToString() const;
|
||||
|
||||
private:
|
||||
BString fName;
|
||||
BStringList fArguments;
|
||||
bool fResolved;
|
||||
};
|
||||
|
||||
|
||||
class FileCreatedEvent : public Event {
|
||||
public:
|
||||
FileCreatedEvent(Event* parent,
|
||||
@ -104,7 +124,7 @@ create_event(Event* parent, const char* name, const BMessenger* target,
|
||||
if (strcmp(name, "file_created") == 0)
|
||||
return new FileCreatedEvent(parent, args);
|
||||
|
||||
return NULL;
|
||||
return new ExternalEvent(parent, name, args);
|
||||
}
|
||||
|
||||
|
||||
@ -353,6 +373,62 @@ DemandEvent::ToString() const
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - External event
|
||||
|
||||
|
||||
ExternalEvent::ExternalEvent(Event* parent, const char* name,
|
||||
const BMessage& args)
|
||||
:
|
||||
Event(parent),
|
||||
fName(name),
|
||||
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()
|
||||
{
|
||||
if (fResolved)
|
||||
return false;
|
||||
|
||||
fResolved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExternalEvent::Register(EventRegistrator& registrator) const
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExternalEvent::Unregister(EventRegistrator& registrator) const
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
ExternalEvent::ToString() const
|
||||
{
|
||||
return fName;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - file_created
|
||||
|
||||
|
||||
@ -416,6 +492,53 @@ Events::AddOnDemand(Event* event)
|
||||
}
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
Events::ResolveRegisteredEvent(Event* event, const char* name)
|
||||
{
|
||||
if (event == NULL)
|
||||
return false;
|
||||
|
||||
if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
|
||||
for (int32 index = 0; index < container->Events().CountItems();
|
||||
index++) {
|
||||
Event* event = container->Events().ItemAt(index);
|
||||
if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
|
||||
if (external->Name() == name && external->Resolve())
|
||||
return true;
|
||||
} else if (dynamic_cast<EventContainer*>(event) != NULL) {
|
||||
if (ResolveRegisteredEvent(event, name))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
Events::TriggerRegisteredEvent(Event* event, const char* name)
|
||||
{
|
||||
if (event == NULL)
|
||||
return;
|
||||
|
||||
if (EventContainer* container = dynamic_cast<EventContainer*>(event)) {
|
||||
for (int32 index = 0; index < container->Events().CountItems();
|
||||
index++) {
|
||||
Event* event = container->Events().ItemAt(index);
|
||||
if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
|
||||
if (external->Name() == name) {
|
||||
external->Trigger();
|
||||
return;
|
||||
}
|
||||
} else if (dynamic_cast<EventContainer*>(event) != NULL) {
|
||||
TriggerRegisteredEvent(event, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
Events::TriggerDemand(Event* event)
|
||||
{
|
||||
|
@ -53,6 +53,10 @@ public:
|
||||
static Event* FromMessage(const BMessenger& target,
|
||||
const BMessage& message);
|
||||
static Event* AddOnDemand(Event* event);
|
||||
static bool ResolveRegisteredEvent(Event* event,
|
||||
const char* name);
|
||||
static void TriggerRegisteredEvent(Event* event,
|
||||
const char* name);
|
||||
static bool TriggerDemand(Event* event);
|
||||
};
|
||||
|
||||
|
@ -67,14 +67,37 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class RegisteredEvent {
|
||||
public:
|
||||
RegisteredEvent(BMessenger& source,
|
||||
const char* ownerName,
|
||||
const char* name);
|
||||
~RegisteredEvent();
|
||||
|
||||
const char* Name() const;
|
||||
|
||||
int32 CountListeners() const;
|
||||
BaseJob* ListenerAt(int32 index) const;
|
||||
|
||||
status_t AddListener(BaseJob* job);
|
||||
void RemoveListener(BaseJob* job);
|
||||
|
||||
private:
|
||||
BString fName;
|
||||
BObjectList<BaseJob> fListeners;
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<BString, Job*> JobMap;
|
||||
typedef std::map<uid_t, Session*> SessionMap;
|
||||
typedef std::map<BString, Target*> TargetMap;
|
||||
typedef std::map<BString, RegisteredEvent*> EventMap;
|
||||
|
||||
|
||||
class LaunchDaemon : public BServer, public Finder, public ConditionContext {
|
||||
public:
|
||||
LaunchDaemon(bool userMode, status_t& error);
|
||||
LaunchDaemon(bool userMode,
|
||||
const EventMap& events, status_t& error);
|
||||
virtual ~LaunchDaemon();
|
||||
|
||||
virtual Job* FindJob(const char* name) const;
|
||||
@ -92,6 +115,9 @@ private:
|
||||
void _HandleLaunchTarget(BMessage* message);
|
||||
void _HandleLaunchSession(BMessage* message);
|
||||
void _HandleRegisterSessionDaemon(BMessage* message);
|
||||
void _HandleRegisterLaunchEvent(BMessage* message);
|
||||
void _HandleUnregisterLaunchEvent(BMessage* message);
|
||||
void _HandleNotifyLaunchEvent(BMessage* message);
|
||||
|
||||
uid_t _GetUserID(BMessage* message);
|
||||
|
||||
@ -120,6 +146,14 @@ private:
|
||||
void _SetEnvironment(BaseJob* job,
|
||||
const BMessage& message);
|
||||
|
||||
RegisteredEvent* _FindEvent(const char* owner,
|
||||
const char* name) const;
|
||||
void _ResolveRegisteredEvents(RegisteredEvent* event,
|
||||
const BString& name);
|
||||
void _ResolveRegisteredEvents(BaseJob* job);
|
||||
void _ForwardEventMessage(uid_t user,
|
||||
BMessage* message);
|
||||
|
||||
status_t _StartSession(const char* login);
|
||||
|
||||
void _RetrieveKernelOptions();
|
||||
@ -131,6 +165,7 @@ private:
|
||||
JobMap fJobs;
|
||||
TargetMap fTargets;
|
||||
BStringList fRunTargets;
|
||||
EventMap fEvents;
|
||||
JobQueue fJobQueue;
|
||||
SessionMap fSessions;
|
||||
MainWorker* fMainWorker;
|
||||
@ -166,11 +201,68 @@ Session::Session(uid_t user, const BMessenger& daemon)
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
LaunchDaemon::LaunchDaemon(bool userMode, status_t& error)
|
||||
RegisteredEvent::RegisteredEvent(BMessenger& source, const char* ownerName,
|
||||
const char* name)
|
||||
:
|
||||
fName(name),
|
||||
fListeners(5, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
RegisteredEvent::~RegisteredEvent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
RegisteredEvent::Name() const
|
||||
{
|
||||
return fName.String();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
RegisteredEvent::CountListeners() const
|
||||
{
|
||||
return fListeners.CountItems();
|
||||
}
|
||||
|
||||
|
||||
BaseJob*
|
||||
RegisteredEvent::ListenerAt(int32 index) const
|
||||
{
|
||||
return fListeners.ItemAt(index);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RegisteredEvent::AddListener(BaseJob* job)
|
||||
{
|
||||
if (fListeners.AddItem(job))
|
||||
return B_OK;
|
||||
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RegisteredEvent::RemoveListener(BaseJob* job)
|
||||
{
|
||||
fListeners.RemoveItem(job);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events,
|
||||
status_t& error)
|
||||
:
|
||||
BServer(kLaunchDaemonSignature, NULL,
|
||||
create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
|
||||
userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
|
||||
fEvents(events),
|
||||
fInitTarget(userMode ? NULL : new Target("init")),
|
||||
fUserMode(userMode)
|
||||
{
|
||||
@ -301,6 +393,18 @@ LaunchDaemon::MessageReceived(BMessage* message)
|
||||
_HandleRegisterSessionDaemon(message);
|
||||
break;
|
||||
|
||||
case B_REGISTER_LAUNCH_EVENT:
|
||||
_HandleRegisterLaunchEvent(message);
|
||||
break;
|
||||
|
||||
case B_UNREGISTER_LAUNCH_EVENT:
|
||||
_HandleUnregisterLaunchEvent(message);
|
||||
break;
|
||||
|
||||
case B_NOTIFY_LAUNCH_EVENT:
|
||||
_HandleNotifyLaunchEvent(message);
|
||||
break;
|
||||
|
||||
case kMsgEventTriggered:
|
||||
{
|
||||
// An internal event has been triggered.
|
||||
@ -495,6 +599,112 @@ LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
|
||||
{
|
||||
uid_t user = _GetUserID(message);
|
||||
if (user < 0)
|
||||
return;
|
||||
|
||||
if (user == 0 || fUserMode) {
|
||||
status_t status = B_OK;
|
||||
|
||||
const char* name = message->GetString("name");
|
||||
const char* ownerName = message->GetString("owner");
|
||||
BMessenger source;
|
||||
if (name != NULL && ownerName != NULL
|
||||
&& message->FindMessenger("source", &source) == B_OK) {
|
||||
// Register event
|
||||
ownerName = get_leaf(ownerName);
|
||||
|
||||
RegisteredEvent* event = new (std::nothrow) RegisteredEvent(
|
||||
source, ownerName, name);
|
||||
if (event != NULL) {
|
||||
// Use short name, and fully qualified name
|
||||
BString eventName = name;
|
||||
fEvents.insert(std::make_pair(eventName, event));
|
||||
_ResolveRegisteredEvents(event, eventName);
|
||||
|
||||
eventName.Prepend("/");
|
||||
eventName.Prepend(ownerName);
|
||||
fEvents.insert(std::make_pair(eventName, event));
|
||||
_ResolveRegisteredEvents(event, eventName);
|
||||
} else
|
||||
status = B_NO_MEMORY;
|
||||
} else
|
||||
status = B_BAD_VALUE;
|
||||
|
||||
BMessage reply((uint32)status);
|
||||
message->SendReply(&reply);
|
||||
}
|
||||
|
||||
_ForwardEventMessage(user, message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message)
|
||||
{
|
||||
uid_t user = _GetUserID(message);
|
||||
if (user < 0)
|
||||
return;
|
||||
|
||||
if (user == 0 || fUserMode) {
|
||||
status_t status = B_OK;
|
||||
|
||||
const char* name = message->GetString("name");
|
||||
const char* ownerName = message->GetString("owner");
|
||||
BMessenger source;
|
||||
if (name != NULL && ownerName != NULL
|
||||
&& message->FindMessenger("source", &source) == B_OK) {
|
||||
// Unregister short and fully qualified event name
|
||||
ownerName = get_leaf(ownerName);
|
||||
|
||||
BString eventName = name;
|
||||
fEvents.erase(eventName);
|
||||
|
||||
eventName.Prepend("/");
|
||||
eventName.Prepend(ownerName);
|
||||
fEvents.erase(eventName);
|
||||
} else
|
||||
status = B_BAD_VALUE;
|
||||
|
||||
BMessage reply((uint32)status);
|
||||
message->SendReply(&reply);
|
||||
}
|
||||
|
||||
_ForwardEventMessage(user, message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
|
||||
{
|
||||
uid_t user = _GetUserID(message);
|
||||
if (user < 0)
|
||||
return;
|
||||
|
||||
if (user == 0 || fUserMode) {
|
||||
// Trigger events
|
||||
const char* name = message->GetString("name");
|
||||
const char* ownerName = message->GetString("owner");
|
||||
// TODO: support arguments (as selectors)
|
||||
|
||||
RegisteredEvent* event = _FindEvent(ownerName, name);
|
||||
if (event != NULL) {
|
||||
// Evaluate all of its jobs
|
||||
int32 count = event->CountListeners();
|
||||
for (int32 index = 0; index < count; index++) {
|
||||
BaseJob* listener = event->ListenerAt(index);
|
||||
Events::TriggerRegisteredEvent(listener->Event(), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ForwardEventMessage(user, message);
|
||||
}
|
||||
|
||||
|
||||
uid_t
|
||||
LaunchDaemon::_GetUserID(BMessage* message)
|
||||
{
|
||||
@ -850,8 +1060,10 @@ LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated)
|
||||
if (updated) {
|
||||
job->SetEvent(event);
|
||||
_ResolveRegisteredEvents(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -864,6 +1076,83 @@ LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
|
||||
}
|
||||
|
||||
|
||||
RegisteredEvent*
|
||||
LaunchDaemon::_FindEvent(const char* owner, const char* name) const
|
||||
{
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
BString eventName = name;
|
||||
eventName.ToLower();
|
||||
|
||||
EventMap::const_iterator found = fEvents.find(eventName);
|
||||
if (found != fEvents.end())
|
||||
return found->second;
|
||||
|
||||
if (owner == NULL)
|
||||
return NULL;
|
||||
|
||||
eventName.Prepend("/");
|
||||
eventName.Prepend(get_leaf(owner));
|
||||
|
||||
found = fEvents.find(eventName);
|
||||
if (found != fEvents.end())
|
||||
return found->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_ResolveRegisteredEvents(RegisteredEvent* event,
|
||||
const BString& name)
|
||||
{
|
||||
for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
|
||||
iterator++) {
|
||||
Job* job = iterator->second;
|
||||
if (Events::ResolveRegisteredEvent(job->Event(), name))
|
||||
event->AddListener(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_ResolveRegisteredEvents(BaseJob* job)
|
||||
{
|
||||
if (job->Event() == NULL)
|
||||
return;
|
||||
|
||||
for (EventMap::iterator iterator = fEvents.begin();
|
||||
iterator != fEvents.end(); iterator++) {
|
||||
RegisteredEvent* event = iterator->second;
|
||||
if (Events::ResolveRegisteredEvent(job->Event(), event->Name()))
|
||||
event->AddListener(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
|
||||
{
|
||||
if (fUserMode)
|
||||
return;
|
||||
|
||||
// Forward event to user launch_daemon(s)
|
||||
if (user == 0) {
|
||||
for (SessionMap::iterator iterator = fSessions.begin();
|
||||
iterator != fSessions.end(); iterator++) {
|
||||
Session* session = iterator->second;
|
||||
session->Daemon().SendMessage(message);
|
||||
// ignore reply
|
||||
}
|
||||
} else {
|
||||
Session* session = FindSession(user);
|
||||
if (session != NULL)
|
||||
session->Daemon().SendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
LaunchDaemon::_StartSession(const char* login)
|
||||
{
|
||||
@ -902,7 +1191,7 @@ LaunchDaemon::_StartSession(const char* login)
|
||||
|
||||
// TODO: take over system jobs, and reserve their names
|
||||
status_t status;
|
||||
LaunchDaemon* daemon = new LaunchDaemon(true, status);
|
||||
LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
|
||||
if (status == B_OK)
|
||||
daemon->Run();
|
||||
|
||||
@ -972,8 +1261,9 @@ LaunchDaemon::_AddInitJob(BJob* job)
|
||||
int
|
||||
main()
|
||||
{
|
||||
EventMap events;
|
||||
status_t status;
|
||||
LaunchDaemon* daemon = new LaunchDaemon(false, status);
|
||||
LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
|
||||
if (status == B_OK)
|
||||
daemon->Run();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user