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