Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

571 lines (450 sloc) 12.237 kB
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2013 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 ret_t
add_static_entry (cherokee_plugin_loader_t *loader,
const char *name,
void *info)
{
entry_t *entry;
entry = malloc (sizeof(entry_t));
if (entry == NULL) {
return ret_nomem;
}
entry->dlopen_ref = dlopen (NULL, RTLD_BASE);
entry->info = info;
entry->built_in = true;
cherokee_avl_add_ptr (&loader->table, (char *)name, entry);
return ret_ok;
}
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));
if (entry == NULL) {
return ret_nomem;
}
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;
if (entry == NULL)
return ret_error;
*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 == NULL) || (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;
}
Jump to Line
Something went wrong with that request. Please try again.