libqb/lib/plugin_loader.c
Angus Salkeld b8a5a74fcc hdb: move functions into cfile
convert int -> int32_t
rename qb_hdb_handle_t -> qb_handle_t
rename DECLARE_HDB_DATABASE -> QB_HDB_DECLARE
rename qb_hdb_handle_database -> qb_hdb

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
2010-06-16 09:12:20 +10:00

554 lines
13 KiB
C

/*
* Copyright (C) 2006 Steven Dake <sdake@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fnmatch.h>
#ifdef QB_SOLARIS
#include <iso/limits_iso.h>
#endif
#include <qb/qbhdb.h>
#include <qb/qbplugin_comp.h>
#include <qb/qbplugin.h>
struct plugin_component_instance {
struct plugin_iface *ifaces;
int iface_count;
qb_handle_t comp_handle;
void *dl_handle;
int refcount;
char library_name[256];
};
struct plugin_iface_instance {
qb_handle_t component_handle;
void *context;
void (*destructor) (void *context);
};
QB_HDB_DECLARE(plugin_component_instance_database, NULL);
QB_HDB_DECLARE(plugin_iface_instance_database, NULL);
/*
static struct hdb_handle_database plugin_component_instance_database = {
.handle_count = 0,
.handles = 0,
.iterator = 0
};
static struct hdb_handle_database plugin_iface_instance_database = {
.handle_count = 0,
.handles = 0,
.iterator = 0
};
*/
static qb_handle_t g_component_handle = 0xFFFFFFFF;
#if defined(QB_LINUX) || defined(QB_SOLARIS)
static int plugin_select_so(const struct dirent *dirent)
#else
static int plugin_select_so(struct dirent *dirent)
#endif
{
unsigned int len;
/*
* TODO check .lcrso > .so
*/
len = strlen(dirent->d_name);
if (len > 3) {
if (strcmp(".so", dirent->d_name + len - 3) == 0) {
return (1);
}
}
return (0);
}
#if defined(QB_LINUX) || defined(QB_SOLARIS)
static int pathlist_select(const struct dirent *dirent)
#else
static int pathlist_select(struct dirent *dirent)
#endif
{
if (fnmatch("*.conf", dirent->d_name, 0) == 0) {
return (1);
}
return (0);
}
static inline struct plugin_component_instance *plugin_comp_find(const char
*iface_name,
unsigned int
version,
unsigned int
*iface_number)
{
struct plugin_component_instance *instance;
void *instance_p = NULL;
qb_handle_t component_handle = 0;
int i;
/*
* Try to find interface in already loaded component
*/
qb_hdb_iterator_reset(&plugin_component_instance_database);
while (qb_hdb_iterator_next(&plugin_component_instance_database,
&instance_p, &component_handle) == 0) {
instance = (struct plugin_component_instance *)instance_p;
for (i = 0; i < instance->iface_count; i++) {
if ((strcmp(instance->ifaces[i].name, iface_name) == 0)
&& instance->ifaces[i].version == version) {
*iface_number = i;
return (instance);
}
}
qb_hdb_handle_put(&plugin_component_instance_database,
component_handle);
}
return (NULL);
}
static inline int plugin_lib_loaded(char *library_name)
{
struct plugin_component_instance *instance;
void *instance_p = NULL;
qb_handle_t component_handle = 0;
/*
* Try to find interface in already loaded component
*/
qb_hdb_iterator_reset(&plugin_component_instance_database);
while (qb_hdb_iterator_next(&plugin_component_instance_database,
(void *)&instance_p,
&component_handle) == 0) {
instance = (struct plugin_component_instance *)instance_p;
if (strcmp(instance->library_name, library_name) == 0) {
return (1);
}
qb_hdb_handle_put(&plugin_component_instance_database,
component_handle);
}
return (0);
}
enum { PATH_LIST_SIZE = 128 };
const char *path_list[PATH_LIST_SIZE];
unsigned int path_list_entries = 0;
static void defaults_path_build(void)
{
char cwd[1024];
char *res;
res = getcwd(cwd, sizeof(cwd));
if (res != NULL && (path_list[0] = strdup(cwd)) != NULL) {
path_list_entries++;
}
path_list[path_list_entries++] = PLUGINSODIR;
}
static void ld_library_path_build(void)
{
char *ld_library_path;
char *my_ld_library_path;
char *p_s, *ptrptr;
ld_library_path = getenv("LD_LIBRARY_PATH");
if (ld_library_path == NULL) {
return;
}
my_ld_library_path = strdup(ld_library_path);
if (my_ld_library_path == NULL) {
return;
}
p_s = strtok_r(my_ld_library_path, ":", &ptrptr);
while (p_s != NULL) {
char *p = strdup(p_s);
if (p && path_list_entries < PATH_LIST_SIZE) {
path_list[path_list_entries++] = p;
}
p_s = strtok_r(NULL, ":", &ptrptr);
}
free(my_ld_library_path);
}
static int ldso_path_build(const char *path, const char *filename)
{
FILE *fp;
char string[1024];
char filename_cat[1024];
char newpath[1024];
char *newpath_tmp;
char *new_filename;
int j;
struct dirent **scandir_list;
unsigned int scandir_entries;
snprintf(filename_cat, sizeof(filename_cat), "%s/%s", path, filename);
if (filename[0] == '*') {
scandir_entries = scandir(path,
&scandir_list,
pathlist_select, alphasort);
if (scandir_entries == 0) {
return 0;
} else if (scandir_entries == -1) {
return -1;
} else {
for (j = 0; j < scandir_entries; j++) {
ldso_path_build(path, scandir_list[j]->d_name);
}
}
}
fp = fopen(filename_cat, "r");
if (fp == NULL) {
return (-1);
}
while (fgets(string, sizeof(string), fp)) {
char *p;
if (strlen(string) > 0)
string[strlen(string) - 1] = '\0';
if (strncmp(string, "include", strlen("include")) == 0) {
newpath_tmp = string + strlen("include") + 1;
for (j = strlen(string);
string[j] != ' ' &&
string[j] != '/' && j > 0; j--) {
}
string[j] = '\0';
new_filename = &string[j] + 1;
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, newpath_tmp);
ldso_path_build(newpath, new_filename);
continue;
}
p = strdup(string);
if (p && path_list_entries < PATH_LIST_SIZE) {
path_list[path_list_entries++] = p;
}
}
fclose(fp);
return (0);
}
#if defined (QB_SOLARIS) && !defined(HAVE_SCANDIR)
static int scandir(const char *dir, struct dirent ***namelist,
int (*filter) (const struct dirent *),
int (*compar) (const struct dirent **,
const struct dirent **))
{
DIR *d;
struct dirent *entry;
struct dirent *result;
struct dirent **names = NULL;
int namelist_items = 0, namelist_size = 0;
size_t len;
int return_code;
d = opendir(dir);
if (d == NULL)
return -1;
names = NULL;
len = offsetof(struct dirent, d_name)+pathconf(dir, _PC_NAME_MAX) + 1;
entry = malloc(len);
for (return_code = readdir_r(d, entry, &result);
dirent != NULL && return_code == 0;
return_code = readdir_r(d, entry, &result)) {
struct dirent *tmpentry;
if ((filter != NULL) && ((*filter) (result) == 0)) {
continue;
}
if (namelist_items >= namelist_size) {
struct dirent **tmp;
namelist_size += 512;
if ((unsigned long)namelist_size > INT_MAX) {
errno = EOVERFLOW;
goto fail;
}
tmp = realloc(names,
namelist_size * sizeof(struct dirent *));
if (tmp == NULL) {
goto fail;
}
names = tmp;
}
tmpentry = malloc(result->d_reclen);
if (tmpentry == NULL) {
goto fail;
}
(void)memcpy(tmpentry, result, result->d_reclen);
names[namelist_items++] = tmpentry;
}
(void)closedir(d);
if ((namelist_items > 1) && (compar != NULL)) {
qsort(names, namelist_items, sizeof(struct dirent *),
(int (*)(const void *, const void *))compar);
}
*namelist = names;
return namelist_items;
fail:
{
int err = errno;
(void)closedir(d);
while (namelist_items != 0) {
namelist_items--;
free(*namelist[namelist_items]);
}
free(entry);
free(names);
*namelist = NULL;
errno = err;
return -1;
}
}
#endif
#if defined (QB_SOLARIS) && !defined(HAVE_ALPHASORT)
static int alphasort(const struct dirent **a, const struct dirent **b)
{
return strcmp((*a)->d_name, (*b)->d_name);
}
#endif
static int interface_find_and_load(const char *path,
const char *iface_name,
int version, struct plugin_component_instance
**instance_ret, unsigned int *iface_number)
{
struct plugin_component_instance *instance;
void *dl_handle;
struct dirent **scandir_list;
int scandir_entries;
unsigned int libs_to_scan;
char dl_name[1024];
#ifdef QB_SOLARIS
void (*comp_reg) (void);
#endif
scandir_entries =
scandir(path, &scandir_list, plugin_select_so, alphasort);
if (scandir_entries > 0)
/*
* no error so load the object
*/
for (libs_to_scan = 0; libs_to_scan < scandir_entries;
libs_to_scan++) {
/*
* Load objects, scan them, unload them if they are not a match
*/
snprintf(dl_name, sizeof(dl_name), "%s/%s",
path, scandir_list[libs_to_scan]->d_name);
/*
* Don't reload already loaded libraries
*/
if (plugin_lib_loaded(dl_name)) {
continue;
}
dl_handle = dlopen(dl_name, RTLD_NOW);
if (dl_handle == NULL) {
fprintf(stderr, "%s: open failed: %s\n",
dl_name, dlerror());
continue;
}
/*
* constructors don't work in Solaris dlopen, so we have to specifically call
* a function to register the component
*/
#ifdef QB_SOLARIS
comp_reg =
dlsym(dl_handle,
"corosync_plugin_component_register");
comp_reg();
#endif
instance =
plugin_comp_find(iface_name, version, iface_number);
if (instance) {
instance->dl_handle = dl_handle;
strcpy(instance->library_name, dl_name);
goto found;
}
/*
* No matching interfaces found, try next shared object
*/
if (g_component_handle != 0xFFFFFFFF) {
qb_hdb_handle_destroy
(&plugin_component_instance_database,
g_component_handle);
g_component_handle = 0xFFFFFFFF;
}
dlclose(dl_handle);
}
/* scanning for pluginso loop */
if (scandir_entries > 0) {
int i;
for (i = 0; i < scandir_entries; i++) {
free(scandir_list[i]);
}
free(scandir_list);
}
g_component_handle = 0xFFFFFFFF;
return -1;
found:
*instance_ret = instance;
if (scandir_entries > 0) {
int i;
for (i = 0; i < scandir_entries; i++) {
free(scandir_list[i]);
}
free(scandir_list);
}
g_component_handle = 0xFFFFFFFF;
return 0;
}
static unsigned int plugin_initialized = 0;
int plugin_ifact_reference(qb_handle_t * iface_handle,
const char *iface_name,
int version, void **iface, void *context)
{
struct plugin_iface_instance *iface_instance;
struct plugin_component_instance *instance;
unsigned int iface_number;
unsigned int res;
unsigned int i;
/*
* Determine if the component is already loaded
*/
instance = plugin_comp_find(iface_name, version, &iface_number);
if (instance) {
goto found;
}
if (plugin_initialized == 0) {
plugin_initialized = 1;
defaults_path_build();
ld_library_path_build();
ldso_path_build("/etc", "ld.so.conf");
}
// TODO error checking in this code is weak
/*
* Search through all pluginso files for desired interface
*/
for (i = 0; i < path_list_entries; i++) {
res = interface_find_and_load(path_list[i],
iface_name,
version,
&instance, &iface_number);
if (res == 0) {
goto found;
}
}
/*
* No matching interfaces found in all shared objects
*/
return (-1);
found:
*iface = instance->ifaces[iface_number].interfaces;
if (instance->ifaces[iface_number].constructor) {
instance->ifaces[iface_number].constructor(context);
}
qb_hdb_handle_create(&plugin_iface_instance_database,
sizeof(struct plugin_iface_instance),
iface_handle);
qb_hdb_handle_get(&plugin_iface_instance_database,
*iface_handle, (void *)&iface_instance);
iface_instance->component_handle = instance->comp_handle;
iface_instance->context = context;
iface_instance->destructor = instance->ifaces[iface_number].destructor;
qb_hdb_handle_put(&plugin_iface_instance_database, *iface_handle);
return (0);
}
int plugin_ifact_release(qb_handle_t handle)
{
struct plugin_iface_instance *iface_instance;
int res = 0;
res = qb_hdb_handle_get(&plugin_iface_instance_database,
handle, (void *)&iface_instance);
if (iface_instance->destructor) {
iface_instance->destructor(iface_instance->context);
}
qb_hdb_handle_put(&plugin_component_instance_database,
iface_instance->component_handle);
qb_hdb_handle_put(&plugin_iface_instance_database, handle);
qb_hdb_handle_destroy(&plugin_iface_instance_database, handle);
return (res);
}
void plugin_component_register(struct plugin_comp *comp)
{
struct plugin_component_instance *instance;
static qb_handle_t comp_handle;
qb_hdb_handle_create(&plugin_component_instance_database,
sizeof(struct plugin_component_instance),
&comp_handle);
qb_hdb_handle_get(&plugin_component_instance_database,
comp_handle, (void *)&instance);
instance->ifaces = comp->ifaces;
instance->iface_count = comp->iface_count;
instance->comp_handle = comp_handle;
instance->dl_handle = NULL;
qb_hdb_handle_put(&plugin_component_instance_database, comp_handle);
g_component_handle = comp_handle;
}