Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 
21 #include "asterisk/optional_api.h"
22 #include "asterisk/utils.h"
23 #include "asterisk/vector.h"
24 
25 #if defined(OPTIONAL_API)
26 
27 /*
28  * \file Optional API innards.
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 */
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 */
57 struct optional_api {
58  /*! Pointer to the implementation function; could be null */
59  ast_optional_fn impl;
60  /*! Users of the API */
62  /*! Name of the optional API function */
63  char symname[];
64 };
65 
66 /*! Vector of \ref optional_api functions */
67 AST_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  */
77 static void optional_api_user_destroy(struct optional_api_user *user)
78 {
79  *user->optional_ref = user->stub;
80  ast_free(user);
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  * \return \c NULL on error.
92  */
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  */
118 static 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  * \return \c NULL on error.
133  */
134 static 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 sysname Name of the function to look up.
155  * \return Corresponding \ref optional_api.
156  * \return \c NULL on error.
157  */
158 static 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  */
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  */
198 static 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)) {
206  } else if (!impl) {
207  /* No users or impl means we should delete this api. */
209  }
210 }
211 
212 void 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 
222 void 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 
232 void 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 
257 void 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)) {
267  }
268  }
269 }
270 #endif /* defined(OPTIONAL_API) */
static char user[512]
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
AST_VECTOR(struct optional_api *)
Free an optional_api_user.
Definition: optional_api.c:67
void ast_optional_api_provide(const char *symname, ast_optional_fn impl)
Definition: optional_api.c:212
Asterisk main include file. File version handling, generic pbx functions.
Optional API function macros.
An optional API.
Definition: optional_api.c:57
#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
static struct optional_api_user * optional_api_user_create(ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
Create an optional_api_user.
Definition: optional_api.c:93
#define AST_VECTOR_ELEM_DEFAULT_CMP(elem, value)
Default comparator for AST_VECTOR_REMOVE_ELEM_UNORDERED()
Definition: vector.h:564
A user of an optional API.
Definition: optional_api.c:47
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
static struct optional_api * optional_api_create(const char *symname)
Create and link an optional_api.
Definition: optional_api.c:134
#define NULL
Definition: resample.c:96
static void optional_api_set_impl(struct optional_api *api, ast_optional_fn impl)
Sets the implementation function pointer for an api.
Definition: optional_api.c:198
Utility functions.
static void optional_api_destroy(struct optional_api *api)
Free an optional_api.
Definition: optional_api.c:118
#define AST_VECTOR_ELEM_CLEANUP_NOOP(elem)
Vector element cleanup that does nothing.
Definition: vector.h:573
ast_optional_fn impl
Definition: optional_api.c:59
void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl)
Definition: optional_api.c:222
#define USER_OPTIONAL_REF_CMP(ele, value)
struct ao2_container * users
void DO_CRASH_NORETURN ast_do_crash(void)
Force a crash if DO_CRASH is defined.
Definition: main/utils.c:2552
void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
Definition: optional_api.c:232
#define ast_free(a)
Definition: astmm.h:182
static void optional_api_user_relink(struct optional_api_user *user, struct optional_api *api)
Re-links a given user against its associated api.
Definition: optional_api.c:181
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref, const char *module)
Definition: optional_api.c:257
Vector container support.
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:733
structure to hold users read from users.conf
ast_optional_fn * optional_ref
Definition: optional_api.c:49
#define OPTIONAL_API_SYMNAME_CMP(ele, value)
static struct optional_api * get_api(const char *symname)
Gets (or creates) the optional_api for the given function.
Definition: optional_api.c:158
ast_optional_fn stub
Definition: optional_api.c:51
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:865