#include <list>
#include <security_utilities/globalizer.h>
#include <security_utilities/threading.h>
#include "eventlistener.h"
#include "SharedMemoryClient.h"
#include <notify.h>
#include "sscommon.h"
#include <sys/syslog.h>
using namespace MachPlusPlus;
namespace Security {
namespace SecurityServer {
typedef RefPointer<EventListener> EventPointer;
typedef std::list<EventPointer> EventListenerList;
static const char* GetNotificationName ()
{
const char* name = getenv (SECURITYSERVER_BOOTSTRAP_ENV);
if (name == NULL)
{
name = SECURITY_MESSAGES_NAME;
}
return name;
}
class SharedMemoryClientMaker
{
private:
SharedMemoryClient mClient;
public:
SharedMemoryClientMaker ();
SharedMemoryClient* Client ();
};
SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize)
{
}
SharedMemoryClient* SharedMemoryClientMaker::Client ()
{
return &mClient;
}
ModuleNexus<EventListenerList> gEventListeners;
ModuleNexus<Mutex> gNotificationLock;
ModuleNexus<SharedMemoryClientMaker> gMemoryClient;
class NotificationPort : public MachPlusPlus::CFAutoPort
{
protected:
SharedMemoryClient *mClient;
void ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur);
static void HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info);
public:
NotificationPort (mach_port_t port);
virtual ~NotificationPort ();
virtual void receive(const MachPlusPlus::Message &msg);
};
NotificationPort::NotificationPort (mach_port_t mp) : CFAutoPort (mp)
{
mClient = gMemoryClient ().Client ();
}
NotificationPort::~NotificationPort ()
{
}
void NotificationPort::ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur)
{
EventListenerList& eventList = gEventListeners();
u_int32_t* ptr = (u_int32_t*) buffer;
SecurityServer::NotificationDomain domain = (SecurityServer::NotificationDomain) OSSwapBigToHostInt32 (*ptr++);
SecurityServer::NotificationEvent event = (SecurityServer::NotificationEvent) OSSwapBigToHostInt32 (*ptr++);
CssmData data ((u_int8_t*) ptr, buffer + length - (u_int8_t*) ptr);
EventListenerList::iterator it = eventList.begin ();
while (it != eventList.end ())
{
try
{
EventPointer ep = *it++;
if (ep->GetDomain () == domain &&
(ep->GetMask () & (1 << event)) != 0)
{
ep->consume (domain, event, data);
}
}
catch (CssmError &e)
{
if (e.error != CSSM_ERRCODE_INTERNAL_ERROR)
{
throw;
}
}
}
}
typedef void (^NotificationBlock)();
void NotificationPort::HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info)
{
NotificationBlock nb = (NotificationBlock) info;
nb();
Block_release(nb);
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
}
void NotificationPort::receive (const MachPlusPlus::Message &msg)
{
SegmentOffsetType length;
UnavailableReason ur;
bool result;
while (true)
{
u_int8_t *buffer = new u_int8_t[kSharedMemoryPoolSize];
{
StLock<Mutex> lock (gNotificationLock ());
result = mClient->ReadMessage(buffer, length, ur);
if (!result)
{
delete [] buffer;
return;
}
}
NotificationBlock nb =
^{
ReceiveImplementation(buffer, length, ur);
delete [] buffer;
};
nb = Block_copy(nb);
CFRunLoopTimerContext ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.info = nb;
CFRunLoopTimerRef timerRef =
CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0,
0, 0, NotificationPort::HandleRunLoopTimer, &ctx);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopDefaultMode);
}
}
class ThreadNotifier
{
protected:
NotificationPort *mNotificationPort;
int mNotifyToken;
public:
ThreadNotifier();
~ThreadNotifier();
};
ThreadNotifier::ThreadNotifier()
: mNotificationPort(NULL)
{
mach_port_t mp;
if (notify_register_mach_port (GetNotificationName (), &mp, 0, &mNotifyToken) == NOTIFY_STATUS_OK) {
mNotificationPort = new NotificationPort (mp);
mNotificationPort->enable ();
}
}
ThreadNotifier::~ThreadNotifier()
{
if (mNotificationPort) {
notify_cancel (mNotifyToken);
delete mNotificationPort;
}
}
ModuleNexus<ThreadNexus<ThreadNotifier> > threadInfo;
static void InitializeNotifications ()
{
threadInfo()(); }
EventListener::EventListener (NotificationDomain domain, NotificationMask eventMask)
: mDomain (domain), mMask (eventMask)
{
InitializeNotifications ();
}
EventListener::~EventListener ()
{
StLock<Mutex> lock (gNotificationLock ());
EventListenerList::iterator it = std::find (gEventListeners ().begin (),
gEventListeners ().end (),
this);
if (it != gEventListeners ().end ())
{
gEventListeners ().erase (it);
}
}
void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
{
}
void EventListener::FinishedInitialization(EventListener *eventListener)
{
StLock<Mutex> lock (gNotificationLock ());
gEventListeners().push_back (eventListener);
}
} }