#include <notify.h>
#include "notifications.h"
#include "server.h"
#include "connection.h"
#include <securityd_client/ucspNotify.h>
Listener::ListenerMap& Listener::listeners = *(new Listener::ListenerMap);
Mutex Listener::setLock(Mutex::recursive);
Listener::Listener(NotificationDomain dom, NotificationMask evs, mach_port_t port)
: domain(dom), events(evs)
{
assert(events);
StLock<Mutex> _(setLock);
listeners.insert(ListenerMap::value_type(port, this));
secdebug("notify", "%p created for domain 0x%x events 0x%x port %d",
this, dom, evs, port);
}
Listener::~Listener()
{
secdebug("notify", "%p destroyed", this);
}
void Listener::notify(NotificationDomain domain,
NotificationEvent event, const CssmData &data)
{
RefPointer<Notification> message = new Notification(domain, event, 0, data);
StLock<Mutex> _(setLock);
sendNotification(message);
}
void Listener::notify(NotificationDomain domain,
NotificationEvent event, uint32 sequence, const CssmData &data)
{
Connection ¤t = Server::active().connection();
RefPointer<Notification> message = new Notification(domain, event, sequence, data);
if (current.inSequence(message)) {
StLock<Mutex> _(setLock);
sendNotification(message);
while (RefPointer<Notification> next = current.popNotification())
sendNotification(next);
}
}
void Listener::sendNotification(Notification *message)
{
for (ListenerMap::const_iterator it = listeners.begin();
it != listeners.end(); it++) {
Listener *listener = it->second;
if (listener->domain == kNotificationDomainAll || (message->domain == listener->domain && listener->wants(message->event)))
listener->notifyMe(message);
}
}
bool Listener::remove(Port port)
{
typedef ListenerMap::iterator Iterator;
StLock<Mutex> _(setLock);
pair<Iterator, Iterator> range = listeners.equal_range(port);
if (range.first == range.second)
return false;
assert(range.first != listeners.end());
secdebug("notify", "remove port %d", port.port());
#if !defined(NDEBUG)
for (Iterator it = range.first; it != range.second; it++) {
assert(it->first == port);
secdebug("notify", "%p listener removed", it->second.get());
}
#endif //NDEBUG
listeners.erase(range.first, range.second);
port.destroy();
return true; }
Listener::Notification::Notification(NotificationDomain inDomain,
NotificationEvent inEvent, uint32 seq, const CssmData &inData)
: domain(inDomain), event(inEvent), sequence(seq), data(Allocator::standard(), inData)
{
secdebug("notify", "%p notification created domain 0x%lx event %ld seq %ld",
this, domain, event, sequence);
}
Listener::Notification::~Notification()
{
secdebug("notify", "%p notification done domain 0x%lx event %ld seq %ld",
this, domain, event, sequence);
}
bool Listener::JitterBuffer::inSequence(Notification *message)
{
if (message->sequence == mNotifyLast + 1) { mNotifyLast++; return true; } else {
secdebug("notify-jit", "%p out of sequence (last %ld got %ld); buffering",
message, mNotifyLast, message->sequence);
mBuffer[message->sequence] = message; return false; }
}
RefPointer<Listener::Notification> Listener::JitterBuffer::popNotification()
{
JBuffer::iterator it = mBuffer.find(mNotifyLast + 1); if (it == mBuffer.end())
return NULL; else {
RefPointer<Notification> result = it->second; mBuffer.erase(it); secdebug("notify-jit", "%p retrieved from jitter buffer", result.get());
return result; }
}
SharedMemoryListener::SharedMemoryListener(const char* segmentName, SegmentOffsetType segmentSize) :
Listener (kNotificationDomainAll, kNotificationAllEvents),
SharedMemoryServer (segmentName, segmentSize),
mActive (false)
{
if (segmentName == NULL)
{
secdebug("notify", "Attempted to start securityd with a NULL segmentName");
exit(1);
}
}
SharedMemoryListener::~SharedMemoryListener ()
{
}
const double kServerWait = 0.005;
void SharedMemoryListener::notifyMe(Notification* notification)
{
const void* data = notification->data.data();
UInt32 length = notification->data.length();
if (length > 16384) return;
WriteMessage (notification->domain, notification->event, data, length);
if (!mActive)
{
Server::active().setTimer (this, Time::Interval(kServerWait));
mActive = true;
}
}
void SharedMemoryListener::action ()
{
secdebug("notify", "Posted notification to clients.");
notify_post (mSegmentName.c_str ());
mActive = false;
}