From d94e9c97d269f47313d5134d0d90a62dd9428dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 16 Jul 2015 22:34:58 +0200 Subject: [PATCH] launch_daemon: Added support for external events. * Other apps can register events, and the launch_daemon can act on them when they are triggered. --- headers/private/app/LaunchDaemonDefs.h | 5 +- headers/private/app/LaunchRoster.h | 9 + src/kits/app/LaunchRoster.cpp | 51 +++++ src/servers/launch/Events.cpp | 125 ++++++++++- src/servers/launch/Events.h | 4 + src/servers/launch/LaunchDaemon.cpp | 300 ++++++++++++++++++++++++- 6 files changed, 487 insertions(+), 7 deletions(-) diff --git a/headers/private/app/LaunchDaemonDefs.h b/headers/private/app/LaunchDaemonDefs.h index ec08f109fb..9842f850f8 100644 --- a/headers/private/app/LaunchDaemonDefs.h +++ b/headers/private/app/LaunchDaemonDefs.h @@ -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', }; diff --git a/headers/private/app/LaunchRoster.h b/headers/private/app/LaunchRoster.h index 6855c37dc1..6a4f961658 100644 --- a/headers/private/app/LaunchRoster.h +++ b/headers/private/app/LaunchRoster.h @@ -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; diff --git a/src/kits/app/LaunchRoster.cpp b/src/kits/app/LaunchRoster.cpp index 00b41b9a05..da1f7323ca 100644 --- a/src/kits/app/LaunchRoster.cpp +++ b/src/kits/app/LaunchRoster.cpp @@ -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; +} diff --git a/src/servers/launch/Events.cpp b/src/servers/launch/Events.cpp index 941f139d8e..8b46df3a02 100644 --- a/src/servers/launch/Events.cpp +++ b/src/servers/launch/Events.cpp @@ -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(event)) { + for (int32 index = 0; index < container->Events().CountItems(); + index++) { + Event* event = container->Events().ItemAt(index); + if (ExternalEvent* external = dynamic_cast(event)) { + if (external->Name() == name && external->Resolve()) + return true; + } else if (dynamic_cast(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(event)) { + for (int32 index = 0; index < container->Events().CountItems(); + index++) { + Event* event = container->Events().ItemAt(index); + if (ExternalEvent* external = dynamic_cast(event)) { + if (external->Name() == name) { + external->Trigger(); + return; + } + } else if (dynamic_cast(event) != NULL) { + TriggerRegisteredEvent(event, name); + } + } + } + return; +} + + /*static*/ bool Events::TriggerDemand(Event* event) { diff --git a/src/servers/launch/Events.h b/src/servers/launch/Events.h index 493b2f43e8..ac7afd067a 100644 --- a/src/servers/launch/Events.h +++ b/src/servers/launch/Events.h @@ -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); }; diff --git a/src/servers/launch/LaunchDaemon.cpp b/src/servers/launch/LaunchDaemon.cpp index bc38c4bb5a..095fe14f1a 100644 --- a/src/servers/launch/LaunchDaemon.cpp +++ b/src/servers/launch/LaunchDaemon.cpp @@ -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 fListeners; +}; + + typedef std::map JobMap; typedef std::map SessionMap; typedef std::map TargetMap; +typedef std::map 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();