--- /dev/null
+/*
+ * Copyright (c) 2000-2001,2003-2004,2006,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// Encapsulate the callback mechanism of CSSM.
+//
+#include <security_cdsa_utilities/callback.h>
+
+
+//
+// Invoke a callback
+//
+void ModuleCallback::operator () (CSSM_MODULE_EVENT event,
+ const Guid &guid, uint32 subId,
+ CSSM_SERVICE_TYPE serviceType) const
+{
+ try
+ {
+ if (mCallback)
+ if (CSSM_RETURN err = mCallback(&guid, mContext, subId, serviceType, event))
+ CssmError::throwMe(err);
+ }
+ catch (...)
+ {
+ }
+}
+
+
+//
+// Manage Callback sets.
+// THREADS: Caller is ensuring single-thread access on these calls.
+//
+void ModuleCallbackSet::insert(const ModuleCallback &newCallback)
+{
+ callbacks.insert(CallbackMap::value_type(newCallback, new CountingMutex));
+}
+
+void ModuleCallbackSet::erase(const ModuleCallback &oldCallback)
+{
+ CallbackMap::iterator it = callbacks.find(oldCallback);
+ if (it == callbacks.end()) // not registered; fail
+ CssmError::throwMe(CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
+ CountingMutex *counter = it->second;
+ {
+ StLock<Mutex> _(*counter);
+ if (!counter->isIdle()) // callbacks are scheduled against this
+ CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED); // @#module is busy
+ }
+ // counter is zero (idle), and we hold the entry lock (via our caller)
+ delete counter;
+ callbacks.erase(it);
+}
+
+
+//
+// Invoke an entire callback set.
+// THREADS: Caller is ensuring single-thread access on these calls.
+//
+void ModuleCallbackSet::operator () (CSSM_MODULE_EVENT event,
+ const Guid &guid, uint32 subId,
+ CSSM_SERVICE_TYPE serviceType) const
+{
+ if (callbacks.empty()) // nothing to do; quick exit
+ return;
+
+#if _USE_THREADS == _USE_NO_THREADS || defined(SYNCHRONOUS_CALLBACKS)
+ // no threading model supported - we HAVE to do this right here
+ // note that the user better not re-enter CSSM too much,
+ // or we might deadlock...
+ for (CallbackMap::const_iterator it = callbacks.begin();
+ it != callbacks.end(); it++) {
+ it->first(event, guid, subId, serviceType);
+ }
+#else // real threads available
+ // lock down all callback elements - still protected by global lock (via caller)
+ for (CallbackMap::iterator it = callbacks.begin();
+ it != callbacks.end(); it++)
+ it->second->enter();
+
+ // get out of this thread - now!
+ (new Runner(callbacks, event, guid, subId, serviceType))->run();
+#endif
+}
+
+void ModuleCallbackSet::Runner::action()
+{
+ //
+ // NOTE WELL: Our callbacks map shares (pointed-to) values with the ModuleCallbackSet
+ // we were created from. Some of these values may be dangling pointers since they have
+ // been destroyed by other threads, but only *after* we are done with them, since
+ // we must call exit() on them before they become eligible for destruction.
+ // In all cases, it is the responsibility of other threads to destroy those mutexi.
+ //
+ // @@@ Could also fan out to multiple callback threads in parallel.
+ for (CallbackMap::iterator it = callbacks.begin();
+ it != callbacks.end(); it++) {
+ //@@@ safety vs. convenience - recheck
+ it->first(event, guid, subserviceId, serviceType);
+ it->second->exit();
+ }
+}