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