]> git.saurik.com Git - apple/security.git/blob - libsecurityd/lib/eventlistener.cpp
Security-55471.tar.gz
[apple/security.git] / libsecurityd / lib / eventlistener.cpp
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
33 using namespace MachPlusPlus;
34
35
36 namespace Security {
37 namespace SecurityServer {
38
39 typedef RefPointer<EventListener> EventPointer;
40 typedef std::list<EventPointer> EventListenerList;
41
42 static 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
56 class SharedMemoryClientMaker
57 {
58 private:
59 SharedMemoryClient mClient;
60
61 public:
62 SharedMemoryClientMaker ();
63 SharedMemoryClient* Client ();
64 };
65
66
67
68 SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize)
69 {
70 }
71
72
73
74 SharedMemoryClient* SharedMemoryClientMaker::Client ()
75 {
76 return &mClient;
77 }
78
79
80
81 ModuleNexus<EventListenerList> gEventListeners;
82 ModuleNexus<Mutex> gNotificationLock;
83 ModuleNexus<SharedMemoryClientMaker> gMemoryClient;
84
85 class NotificationPort : public MachPlusPlus::CFAutoPort
86 {
87 protected:
88 SharedMemoryClient *mClient;
89
90 void ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur);
91 static void HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info);
92
93 public:
94 NotificationPort (mach_port_t port);
95 virtual ~NotificationPort ();
96 virtual void receive(const MachPlusPlus::Message &msg);
97 };
98
99 NotificationPort::NotificationPort (mach_port_t mp) : CFAutoPort (mp)
100 {
101 mClient = gMemoryClient ().Client ();
102 }
103
104
105
106 NotificationPort::~NotificationPort ()
107 {
108 }
109
110
111
112 void 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
148 typedef void (^NotificationBlock)();
149
150
151
152 void 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
166 void 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
221 class ThreadNotifier
222 {
223 protected:
224 NotificationPort *mNotificationPort;
225 int mNotifyToken;
226
227 public:
228 ThreadNotifier();
229 ~ThreadNotifier();
230 };
231
232
233
234 ThreadNotifier::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
246 ThreadNotifier::~ThreadNotifier()
247 {
248 if (mNotificationPort) {
249 notify_cancel (mNotifyToken);
250 delete mNotificationPort;
251 }
252 }
253
254
255
256 ModuleNexus<ThreadNexus<ThreadNotifier> > threadInfo;
257
258
259
260 static void InitializeNotifications ()
261 {
262 threadInfo()(); // cause the notifier for this thread to initialize
263 }
264
265
266
267 EventListener::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 //
278 EventListener::~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
295 void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
296 {
297 }
298
299
300
301 void 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