PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/cherokee/plugin_loader.c

https://bitbucket.org/mdavid/cherokee-webserver-svnclone
C | 558 lines | 363 code | 117 blank | 78 comment | 58 complexity | a28e3353f0d182f24392b34d0cd3393f MD5 | raw file
  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2. /* Cherokee
  3. *
  4. * Authors:
  5. * Alvaro Lopez Ortega <alvaro@alobbs.com>
  6. *
  7. * Copyright (C) 2001-2010 Alvaro Lopez Ortega
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of version 2 of the GNU General Public
  11. * License as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21. * 02110-1301, USA.
  22. */
  23. #include "common-internal.h"
  24. #include "plugin_loader.h"
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #ifdef HAVE_DLFCN_H
  28. # include <dlfcn.h>
  29. #endif
  30. #include "buffer.h"
  31. typedef cherokee_plugin_loader_entry_t entry_t;
  32. #if defined(HAVE_PTHREAD)
  33. static pthread_mutex_t dlerror_mutex = PTHREAD_MUTEX_INITIALIZER;
  34. #endif
  35. /* This is the non-embedded implementation
  36. */
  37. #include "loader.autoconf.h"
  38. #ifdef HAVE_RTLDNOW
  39. # define RTLD_BASE RTLD_NOW
  40. #else
  41. # define RTLD_BASE RTLD_LAZY
  42. #endif
  43. typedef void *func_new_t;
  44. static void
  45. add_static_entry (cherokee_plugin_loader_t *loader,
  46. const char *name,
  47. void *info)
  48. {
  49. entry_t *entry;
  50. entry = malloc (sizeof(entry_t));
  51. entry->dlopen_ref = dlopen (NULL, RTLD_BASE);
  52. entry->info = info;
  53. entry->built_in = true;
  54. cherokee_avl_add_ptr (&loader->table, (char *)name, entry);
  55. }
  56. static ret_t
  57. load_static_linked_modules (cherokee_plugin_loader_t *loader)
  58. {
  59. /* I do know that to include code in the middle of a function is hacky
  60. * and dirty, but this is the best solution that I could figure out.
  61. * If you have some idea to clean it up, please, don't hesitate and
  62. * let me know. - alo
  63. */
  64. #include "loader.autoconf.inc"
  65. return ret_ok;
  66. }
  67. static void
  68. free_entry (void *item)
  69. {
  70. entry_t *entry = (entry_t *)item;
  71. if (entry->dlopen_ref) {
  72. dlclose (entry->dlopen_ref);
  73. entry->dlopen_ref = NULL;
  74. }
  75. free (item);
  76. }
  77. ret_t
  78. cherokee_plugin_loader_init (cherokee_plugin_loader_t *loader)
  79. {
  80. ret_t ret;
  81. ret = cherokee_avl_init (&loader->table);
  82. if (unlikely(ret < ret_ok))
  83. return ret;
  84. /* Plug-in dir
  85. */
  86. ret = cherokee_buffer_init (&loader->module_dir);
  87. if (unlikely(ret < ret_ok))
  88. return ret;
  89. cherokee_buffer_add_str (&loader->module_dir, CHEROKEE_PLUGINDIR);
  90. /* Plug-in dependencies dir
  91. */
  92. ret = cherokee_buffer_init (&loader->deps_dir);
  93. if (unlikely(ret < ret_ok))
  94. return ret;
  95. cherokee_buffer_add_str (&loader->deps_dir, CHEROKEE_DEPSDIR);
  96. ret = load_static_linked_modules (loader);
  97. if (unlikely(ret < ret_ok))
  98. return ret;
  99. return ret_ok;
  100. }
  101. ret_t
  102. cherokee_plugin_loader_mrproper (cherokee_plugin_loader_t *loader)
  103. {
  104. cherokee_buffer_mrproper (&loader->module_dir);
  105. cherokee_buffer_mrproper (&loader->deps_dir);
  106. cherokee_avl_mrproper (&loader->table, free_entry);
  107. return ret_ok;
  108. }
  109. static void *
  110. get_sym_from_dlopen_handler (void *dl_handle, const char *sym)
  111. {
  112. void *re;
  113. const char *error;
  114. /* Clear the possible error and look for the symbol
  115. */
  116. CHEROKEE_MUTEX_LOCK (&dlerror_mutex);
  117. dlerror();
  118. re = (void *) dlsym(dl_handle, sym);
  119. if ((error = dlerror()) != NULL) {
  120. LOG_ERROR (CHEROKEE_ERROR_PLUGIN_LOAD_NO_SYM, sym, error);
  121. CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
  122. return NULL;
  123. }
  124. CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
  125. return re;
  126. }
  127. static ret_t
  128. dylib_open (cherokee_plugin_loader_t *loader,
  129. const char *libname,
  130. int extra_flags,
  131. void **handler_out)
  132. {
  133. ret_t ret;
  134. void *lib;
  135. int flags;
  136. cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
  137. flags = RTLD_BASE | extra_flags;
  138. /* Build the path string
  139. */
  140. ret = cherokee_buffer_add_va (&tmp, "%s/libplugin_%s." MOD_SUFFIX, loader->module_dir.buf, libname);
  141. if (unlikely(ret < ret_ok)) {
  142. cherokee_buffer_mrproper (&tmp);
  143. return ret;
  144. }
  145. /* Open the library
  146. */
  147. CHEROKEE_MUTEX_LOCK (&dlerror_mutex);
  148. lib = dlopen (tmp.buf, flags);
  149. if (lib == NULL) {
  150. LOG_ERROR (CHEROKEE_ERROR_PLUGIN_DLOPEN, dlerror(), tmp.buf);
  151. CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
  152. cherokee_buffer_mrproper (&tmp);
  153. return ret_error;
  154. }
  155. CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
  156. /* Free the memory
  157. */
  158. cherokee_buffer_mrproper (&tmp);
  159. *handler_out = lib;
  160. return ret_ok;
  161. }
  162. static ret_t
  163. execute_init_func (cherokee_plugin_loader_t *loader,
  164. const char *module,
  165. entry_t *entry)
  166. {
  167. ret_t ret;
  168. void (*init_func) (cherokee_plugin_loader_t *);
  169. cherokee_buffer_t init_name = CHEROKEE_BUF_INIT;
  170. /* Build the init function name
  171. */
  172. ret = cherokee_buffer_add_va (&init_name, "cherokee_plugin_%s_init", module);
  173. if (unlikely(ret < ret_ok)) {
  174. cherokee_buffer_mrproper (&init_name);
  175. return ret;
  176. }
  177. /* Get the function
  178. */
  179. if (entry->dlopen_ref == NULL) {
  180. cherokee_buffer_mrproper (&init_name);
  181. return ret_error;
  182. }
  183. init_func = get_sym_from_dlopen_handler (entry->dlopen_ref, init_name.buf);
  184. /* Only try to execute if it exists
  185. */
  186. if (init_func == NULL) {
  187. LOG_WARNING (CHEROKEE_ERROR_PLUGIN_NO_INIT, init_name.buf);
  188. cherokee_buffer_mrproper (&init_name);
  189. return ret_not_found;
  190. }
  191. /* Free the init function name string
  192. */
  193. cherokee_buffer_mrproper (&init_name);
  194. /* Execute the init func
  195. */
  196. init_func(loader);
  197. return ret_ok;
  198. }
  199. static ret_t
  200. get_info (cherokee_plugin_loader_t *loader,
  201. const char *module,
  202. int flags,
  203. cherokee_plugin_info_t **info,
  204. void **dl_handler)
  205. {
  206. ret_t ret;
  207. cherokee_buffer_t info_name = CHEROKEE_BUF_INIT;
  208. /* Build the info struct string
  209. */
  210. cherokee_buffer_add_va (&info_name, "cherokee_%s_info", module);
  211. /* Open it
  212. */
  213. ret = dylib_open (loader, module, flags, dl_handler);
  214. if (ret != ret_ok) {
  215. cherokee_buffer_mrproper (&info_name);
  216. return ret_error;
  217. }
  218. *info = get_sym_from_dlopen_handler (*dl_handler, info_name.buf);
  219. if (*info == NULL) {
  220. cherokee_buffer_mrproper (&info_name);
  221. return ret_not_found;
  222. }
  223. /* Free the info struct string
  224. */
  225. cherokee_buffer_mrproper (&info_name);
  226. return ret_ok;
  227. }
  228. static ret_t
  229. check_deps_file (cherokee_plugin_loader_t *loader,
  230. const char *modname)
  231. {
  232. FILE *file;
  233. char temp[128];
  234. cherokee_buffer_t filename = CHEROKEE_BUF_INIT;
  235. cherokee_buffer_add_va (&filename, "%s/%s.deps", loader->deps_dir.buf, modname);
  236. file = fopen (filename.buf, "r");
  237. if (file == NULL)
  238. goto exit;
  239. while (!feof(file)) {
  240. int len;
  241. char *ret;
  242. ret = fgets (temp, 127, file);
  243. if (ret == NULL)
  244. break;
  245. len = strlen (temp);
  246. if (len < 2)
  247. continue;
  248. if (temp[0] == '#')
  249. continue;
  250. if (temp[len-1] == '\n')
  251. temp[len-1] = '\0';
  252. cherokee_plugin_loader_load (loader, temp);
  253. temp[0] = '\0';
  254. }
  255. fclose (file);
  256. exit:
  257. cherokee_buffer_mrproper (&filename);
  258. return ret_ok;
  259. }
  260. static ret_t
  261. load_common (cherokee_plugin_loader_t *loader,
  262. const char *modname,
  263. int flags)
  264. {
  265. ret_t ret;
  266. entry_t *entry = NULL;
  267. cherokee_plugin_info_t *info = NULL;
  268. void *dl_handle = NULL;
  269. /* If it is already loaded just return
  270. */
  271. ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
  272. if (ret == ret_ok)
  273. return ret_ok;
  274. /* Check deps
  275. */
  276. ret = check_deps_file (loader, modname);
  277. if (ret != ret_ok)
  278. return ret;
  279. /* Get the module info
  280. */
  281. ret = get_info (loader, modname, flags, &info, &dl_handle);
  282. switch (ret) {
  283. case ret_ok:
  284. break;
  285. case ret_error:
  286. LOG_ERROR (CHEROKEE_ERROR_PLUGIN_NO_OPEN, modname);
  287. return ret;
  288. case ret_not_found:
  289. LOG_ERROR (CHEROKEE_ERROR_PLUGIN_NO_INFO, modname);
  290. return ret;
  291. default:
  292. SHOULDNT_HAPPEN;
  293. return ret_error;
  294. }
  295. /* Add new entry
  296. */
  297. entry = malloc (sizeof(entry_t));
  298. entry->dlopen_ref = dl_handle;
  299. entry->info = info;
  300. entry->built_in = false;
  301. ret = cherokee_avl_add_ptr (&loader->table, modname, entry);
  302. if (unlikely(ret != ret_ok)) {
  303. dlclose (entry->dlopen_ref);
  304. free(entry);
  305. return ret;
  306. }
  307. /* Execute init function
  308. */
  309. ret = execute_init_func (loader, modname, entry);
  310. if (ret != ret_ok) {
  311. return ret;
  312. }
  313. return ret_ok;
  314. }
  315. ret_t
  316. cherokee_plugin_loader_load_no_global (cherokee_plugin_loader_t *loader,
  317. const char *modname)
  318. {
  319. return load_common (loader, modname, 0);
  320. }
  321. ret_t
  322. cherokee_plugin_loader_load (cherokee_plugin_loader_t *loader,
  323. const char *modname)
  324. {
  325. #ifdef HAVE_RTLDGLOBAL
  326. return load_common (loader, modname, RTLD_GLOBAL);
  327. #else
  328. return load_common (loader, modname, 0);
  329. #endif
  330. }
  331. ret_t
  332. cherokee_plugin_loader_unload (cherokee_plugin_loader_t *loader,
  333. const char *modname)
  334. {
  335. int re = 0;
  336. ret_t ret;
  337. entry_t *entry;
  338. /* Remove item from the table
  339. */
  340. ret = cherokee_avl_del_ptr (&loader->table, modname, (void **)&entry);
  341. if (ret != ret_ok)
  342. return ret;
  343. /* Free the resources
  344. */
  345. if (entry->dlopen_ref != NULL) {
  346. re = dlclose (entry->dlopen_ref);
  347. }
  348. free (entry);
  349. return (re == 0) ? ret_ok : ret_error;
  350. }
  351. ret_t
  352. cherokee_plugin_loader_get_info (cherokee_plugin_loader_t *loader,
  353. const char *modname,
  354. cherokee_plugin_info_t **info)
  355. {
  356. ret_t ret;
  357. entry_t *entry;
  358. ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
  359. if (ret != ret_ok)
  360. return ret;
  361. *info = entry->info;
  362. return ret_ok;
  363. }
  364. ret_t
  365. cherokee_plugin_loader_get_sym (cherokee_plugin_loader_t *loader,
  366. const char *modname,
  367. const char *name,
  368. void **sym)
  369. {
  370. ret_t ret;
  371. entry_t *entry;
  372. void *tmp;
  373. /* Get the symbol from a dynamic library
  374. */
  375. ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
  376. if (ret != ret_ok)
  377. return ret;
  378. /* Even if we're trying to look for symbols in the executable,
  379. * using dlopen(NULL), the handler pointer should not be nil.
  380. */
  381. if (entry->dlopen_ref == NULL)
  382. return ret_not_found;
  383. tmp = get_sym_from_dlopen_handler (entry->dlopen_ref, name);
  384. if (tmp == NULL)
  385. return ret_not_found;
  386. *sym = tmp;
  387. return ret_ok;
  388. }
  389. ret_t
  390. cherokee_plugin_loader_get (cherokee_plugin_loader_t *loader,
  391. const char *modname,
  392. cherokee_plugin_info_t **info)
  393. {
  394. ret_t ret;
  395. ret = cherokee_plugin_loader_load (loader, modname);
  396. if (ret != ret_ok)
  397. return ret;
  398. ret = cherokee_plugin_loader_get_info (loader, modname, info);
  399. if (ret != ret_ok)
  400. return ret;
  401. return ret_ok;
  402. }
  403. ret_t
  404. cherokee_plugin_loader_set_directory (cherokee_plugin_loader_t *loader, cherokee_buffer_t *dir)
  405. {
  406. cherokee_buffer_clean (&loader->module_dir);
  407. cherokee_buffer_add_buffer (&loader->module_dir, dir);
  408. return ret_ok;
  409. }
  410. ret_t
  411. cherokee_plugin_loader_set_deps_dir (cherokee_plugin_loader_t *loader, cherokee_buffer_t *dir)
  412. {
  413. cherokee_buffer_clean (&loader->deps_dir);
  414. cherokee_buffer_add_buffer (&loader->deps_dir, dir);
  415. return ret_ok;
  416. }
  417. static ret_t
  418. while_print_name (cherokee_buffer_t *key, void *value, void *param)
  419. {
  420. entry_t *entry = value;
  421. cherokee_buffer_t *buffer = param;
  422. if (entry->built_in) {
  423. cherokee_buffer_add_buffer (buffer, key);
  424. cherokee_buffer_add_char (buffer, ' ');
  425. }
  426. return ret_ok;
  427. }
  428. ret_t
  429. cherokee_plugin_loader_get_mods_info (cherokee_plugin_loader_t *loader,
  430. cherokee_buffer_t *builtin)
  431. {
  432. /* Build the built-in module string
  433. */
  434. cherokee_avl_while (&loader->table, while_print_name, builtin, NULL, NULL);
  435. if ((builtin->len >= 2) &&
  436. (cherokee_buffer_end_char (builtin) == ' '))
  437. {
  438. cherokee_buffer_drop_ending (builtin, 1);
  439. }
  440. return ret_ok;
  441. }