blob: c92ab23024992e9428c13f27475ee35b3bb10215 [file] [log] [blame]
/*
* module.c, dynamic module interface
*
* Copyright (c) 2009-2010 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <module.h>
#define LOG_TAG "module"
#include <log.h>
static struct module *g_module_head;
static char *g_module_err;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
#define for_each_module(__module, __head) \
for ((__module) = (__head); (__module) != NULL; \
(__module) = (__module)->next)
#define find_last_module(__head) \
({ \
struct module *m; \
\
for_each_module(m, (__head)) { \
if (!m->next) \
break; \
} \
m; \
})
static struct module *module_find_with_name(struct module *head,
const char *filename)
{
struct module *module;
for_each_module(module, head) {
if (!strcmp(module->name, filename))
return module;
}
return NULL;
}
static struct module *module_find_with_handle(struct module *head,
const void *handle)
{
struct module *module;
for_each_module(module, head) {
if (module->handle == handle)
return module;
}
return NULL;
}
static struct module *module_add_list(struct module *head,
struct module *add)
{
struct module *last;
last = find_last_module(head);
if (last)
last->next = add;
else
head = add;
return head;
}
static struct module *module_del_list(struct module *head,
struct module *del)
{
struct module *prev = NULL;
for_each_module(prev, head) {
if (prev->next == del)
break;
}
if (!prev)
head = del->next;
else
prev->next = del->next;
return head;
}
static inline void module_set_error(const char *dlerr)
{
if (g_module_err)
free(g_module_err);
if (dlerr)
g_module_err = strdup(dlerr);
else
g_module_err = NULL;
}
const char *module_error(void)
{
return g_module_err;
}
struct module *module_open(const char *file, int flag)
{
struct module *new, *existing;
const char *dlerr;
int init_ret = 0;
pthread_mutex_lock(&g_lock);
existing = module_find_with_name(g_module_head, file);
if (existing) {
LOGE("found opened module %s with name\n", existing->name);
existing->ref_count++;
pthread_mutex_unlock(&g_lock);
return existing;
}
new = malloc(sizeof(*new));
if (!new) {
pthread_mutex_unlock(&g_lock);
return NULL;
}
new->ref_count = 1;
new->priv = NULL;
new->next = NULL;
new->handle = NULL;
new->handle = dlopen(file, flag);
if (!(new->handle)) {
LOGE("dlopen failed (%s)\n", file);
dlerr = dlerror();
module_set_error(dlerr);
goto free_new;
}
existing = module_find_with_handle(g_module_head, new->handle);
if (existing) {
LOGE("found opened module %s with handle\n", existing->name);
existing->ref_count++;
free(new);
pthread_mutex_unlock(&g_lock);
return existing;
}
new->name = strdup(file);
if (!new->name) {
goto free_handle;
}
dlerror();
new->init = dlsym(new->handle, "module_init");
dlerr = dlerror();
if (!dlerr && new->init) {
LOGE("module %s has init(), call the symbol\n", new->name);
init_ret = new->init(new);
}
if (init_ret) {
LOGE("module %s init() failed (%d)\n", new->name, init_ret);
goto free_handle;
}
dlerror();
new->exit = dlsym(new->handle, "module_exit");
dlerr = dlerror();
if (dlerr)
new->exit = NULL;
g_module_head = module_add_list(g_module_head, new);
pthread_mutex_unlock(&g_lock);
return new;
free_handle:
dlclose(new->handle);
free_new:
free(new);
pthread_mutex_unlock(&g_lock);
return NULL;
}
int module_close(struct module *module)
{
int ret = 0;
if (!module || !module->handle)
return 0;
pthread_mutex_lock(&g_lock);
module->ref_count--;
ret = module->ref_count;
LOGV("module %s decrease refcont (%d)\n", module->name, module->ref_count);
if (!module->ref_count) {
if (module->exit)
module->exit(module);
if (dlclose(module->handle)) {
LOGE("module %s dlclose failed",module->name);
}
g_module_head = module_del_list(g_module_head, module);
LOGV("module %s closed\n", module->name);
free(module->name);
free(module);
}
pthread_mutex_unlock(&g_lock);
return ret;
}
void *module_symbol(struct module *module, const char *string)
{
void *symbol = NULL;
const char *dlerr;
if (!module || !module->handle || !string)
return NULL;
pthread_mutex_lock(&g_lock);
symbol = dlsym(module->handle, string);
if (!symbol) {
dlerr = dlerror();
LOGE("not founded symbol %s in module %s (%s)\n",
string, module->name, dlerr);
module_set_error(dlerr);
symbol = NULL;
}
else
LOGV("found symbol %s in module %s\n", string, module->name);
pthread_mutex_unlock(&g_lock);
return symbol;
}