1/*
2 * Copyright (c) 2003-2004,2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <list>
25#include <security_utilities/globalizer.h>
26#include <security_utilities/threading.h>
27#include "eventlistener.h"
28#include "SharedMemoryClient.h"
29#include <notify.h>
30#include "sscommon.h"
31#include <sys/syslog.h>
32
33using namespace MachPlusPlus;
34
35
36namespace Security {
37namespace SecurityServer {
38
39typedef RefPointer<EventListener> EventPointer;
40typedef std::list<EventPointer> EventListenerList;
41
42static const char* GetNotificationName ()
43{
44	// the name we give the client depends on the value of the environment variable "SECURITYSERVER"
45	const char* name = getenv (SECURITYSERVER_BOOTSTRAP_ENV);
46	if (name == NULL)
47	{
48		name = SECURITY_MESSAGES_NAME;
49	}
50
51	return name;
52}
53
54
55
56class SharedMemoryClientMaker
57{
58private:
59	SharedMemoryClient mClient;
60
61public:
62	SharedMemoryClientMaker ();
63	SharedMemoryClient* Client ();
64};
65
66
67
68SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize)
69{
70}
71
72
73
74SharedMemoryClient* SharedMemoryClientMaker::Client ()
75{
76	return &mClient;
77}
78
79
80
81ModuleNexus<EventListenerList> gEventListeners;
82ModuleNexus<Mutex> gNotificationLock;
83ModuleNexus<SharedMemoryClientMaker> gMemoryClient;
84
85class NotificationPort : public MachPlusPlus::CFAutoPort
86{
87protected:
88	SharedMemoryClient *mClient;
89
90    void ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur);
91    static void HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info);
92
93public:
94	NotificationPort (mach_port_t port);
95	virtual ~NotificationPort ();
96	virtual void receive(const MachPlusPlus::Message &msg);
97};
98
99NotificationPort::NotificationPort (mach_port_t mp) : CFAutoPort (mp)
100{
101	mClient = gMemoryClient ().Client ();
102}
103
104
105
106NotificationPort::~NotificationPort ()
107{
108}
109
110
111
112void NotificationPort::ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur)
113{
114    EventListenerList& eventList = gEventListeners();
115
116	// route the message to its destination
117    u_int32_t* ptr = (u_int32_t*) buffer;
118
119    // we have a message, do the semantics...
120    SecurityServer::NotificationDomain domain = (SecurityServer::NotificationDomain) OSSwapBigToHostInt32 (*ptr++);
121    SecurityServer::NotificationEvent event = (SecurityServer::NotificationEvent) OSSwapBigToHostInt32 (*ptr++);
122    CssmData data ((u_int8_t*) ptr, buffer + length - (u_int8_t*) ptr);
123
124    EventListenerList::iterator it = eventList.begin ();
125    while (it != eventList.end ())
126    {
127        try
128        {
129            EventPointer ep = *it++;
130            if (ep->GetDomain () == domain &&
131                (ep->GetMask () & (1 << event)) != 0)
132            {
133                ep->consume (domain, event, data);
134            }
135        }
136        catch (CssmError &e)
137        {
138            if (e.error != CSSM_ERRCODE_INTERNAL_ERROR)
139            {
140                throw;
141            }
142        }
143    }
144}
145
146
147
148typedef void (^NotificationBlock)();
149
150
151
152void NotificationPort::HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info)
153{
154    // reconstruct our context and call it
155    NotificationBlock nb = (NotificationBlock) info;
156    nb();
157
158    // clean up
159    Block_release(nb);
160    CFRunLoopTimerInvalidate(timer);
161    CFRelease(timer);
162}
163
164
165
166void NotificationPort::receive (const MachPlusPlus::Message &msg)
167{
168    /*
169        Read each notification received and post a timer for each with an expiration of
170        zero.  I'd prefer to use a notification here, but I can't because, according to
171        the documentation, each application may only have one notification center and
172        the main application should have the right to pick the one it needs.
173    */
174
175    SegmentOffsetType length;
176    UnavailableReason ur;
177
178    bool result;
179
180    while (true)
181    {
182        u_int8_t *buffer = new u_int8_t[kSharedMemoryPoolSize];
183
184       {
185            StLock<Mutex> lock (gNotificationLock ());
186            result = mClient->ReadMessage(buffer, length, ur);
187            if (!result)
188            {
189                delete [] buffer;
190                return;
191            }
192        }
193
194        // make a block that contains our data
195        NotificationBlock nb =
196            ^{
197                ReceiveImplementation(buffer, length, ur);
198                delete [] buffer;
199            };
200
201        // keep it in scope
202        nb = Block_copy(nb);
203
204        // set up to run the next time the run loop fires
205        CFRunLoopTimerContext ctx;
206        memset(&ctx, 0, sizeof(ctx));
207        ctx.info = nb;
208
209        // make a run loop timer
210        CFRunLoopTimerRef timerRef =
211            CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0,
212                                 0, 0, NotificationPort::HandleRunLoopTimer, &ctx);
213
214        // install it to be run.
215        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopDefaultMode);
216    }
217}
218
219
220
221class ThreadNotifier
222{
223protected:
224	NotificationPort *mNotificationPort;
225	int mNotifyToken;
226
227public:
228	ThreadNotifier();
229	~ThreadNotifier();
230};
231
232
233
234ThreadNotifier::ThreadNotifier()
235    : mNotificationPort(NULL)
236{
237	mach_port_t mp;
238	if (notify_register_mach_port (GetNotificationName (), &mp, 0, &mNotifyToken) == NOTIFY_STATUS_OK) {
239		mNotificationPort = new NotificationPort (mp);
240		mNotificationPort->enable ();
241	}
242}
243
244
245
246ThreadNotifier::~ThreadNotifier()
247{
248	if (mNotificationPort) {
249		notify_cancel (mNotifyToken);
250		delete mNotificationPort;
251	}
252}
253
254
255
256ModuleNexus<ThreadNexus<ThreadNotifier> > threadInfo;
257
258
259
260static void InitializeNotifications ()
261{
262	threadInfo()(); // cause the notifier for this thread to initialize
263}
264
265
266
267EventListener::EventListener (NotificationDomain domain, NotificationMask eventMask)
268	: mDomain (domain), mMask (eventMask)
269{
270	// make sure that notifications are turned on.
271	InitializeNotifications ();
272}
273
274
275//
276// StopNotification() is needed on destruction; everyone else cleans up after themselves.
277//
278EventListener::~EventListener ()
279{
280	StLock<Mutex> lock (gNotificationLock ());
281
282	// find the listener in the list and remove it
283	EventListenerList::iterator it = std::find (gEventListeners ().begin (),
284												gEventListeners ().end (),
285												this);
286	if (it != gEventListeners ().end ())
287	{
288		gEventListeners ().erase (it);
289	}
290}
291
292
293
294// get rid of the pure virtual
295void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
296{
297}
298
299
300
301void EventListener::FinishedInitialization(EventListener *eventListener)
302{
303	StLock<Mutex> lock (gNotificationLock ());
304	gEventListeners().push_back (eventListener);
305}
306
307
308
309} // end namespace SecurityServer
310} // end namespace Security
311