optional_api.c 8.5 KB

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