/cherokee/plugin_loader.c
C | 558 lines | 363 code | 117 blank | 78 comment | 58 complexity | a28e3353f0d182f24392b34d0cd3393f MD5 | raw file
- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
- /* Cherokee
- *
- * Authors:
- * Alvaro Lopez Ortega <alvaro@alobbs.com>
- *
- * Copyright (C) 2001-2010 Alvaro Lopez Ortega
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
- #include "common-internal.h"
- #include "plugin_loader.h"
- #include <stdlib.h>
- #include <string.h>
- #ifdef HAVE_DLFCN_H
- # include <dlfcn.h>
- #endif
- #include "buffer.h"
- typedef cherokee_plugin_loader_entry_t entry_t;
- #if defined(HAVE_PTHREAD)
- static pthread_mutex_t dlerror_mutex = PTHREAD_MUTEX_INITIALIZER;
- #endif
- /* This is the non-embedded implementation
- */
- #include "loader.autoconf.h"
- #ifdef HAVE_RTLDNOW
- # define RTLD_BASE RTLD_NOW
- #else
- # define RTLD_BASE RTLD_LAZY
- #endif
- typedef void *func_new_t;
- static void
- add_static_entry (cherokee_plugin_loader_t *loader,
- const char *name,
- void *info)
- {
- entry_t *entry;
- entry = malloc (sizeof(entry_t));
- entry->dlopen_ref = dlopen (NULL, RTLD_BASE);
- entry->info = info;
- entry->built_in = true;
- cherokee_avl_add_ptr (&loader->table, (char *)name, entry);
- }
- static ret_t
- load_static_linked_modules (cherokee_plugin_loader_t *loader)
- {
- /* I do know that to include code in the middle of a function is hacky
- * and dirty, but this is the best solution that I could figure out.
- * If you have some idea to clean it up, please, don't hesitate and
- * let me know. - alo
- */
- #include "loader.autoconf.inc"
- return ret_ok;
- }
- static void
- free_entry (void *item)
- {
- entry_t *entry = (entry_t *)item;
- if (entry->dlopen_ref) {
- dlclose (entry->dlopen_ref);
- entry->dlopen_ref = NULL;
- }
- free (item);
- }
- ret_t
- cherokee_plugin_loader_init (cherokee_plugin_loader_t *loader)
- {
- ret_t ret;
- ret = cherokee_avl_init (&loader->table);
- if (unlikely(ret < ret_ok))
- return ret;
- /* Plug-in dir
- */
- ret = cherokee_buffer_init (&loader->module_dir);
- if (unlikely(ret < ret_ok))
- return ret;
- cherokee_buffer_add_str (&loader->module_dir, CHEROKEE_PLUGINDIR);
- /* Plug-in dependencies dir
- */
- ret = cherokee_buffer_init (&loader->deps_dir);
- if (unlikely(ret < ret_ok))
- return ret;
- cherokee_buffer_add_str (&loader->deps_dir, CHEROKEE_DEPSDIR);
- ret = load_static_linked_modules (loader);
- if (unlikely(ret < ret_ok))
- return ret;
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_mrproper (cherokee_plugin_loader_t *loader)
- {
- cherokee_buffer_mrproper (&loader->module_dir);
- cherokee_buffer_mrproper (&loader->deps_dir);
- cherokee_avl_mrproper (&loader->table, free_entry);
- return ret_ok;
- }
- static void *
- get_sym_from_dlopen_handler (void *dl_handle, const char *sym)
- {
- void *re;
- const char *error;
- /* Clear the possible error and look for the symbol
- */
- CHEROKEE_MUTEX_LOCK (&dlerror_mutex);
- dlerror();
- re = (void *) dlsym(dl_handle, sym);
- if ((error = dlerror()) != NULL) {
- LOG_ERROR (CHEROKEE_ERROR_PLUGIN_LOAD_NO_SYM, sym, error);
- CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
- return NULL;
- }
- CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
- return re;
- }
- static ret_t
- dylib_open (cherokee_plugin_loader_t *loader,
- const char *libname,
- int extra_flags,
- void **handler_out)
- {
- ret_t ret;
- void *lib;
- int flags;
- cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
- flags = RTLD_BASE | extra_flags;
- /* Build the path string
- */
- ret = cherokee_buffer_add_va (&tmp, "%s/libplugin_%s." MOD_SUFFIX, loader->module_dir.buf, libname);
- if (unlikely(ret < ret_ok)) {
- cherokee_buffer_mrproper (&tmp);
- return ret;
- }
- /* Open the library
- */
- CHEROKEE_MUTEX_LOCK (&dlerror_mutex);
- lib = dlopen (tmp.buf, flags);
- if (lib == NULL) {
- LOG_ERROR (CHEROKEE_ERROR_PLUGIN_DLOPEN, dlerror(), tmp.buf);
- CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
- cherokee_buffer_mrproper (&tmp);
- return ret_error;
- }
- CHEROKEE_MUTEX_UNLOCK (&dlerror_mutex);
- /* Free the memory
- */
- cherokee_buffer_mrproper (&tmp);
- *handler_out = lib;
- return ret_ok;
- }
- static ret_t
- execute_init_func (cherokee_plugin_loader_t *loader,
- const char *module,
- entry_t *entry)
- {
- ret_t ret;
- void (*init_func) (cherokee_plugin_loader_t *);
- cherokee_buffer_t init_name = CHEROKEE_BUF_INIT;
- /* Build the init function name
- */
- ret = cherokee_buffer_add_va (&init_name, "cherokee_plugin_%s_init", module);
- if (unlikely(ret < ret_ok)) {
- cherokee_buffer_mrproper (&init_name);
- return ret;
- }
- /* Get the function
- */
- if (entry->dlopen_ref == NULL) {
- cherokee_buffer_mrproper (&init_name);
- return ret_error;
- }
- init_func = get_sym_from_dlopen_handler (entry->dlopen_ref, init_name.buf);
- /* Only try to execute if it exists
- */
- if (init_func == NULL) {
- LOG_WARNING (CHEROKEE_ERROR_PLUGIN_NO_INIT, init_name.buf);
- cherokee_buffer_mrproper (&init_name);
- return ret_not_found;
- }
- /* Free the init function name string
- */
- cherokee_buffer_mrproper (&init_name);
- /* Execute the init func
- */
- init_func(loader);
- return ret_ok;
- }
- static ret_t
- get_info (cherokee_plugin_loader_t *loader,
- const char *module,
- int flags,
- cherokee_plugin_info_t **info,
- void **dl_handler)
- {
- ret_t ret;
- cherokee_buffer_t info_name = CHEROKEE_BUF_INIT;
- /* Build the info struct string
- */
- cherokee_buffer_add_va (&info_name, "cherokee_%s_info", module);
- /* Open it
- */
- ret = dylib_open (loader, module, flags, dl_handler);
- if (ret != ret_ok) {
- cherokee_buffer_mrproper (&info_name);
- return ret_error;
- }
- *info = get_sym_from_dlopen_handler (*dl_handler, info_name.buf);
- if (*info == NULL) {
- cherokee_buffer_mrproper (&info_name);
- return ret_not_found;
- }
- /* Free the info struct string
- */
- cherokee_buffer_mrproper (&info_name);
- return ret_ok;
- }
- static ret_t
- check_deps_file (cherokee_plugin_loader_t *loader,
- const char *modname)
- {
- FILE *file;
- char temp[128];
- cherokee_buffer_t filename = CHEROKEE_BUF_INIT;
- cherokee_buffer_add_va (&filename, "%s/%s.deps", loader->deps_dir.buf, modname);
- file = fopen (filename.buf, "r");
- if (file == NULL)
- goto exit;
- while (!feof(file)) {
- int len;
- char *ret;
- ret = fgets (temp, 127, file);
- if (ret == NULL)
- break;
- len = strlen (temp);
- if (len < 2)
- continue;
- if (temp[0] == '#')
- continue;
- if (temp[len-1] == '\n')
- temp[len-1] = '\0';
- cherokee_plugin_loader_load (loader, temp);
- temp[0] = '\0';
- }
- fclose (file);
- exit:
- cherokee_buffer_mrproper (&filename);
- return ret_ok;
- }
- static ret_t
- load_common (cherokee_plugin_loader_t *loader,
- const char *modname,
- int flags)
- {
- ret_t ret;
- entry_t *entry = NULL;
- cherokee_plugin_info_t *info = NULL;
- void *dl_handle = NULL;
- /* If it is already loaded just return
- */
- ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
- if (ret == ret_ok)
- return ret_ok;
- /* Check deps
- */
- ret = check_deps_file (loader, modname);
- if (ret != ret_ok)
- return ret;
- /* Get the module info
- */
- ret = get_info (loader, modname, flags, &info, &dl_handle);
- switch (ret) {
- case ret_ok:
- break;
- case ret_error:
- LOG_ERROR (CHEROKEE_ERROR_PLUGIN_NO_OPEN, modname);
- return ret;
- case ret_not_found:
- LOG_ERROR (CHEROKEE_ERROR_PLUGIN_NO_INFO, modname);
- return ret;
- default:
- SHOULDNT_HAPPEN;
- return ret_error;
- }
- /* Add new entry
- */
- entry = malloc (sizeof(entry_t));
- entry->dlopen_ref = dl_handle;
- entry->info = info;
- entry->built_in = false;
- ret = cherokee_avl_add_ptr (&loader->table, modname, entry);
- if (unlikely(ret != ret_ok)) {
- dlclose (entry->dlopen_ref);
- free(entry);
- return ret;
- }
- /* Execute init function
- */
- ret = execute_init_func (loader, modname, entry);
- if (ret != ret_ok) {
- return ret;
- }
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_load_no_global (cherokee_plugin_loader_t *loader,
- const char *modname)
- {
- return load_common (loader, modname, 0);
- }
- ret_t
- cherokee_plugin_loader_load (cherokee_plugin_loader_t *loader,
- const char *modname)
- {
- #ifdef HAVE_RTLDGLOBAL
- return load_common (loader, modname, RTLD_GLOBAL);
- #else
- return load_common (loader, modname, 0);
- #endif
- }
- ret_t
- cherokee_plugin_loader_unload (cherokee_plugin_loader_t *loader,
- const char *modname)
- {
- int re = 0;
- ret_t ret;
- entry_t *entry;
- /* Remove item from the table
- */
- ret = cherokee_avl_del_ptr (&loader->table, modname, (void **)&entry);
- if (ret != ret_ok)
- return ret;
- /* Free the resources
- */
- if (entry->dlopen_ref != NULL) {
- re = dlclose (entry->dlopen_ref);
- }
- free (entry);
- return (re == 0) ? ret_ok : ret_error;
- }
- ret_t
- cherokee_plugin_loader_get_info (cherokee_plugin_loader_t *loader,
- const char *modname,
- cherokee_plugin_info_t **info)
- {
- ret_t ret;
- entry_t *entry;
- ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
- if (ret != ret_ok)
- return ret;
- *info = entry->info;
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_get_sym (cherokee_plugin_loader_t *loader,
- const char *modname,
- const char *name,
- void **sym)
- {
- ret_t ret;
- entry_t *entry;
- void *tmp;
- /* Get the symbol from a dynamic library
- */
- ret = cherokee_avl_get_ptr (&loader->table, modname, (void **)&entry);
- if (ret != ret_ok)
- return ret;
- /* Even if we're trying to look for symbols in the executable,
- * using dlopen(NULL), the handler pointer should not be nil.
- */
- if (entry->dlopen_ref == NULL)
- return ret_not_found;
- tmp = get_sym_from_dlopen_handler (entry->dlopen_ref, name);
- if (tmp == NULL)
- return ret_not_found;
- *sym = tmp;
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_get (cherokee_plugin_loader_t *loader,
- const char *modname,
- cherokee_plugin_info_t **info)
- {
- ret_t ret;
- ret = cherokee_plugin_loader_load (loader, modname);
- if (ret != ret_ok)
- return ret;
- ret = cherokee_plugin_loader_get_info (loader, modname, info);
- if (ret != ret_ok)
- return ret;
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_set_directory (cherokee_plugin_loader_t *loader, cherokee_buffer_t *dir)
- {
- cherokee_buffer_clean (&loader->module_dir);
- cherokee_buffer_add_buffer (&loader->module_dir, dir);
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_set_deps_dir (cherokee_plugin_loader_t *loader, cherokee_buffer_t *dir)
- {
- cherokee_buffer_clean (&loader->deps_dir);
- cherokee_buffer_add_buffer (&loader->deps_dir, dir);
- return ret_ok;
- }
- static ret_t
- while_print_name (cherokee_buffer_t *key, void *value, void *param)
- {
- entry_t *entry = value;
- cherokee_buffer_t *buffer = param;
- if (entry->built_in) {
- cherokee_buffer_add_buffer (buffer, key);
- cherokee_buffer_add_char (buffer, ' ');
- }
- return ret_ok;
- }
- ret_t
- cherokee_plugin_loader_get_mods_info (cherokee_plugin_loader_t *loader,
- cherokee_buffer_t *builtin)
- {
- /* Build the built-in module string
- */
- cherokee_avl_while (&loader->table, while_print_name, builtin, NULL, NULL);
- if ((builtin->len >= 2) &&
- (cherokee_buffer_end_char (builtin) == ' '))
- {
- cherokee_buffer_drop_ending (builtin, 1);
- }
- return ret_ok;
- }