Asterisk - The Open Source Telephony Project GIT-master-7e7a603
optional_api.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * David M. Lee, II <dlee@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19#include "asterisk.h"
20
22#include "asterisk/utils.h"
23#include "asterisk/vector.h"
24
25#if defined(OPTIONAL_API)
26
27/*!
28 * \file
29 *
30 * The calls to ast_optional_api_*() happen implicitly from \c __constructor__
31 * calls which are defined in header files. This means that some number of them
32 * happen before main() is called. This makes calling most Asterisk APIs
33 * dangerous, since we could be called before they are initialized. This
34 * includes things like AO2, malloc debug, and static mutexes.
35 *
36 * Another limitation is that most functions are called from the midst of
37 * dlopen() or dlclose(), and there is no opportunity to return a failure code.
38 * The best we can do is log an error, and call ast_do_crash().
39 *
40 * Fortunately, there are some constraints that help us out. The \c
41 * ast_optional_api_*() are called during module loads, which happens either
42 * before main(), or during dlopen() calls. These are already serialized, so we
43 * don't have to lock ourselves.
44 */
45
46/*! \brief A user of an optional API */
47struct optional_api_user {
48 /*! Pointer to function pointer to link */
49 ast_optional_fn *optional_ref;
50 /*! Stub to use when impl is unavailable */
51 ast_optional_fn stub;
52 /*! Name of the module using the API */
53 char module[];
54};
55
56/*! \brief An optional API */
57struct optional_api {
58 /*! Pointer to the implementation function; could be null */
59 ast_optional_fn impl;
60 /*! Users of the API */
61 AST_VECTOR(, struct optional_api_user *) users;
62 /*! Name of the optional API function */
63 char symname[];
64};
65
66/*! Vector of \ref optional_api functions */
67AST_VECTOR(, struct optional_api *) apis;
68
69#define USER_OPTIONAL_REF_CMP(ele, value) (ele->optional_ref == value)
70#define OPTIONAL_API_SYMNAME_CMP(ele, value) (!strcmp(ele->symname, value))
71
72/*!
73 * \brief Free an \ref optional_api_user.
74 *
75 * \param user User struct to free.
76 */
77static void optional_api_user_destroy(struct optional_api_user *user)
78{
79 *user->optional_ref = user->stub;
81}
82
83/*!
84 * \brief Create an \ref optional_api_user.
85 *
86 * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
87 * \param stub Stub function to link to when impl is not available.
88 * \param module Name of the module requesting the API.
89 *
90 * \return New \ref optional_api_user.
91 * \retval NULL on error.
92 */
93static struct optional_api_user *optional_api_user_create(
94 ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
95{
96 struct optional_api_user *user;
97 size_t size = sizeof(*user) + strlen(module) + 1;
98
99 user = ast_calloc(1, size);
100 if (!user) {
101 ast_do_crash();
102
103 return NULL;
104 }
105
106 user->optional_ref = optional_ref;
107 user->stub = stub;
108 strcpy(user->module, module); /* SAFE */
109
110 return user;
111}
112
113/*!
114 * \brief Free an \ref optional_api.
115 *
116 * \param api API struct to free.
117 */
118static void optional_api_destroy(struct optional_api *api)
119{
122 AST_VECTOR_CALLBACK_VOID(&api->users, optional_api_user_destroy);
123 AST_VECTOR_FREE(&api->users);
124 ast_free(api);
125}
126
127/*!
128 * \brief Create and link an \ref optional_api.
129 *
130 * \param symname Name of the optional function.
131 * \return New \ref optional_api.
132 * \retval NULL on error.
133 */
134static struct optional_api *optional_api_create(const char *symname)
135{
136 struct optional_api *api;
137
138 api = ast_calloc(1, sizeof(*api) + strlen(symname) + 1);
139 if (!api || AST_VECTOR_APPEND(&apis, api)) {
140 ast_free(api);
141 ast_do_crash();
142
143 return NULL;
144 }
145
146 strcpy(api->symname, symname); /* SAFE */
147
148 return api;
149}
150
151/*!
152 * \brief Gets (or creates) the \ref optional_api for the given function.
153 *
154 * \param symname Name of the function to look up.
155 * \return Corresponding \ref optional_api.
156 * \retval NULL on error.
157 */
158static struct optional_api *get_api(const char *symname)
159{
160 struct optional_api **api;
161
162 /* Find one, if we already have it */
163 api = AST_VECTOR_GET_CMP(&apis, symname, OPTIONAL_API_SYMNAME_CMP);
164 if (api) {
165 return *api;
166 }
167
168 /* API not found. Build one */
169 return optional_api_create(symname);
170}
171
172/*!
173 * \brief Re-links a given \a user against its associated \a api.
174 *
175 * If the \a api has an implementation, the \a user is linked to that
176 * implementation. Otherwise, the \a user is linked to its \a stub.
177 *
178 * \param user \ref optional_api_user to link.
179 * \param api \ref optional_api to link.
180 */
181static void optional_api_user_relink(struct optional_api_user *user,
182 struct optional_api *api)
183{
184 if (api->impl && *user->optional_ref != api->impl) {
185 *user->optional_ref = api->impl;
186 } else if (!api->impl && *user->optional_ref != user->stub) {
187 *user->optional_ref = user->stub;
188 }
189}
190
191/*!
192 * \brief Sets the implementation function pointer for an \a api.
193 *
194 * \param api API to implement/stub out.
195 * \param impl Pointer to implementation function. Can be 0 to remove
196 * implementation.
197 */
198static void optional_api_set_impl(struct optional_api *api,
199 ast_optional_fn impl)
200{
201 api->impl = impl;
202
203 /* re-link all users */
204 if (AST_VECTOR_SIZE(&api->users)) {
205 AST_VECTOR_CALLBACK_VOID(&api->users, optional_api_user_relink, api);
206 } else if (!impl) {
207 /* No users or impl means we should delete this api. */
208 optional_api_destroy(api);
209 }
210}
211
212void ast_optional_api_provide(const char *symname, ast_optional_fn impl)
213{
214 struct optional_api *api;
215
216 api = get_api(symname);
217 if (api) {
218 optional_api_set_impl(api, impl);
219 }
220}
221
222void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl)
223{
224 struct optional_api *api;
225
226 api = get_api(symname);
227 if (api) {
228 optional_api_set_impl(api, 0);
229 }
230}
231
232void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
233 ast_optional_fn stub, const char *module)
234{
235 struct optional_api_user *user;
236 struct optional_api *api;
237
238 api = get_api(symname);
239 if (!api) {
240 return;
241 }
242
243 user = optional_api_user_create(optional_ref, stub, module);
244 if (!user) {
245 return;
246 }
247
248 /* Add user to the API */
249 if (!AST_VECTOR_APPEND(&api->users, user)) {
250 optional_api_user_relink(user, api);
251 } else {
252 optional_api_user_destroy(user);
253 ast_do_crash();
254 }
255}
256
257void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
258 const char *module)
259{
260 struct optional_api *api;
261
262 api = get_api(symname);
263 if (api) {
264 AST_VECTOR_REMOVE_CMP_UNORDERED(&api->users, optional_ref, USER_OPTIONAL_REF_CMP, optional_api_user_destroy);
265 if (!api->impl && !AST_VECTOR_SIZE(&api->users)) {
266 optional_api_destroy(api);
267 }
268 }
269}
270#endif /* defined(OPTIONAL_API) */
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Optional API function macros.
static char user[512]
struct ao2_container * users
#define NULL
Definition: resample.c:96
structure to hold users read from users.conf
Utility functions.
void DO_CRASH_NORETURN ast_do_crash(void)
Force a crash if DO_CRASH is defined.
Definition: utils.c:2804
Vector container support.
#define AST_VECTOR_ELEM_CLEANUP_NOOP(elem)
Vector element cleanup that does nothing.
Definition: vector.h:571
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_REMOVE_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove an element from a vector that matches the given comparison.
Definition: vector.h:488
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:731
#define AST_VECTOR_ELEM_DEFAULT_CMP(elem, value)
Default comparator for AST_VECTOR_REMOVE_ELEM_UNORDERED()
Definition: vector.h:564
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:862
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44