1/*
2 * Copyright (c) 2000-2001,2003-2004,2006,2011-2012,2014 Apple 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
25//
26// Encapsulate the callback mechanism of CSSM.
27//
28#include <security_cdsa_utilities/callback.h>
29
30
31//
32// Invoke a callback
33//
34void ModuleCallback::operator () (CSSM_MODULE_EVENT event,
35                                  const Guid &guid, uint32 subId,
36                                  CSSM_SERVICE_TYPE serviceType) const
37{
38    try
39    {
40        if (mCallback)
41            if (CSSM_RETURN err = mCallback(&guid, mContext, subId, serviceType, event))
42                CssmError::throwMe(err);
43    }
44    catch (...)
45    {
46    }
47}
48
49
50//
51// Manage Callback sets.
52// THREADS: Caller is ensuring single-thread access on these calls.
53//
54void ModuleCallbackSet::insert(const ModuleCallback &newCallback)
55{
56    callbacks.insert(CallbackMap::value_type(newCallback, new CountingMutex));
57}
58
59void ModuleCallbackSet::erase(const ModuleCallback &oldCallback)
60{
61    CallbackMap::iterator it = callbacks.find(oldCallback);
62    if (it == callbacks.end())	// not registered; fail
63        CssmError::throwMe(CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
64    CountingMutex *counter = it->second;
65    {
66        StLock<Mutex> _(*counter);
67        if (!counter->isIdle()) // callbacks are scheduled against this
68            CssmError::throwMe(CSSM_ERRCODE_FUNCTION_FAILED);	// @#module is busy
69    }
70    // counter is zero (idle), and we hold the entry lock (via our caller)
71    delete counter;
72    callbacks.erase(it);
73}
74
75
76//
77// Invoke an entire callback set.
78// THREADS: Caller is ensuring  single-thread access on these calls.
79//
80void ModuleCallbackSet::operator () (CSSM_MODULE_EVENT event,
81                                     const Guid &guid, uint32 subId,
82                                     CSSM_SERVICE_TYPE serviceType) const
83{
84    if (callbacks.empty())	// nothing to do; quick exit
85        return;
86
87#if _USE_THREADS == _USE_NO_THREADS || defined(SYNCHRONOUS_CALLBACKS)
88    // no threading model supported - we HAVE to do this right here
89    // note that the user better not re-enter CSSM too much,
90    // or we might deadlock...
91    for (CallbackMap::const_iterator it = callbacks.begin();
92         it != callbacks.end(); it++) {
93        it->first(event, guid, subId, serviceType);
94    }
95#else // real threads available
96    // lock down all callback elements - still protected by global lock (via caller)
97    for (CallbackMap::iterator it = callbacks.begin();
98         it != callbacks.end(); it++)
99        it->second->enter();
100
101    // get out of this thread - now!
102    (new Runner(callbacks, event, guid, subId, serviceType))->run();
103#endif
104}
105
106void ModuleCallbackSet::Runner::action()
107{
108    //
109    // NOTE WELL: Our callbacks map shares (pointed-to) values with the ModuleCallbackSet
110    // we were created from. Some of these values may be dangling pointers since they have
111    // been destroyed by other threads, but only *after* we are done with them, since
112    // we must call exit() on them before they become eligible for destruction.
113    // In all cases, it is the responsibility of other threads to destroy those mutexi.
114    //
115    // @@@ Could also fan out to multiple callback threads in parallel.
116    for (CallbackMap::iterator it = callbacks.begin();
117         it != callbacks.end(); it++) {
118        //@@@ safety vs. convenience - recheck
119        it->first(event, guid, subserviceId, serviceType);
120        it->second->exit();
121    }
122}
123