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:
Axel Dörfler 2015-07-16 22:34:58 +02:00
parent 2c756e16e0
commit d94e9c97d2
6 changed files with 487 additions and 7 deletions

View File

@ -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',
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);
};

View File

@ -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();