]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2003-2004,2006,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 |