Remove ASTERISK_REGISTER_FILE.
[asterisk/asterisk.git] / main / optional_api.c
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
21 #include "asterisk/optional_api.h"
22 #include "asterisk/utils.h"
23
24 #if defined(OPTIONAL_API)
25
26 /*
27  * \file Optional API innards.
28  *
29  * The calls to ast_optional_api_*() happen implicitly from \c __constructor__
30  * calls which are defined in header files. This means that some number of them
31  * happen before main() is called. This makes calling most Asterisk APIs
32  * dangerous, since we could be called before they are initialized. This
33  * includes things like AO2, malloc debug, and static mutexes.
34  *
35  * Another limitation is that most functions are called from the midst of
36  * dlopen() or dlclose(), and there is no opportunity to return a failure code.
37  * The best we can do is log an error, and call ast_do_crash().
38  *
39  * Fortunately, there are some constraints that help us out. The \c
40  * ast_optional_api_*() are called during module loads, which happens either
41  * before main(), or during dlopen() calls. These are already serialized, so we
42  * don't have to lock ourselves.
43  */
44
45 /*! \brief A user of an optional API */
46 struct optional_api_user {
47         /*! Pointer to function pointer to link */
48         ast_optional_fn *optional_ref;
49         /*! Stub to use when impl is unavailable */
50         ast_optional_fn stub;
51         /*! Name of the module using the API */
52         char module[];
53 };
54
55 /*! \brief An optional API */
56 struct optional_api {
57         /*! Pointer to the implementation function; could be null */
58         ast_optional_fn impl;
59         /*! Variable length array of users of this API */
60         struct optional_api_user **users;
61         /*! Allocated size of the \a users array */
62         size_t users_maxlen;
63         /*! Number of entries in the \a users array */
64         size_t users_len;
65         /*! Name of the optional API function */
66         char symname[];
67 };
68
69 /*!
70  * \brief Free an \ref optional_api_user.
71  *
72  * \param user User struct to free.
73  */
74 static void optional_api_user_destroy(struct optional_api_user *user)
75 {
76         *user->optional_ref = user->stub;
77         ast_std_free(user);
78 }
79
80 /*!
81  * \brief Create an \ref optional_api_user.
82  *
83  * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
84  * \param stub Stub function to link to when impl is not available.
85  * \param module Name of the module requesting the API.
86  *
87  * \return New \ref optional_api_user.
88  * \return \c NULL on error.
89  */
90 static struct optional_api_user *optional_api_user_create(
91         ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
92 {
93         struct optional_api_user *user;
94         size_t size = sizeof(*user) + strlen(module) + 1;
95
96         user = ast_std_calloc(1, size);
97         if (!user) {
98                 return NULL;
99         }
100
101         user->optional_ref = optional_ref;
102         user->stub = stub;
103         strcpy(user->module, module); /* SAFE */
104
105         return user;
106 }
107
108 /*!
109  * \brief Free an \ref optional_api.
110  *
111  * \param api API struct to free.
112  */
113 static void optional_api_destroy(struct optional_api *api)
114 {
115         while (api->users_len--) {
116                 optional_api_user_destroy(api->users[api->users_len]);
117         }
118         ast_std_free(api->users);
119         api->users = NULL;
120         api->users_maxlen = 0;
121         ast_std_free(api);
122 }
123
124 /*!
125  * \brief Create an \ref optional_api.
126  *
127  * \param symname Name of the optional function.
128  * \return New \ref optional_api.
129  * \return \c NULL on error.
130  */
131 static struct optional_api *optional_api_create(const char *symname)
132 {
133         struct optional_api *api;
134         size_t size;
135
136         size = sizeof(*api) + strlen(symname) + 1;
137         api = ast_std_calloc(1, size);
138         if (!api) {
139                 ast_log(LOG_ERROR, "Failed to allocate api\n");
140                 return NULL;
141         }
142
143         strcpy(api->symname, symname); /* SAFE */
144
145         return api;
146 }
147
148 /*! Array of \ref optional_api functions */
149 struct {
150         /*! Variable length array of API's */
151         struct optional_api **list;
152         /*! Allocated size of the \a list array */
153         size_t maxlen;
154         /*! Number of entries in the \a list array */
155         size_t len;
156 } apis;
157
158 /*!
159  * \brief Gets (or creates) the \ref optional_api for the given function.
160  *
161  * \param sysname Name of the function to look up.
162  * \return Corresponding \ref optional_api.
163  * \return \c NULL on error.
164  */
165 static struct optional_api *get_api(const char *symname)
166 {
167         struct optional_api *api;
168         size_t i;
169
170         /* Find one, if we already have it */
171         if (apis.list) {
172                 for (i = 0; i < apis.len; ++i) {
173                         if (strcmp(symname, apis.list[i]->symname) == 0) {
174                                 return apis.list[i];
175                         }
176                 }
177         }
178
179         /* API not found. Build one */
180         api = optional_api_create(symname);
181         if (!api) {
182                 return NULL;
183         }
184
185         /* Grow the list, if needed */
186         if (apis.len + 1 > apis.maxlen) {
187                 size_t new_maxlen = apis.maxlen ? 2 * apis.maxlen : 1;
188                 struct optional_api **new_list;
189
190                 new_list = ast_std_realloc(apis.list, new_maxlen * sizeof(*new_list));
191                 if (!new_list) {
192                         optional_api_destroy(api);
193                         ast_log(LOG_ERROR, "Failed to allocate api list\n");
194                         return NULL;
195                 }
196
197                 apis.maxlen = new_maxlen;
198                 apis.list = new_list;
199         }
200
201         apis.list[apis.len++] = api;
202
203         return api;
204 }
205
206 /*!
207  * \brief Re-links a given \a user against its associated \a api.
208  *
209  * If the \a api has an implementation, the \a user is linked to that
210  * implementation. Otherwise, the \a user is linked to its \a stub.
211  *
212  * \param user \ref optional_api_user to link.
213  * \param api \ref optional_api to link.
214  */
215 static void optional_api_user_relink(struct optional_api_user *user,
216         struct optional_api *api)
217 {
218         if (api->impl && *user->optional_ref != api->impl) {
219                 *user->optional_ref = api->impl;
220         } else if (!api->impl && *user->optional_ref != user->stub) {
221                 *user->optional_ref = user->stub;
222         }
223 }
224
225 /*!
226  * \brief Sets the implementation function pointer for an \a api.
227  *
228  * \param api API to implement/stub out.
229  * \param impl Pointer to implementation function. Can be 0 to remove
230  *             implementation.
231  */
232 static void optional_api_set_impl(struct optional_api *api,
233         ast_optional_fn impl)
234 {
235         size_t i;
236
237         api->impl = impl;
238
239         /* re-link all users */
240         for (i = 0; i < api->users_len; ++i) {
241                 optional_api_user_relink(api->users[i], api);
242         }
243 }
244
245 void ast_optional_api_provide(const char *symname, ast_optional_fn impl)
246 {
247         struct optional_api *api;
248
249         api = get_api(symname);
250         if (!api) {
251                 ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
252                 ast_do_crash();
253                 return;
254         }
255
256         optional_api_set_impl(api, impl);
257 }
258
259 void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl)
260 {
261         struct optional_api *api;
262
263         api = get_api(symname);
264         if (!api) {
265                 ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
266                 ast_do_crash();
267                 return;
268         }
269
270         optional_api_set_impl(api, 0);
271 }
272
273 void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
274         ast_optional_fn stub, const char *module)
275 {
276         struct optional_api_user *user;
277         struct optional_api *api;
278
279
280         api = get_api(symname);
281         if (!api) {
282                 ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
283                 ast_do_crash();
284                 return;
285         }
286
287         user = optional_api_user_create(optional_ref, stub, module);
288         if (!user) {
289                 ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
290                 ast_do_crash();
291                 return;
292         }
293
294         /* Add user to the API */
295         if (api->users_len + 1 > api->users_maxlen) {
296                 size_t new_maxlen = api->users_maxlen ? 2 * api->users_maxlen : 1;
297                 struct optional_api_user **new_list;
298
299                 new_list = ast_std_realloc(api->users, new_maxlen * sizeof(*new_list));
300                 if (!new_list) {
301                         optional_api_user_destroy(user);
302                         ast_log(LOG_ERROR, "Failed to allocate api list\n");
303                         ast_do_crash();
304                         return;
305                 }
306
307                 api->users_maxlen = new_maxlen;
308                 api->users = new_list;
309         }
310
311         api->users[api->users_len++] = user;
312
313         optional_api_user_relink(user, api);
314 }
315
316 void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
317         const char *module)
318 {
319         struct optional_api *api;
320         size_t i;
321
322         api = get_api(symname);
323         if (!api) {
324                 ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
325                 ast_do_crash();
326                 return;
327         }
328
329         for (i = 0; i < api->users_len; ++i) {
330                 struct optional_api_user *user = api->users[i];
331
332                 if (user->optional_ref == optional_ref) {
333                         if (*user->optional_ref != user->stub) {
334                                 *user->optional_ref = user->stub;
335                         }
336
337                         /* Remove from the list */
338                         api->users[i] = api->users[--api->users_len];
339
340                         optional_api_user_destroy(user);
341                         return;
342                 }
343         }
344
345         ast_log(LOG_ERROR, "%s: Could not find user %s\n", symname, module);
346 }
347
348 #endif /* defined(OPTIONAL_API) */