You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
klish/clish/plugin/plugin.c

486 lines
12 KiB

/*
* plugin.c
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "private.h"
#include "lub/string.h"
#include "lub/list.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
/**********************************************************
* SYM functions *
**********************************************************/
/*--------------------------------------------------------- */
int clish_sym_compare(const void *first, const void *second)
{
const clish_sym_t *f = (const clish_sym_t *)first;
const clish_sym_t *s = (const clish_sym_t *)second;
return strcmp(f->name, s->name);
}
/*--------------------------------------------------------- */
clish_sym_t *clish_sym_new(const char *name, void *func, int type)
{
clish_sym_t *this;
this = malloc(sizeof(*this));
this->name = lub_string_dup(name);
this->func = func;
this->type = type;
this->api = CLISH_SYM_API_SIMPLE;
this->permanent = BOOL_FALSE;
return this;
}
/*--------------------------------------------------------- */
void clish_sym_free(clish_sym_t *this)
{
if (!this)
return;
lub_string_free(this->name);
free(this);
}
/*--------------------------------------------------------- */
void clish_sym__set_func(clish_sym_t *this, void *func)
{
this->func = func;
}
/*--------------------------------------------------------- */
void *clish_sym__get_func(clish_sym_t *this)
{
return this->func;
}
/*--------------------------------------------------------- */
void clish_sym__set_permanent(clish_sym_t *this, bool_t permanent)
{
this->permanent = permanent;
}
/*--------------------------------------------------------- */
bool_t clish_sym__get_permanent(clish_sym_t *this)
{
return this->permanent;
}
/*--------------------------------------------------------- */
void clish_sym__set_name(clish_sym_t *this, const char *name)
{
lub_string_free(this->name);
this->name = lub_string_dup(name);
}
/*--------------------------------------------------------- */
char *clish_sym__get_name(clish_sym_t *this)
{
return this->name;
}
/*--------------------------------------------------------- */
void clish_sym__set_plugin(clish_sym_t *this, clish_plugin_t *plugin)
{
this->plugin = plugin;
}
/*--------------------------------------------------------- */
clish_plugin_t *clish_sym__get_plugin(clish_sym_t *this)
{
return this->plugin;
}
/*--------------------------------------------------------- */
void clish_sym__set_type(clish_sym_t *this, int type)
{
this->type = type;
}
/*--------------------------------------------------------- */
int clish_sym__get_type(const clish_sym_t *this)
{
return this->type;
}
/*--------------------------------------------------------- */
void clish_sym__set_api(clish_sym_t *this, clish_sym_api_e api)
{
this->api = api;
}
/*--------------------------------------------------------- */
clish_sym_api_e clish_sym__get_api(const clish_sym_t *this)
{
return this->api;
}
/*--------------------------------------------------------- */
int clish_sym_clone(clish_sym_t *dst, clish_sym_t *src)
{
char *name;
if (!dst || !src)
return -1;
name = dst->name;
*dst = *src;
dst->name = name;
return 0;
}
/**********************************************************
* PLUGIN functions *
**********************************************************/
/*--------------------------------------------------------- */
clish_plugin_t *clish_plugin_new(const char *name)
{
clish_plugin_t *this;
this = malloc(sizeof(*this));
this->name = lub_string_dup(name);
this->conf = NULL;
this->alias = NULL;
this->file = NULL;
this->builtin_flag = BOOL_FALSE; /* The plugin is shared object by default */
this->dlhan = NULL;
/* Initialise the list of symbols */
this->syms = lub_list_new(clish_sym_compare);
/* Constructor and destructor */
this->init = NULL;
this->fini = NULL;
/* Flags */
this->rtld_global = BOOL_FALSE; /* The dlopen() use RTLD_LOCAL by default */
return this;
}
/*--------------------------------------------------------- */
void clish_plugin_free(clish_plugin_t *this, void *userdata)
{
lub_list_node_t *iter;
if (!this)
return;
/* Execute destructor */
if (this->fini)
this->fini(userdata, this);
lub_string_free(this->name);
lub_string_free(this->alias);
lub_string_free(this->file);
lub_string_free(this->conf);
/* Free symbol list */
while ((iter = lub_list__get_head(this->syms))) {
/* Remove the symbol from the list */
lub_list_del(this->syms, iter);
/* Free the instance */
clish_sym_free((clish_sym_t *)lub_list_node__get_data(iter));
lub_list_node_free(iter);
}
lub_list_free(this->syms);
#ifdef HAVE_DLFCN_H
if (this->dlhan)
dlclose(this->dlhan);
#endif
free(this);
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_add_generic(clish_plugin_t *this,
void *func, const char *name, int type, bool_t permanent)
{
clish_sym_t *sym;
if (!name || !func)
return NULL;
if (!(sym = clish_sym_new(name, func, type)))
return NULL;
clish_sym__set_plugin(sym, this);
clish_sym__set_permanent(sym, permanent);
lub_list_add(this->syms, sym);
return sym;
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_add_sym(clish_plugin_t *this,
clish_hook_action_fn_t *func, const char *name)
{
return clish_plugin_add_generic(this, func,
name, CLISH_SYM_TYPE_ACTION, BOOL_FALSE);
}
/*--------------------------------------------------------- */
/* Add permanent symbol (can't be turned off by dry-run) */
clish_sym_t *clish_plugin_add_psym(clish_plugin_t *this,
clish_hook_action_fn_t *func, const char *name)
{
return clish_plugin_add_generic(this, func,
name, CLISH_SYM_TYPE_ACTION, BOOL_TRUE);
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_add_osym(clish_plugin_t *this,
clish_hook_oaction_fn_t *func, const char *name)
{
clish_sym_t *s;
if (!(s = clish_plugin_add_generic(this, func,
name, CLISH_SYM_TYPE_ACTION, BOOL_FALSE)))
return s;
clish_sym__set_api(s, CLISH_SYM_API_STDOUT);
return s;
}
/*--------------------------------------------------------- */
/* Add permanent symbol (can't be turned off by dry-run) */
clish_sym_t *clish_plugin_add_posym(clish_plugin_t *this,
clish_hook_oaction_fn_t *func, const char *name)
{
clish_sym_t *s;
if (!(s = clish_plugin_add_generic(this, func,
name, CLISH_SYM_TYPE_ACTION, BOOL_TRUE)))
return s;
clish_sym__set_api(s, CLISH_SYM_API_STDOUT);
return s;
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_add_hook(clish_plugin_t *this,
void *func, const char *name, int type)
{
return clish_plugin_add_generic(this, func,
name, type, BOOL_FALSE);
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_add_phook(clish_plugin_t *this,
void *func, const char *name, int type)
{
return clish_plugin_add_generic(this, func,
name, type, BOOL_TRUE);
}
/*--------------------------------------------------------- */
void clish_plugin_add_fini(clish_plugin_t *this,
clish_plugin_fini_t *fini)
{
this->fini = fini;
}
/*--------------------------------------------------------- */
clish_plugin_fini_t * clish_plugin_get_fini(clish_plugin_t *this)
{
return this->fini;
}
/*--------------------------------------------------------- */
void clish_plugin_add_init(clish_plugin_t *this,
clish_plugin_init_t *init)
{
this->init = init;
}
/*--------------------------------------------------------- */
clish_plugin_init_t * clish_plugin_get_init(clish_plugin_t *this)
{
return this->init;
}
/*--------------------------------------------------------- */
clish_sym_t *clish_plugin_get_sym(clish_plugin_t *this, const char *name, int type)
{
lub_list_node_t *iter;
clish_sym_t *sym;
/* Iterate elements */
for(iter = lub_list__get_head(this->syms);
iter; iter = lub_list_node__get_next(iter)) {
int res;
sym = (clish_sym_t *)lub_list_node__get_data(iter);
res = strcmp(clish_sym__get_name(sym), name);
if (!res && ((CLISH_SYM_TYPE_NONE == type) || (clish_sym__get_type(sym) == type)))
return sym;
if (res > 0) /* No chances to find name */
break;
}
return NULL;
}
/*--------------------------------------------------------- */
static int clish_plugin_load_shared(clish_plugin_t *this)
{
#ifdef HAVE_DLFCN_H
char *file = NULL; /* Plugin so file name */
char *init_name = NULL; /* Init function name */
int flag = RTLD_NOW;
if (this->file) {
file = lub_string_dup(this->file);
} else {
lub_string_cat(&file, "clish_plugin_");
lub_string_cat(&file, this->name);
lub_string_cat(&file, ".so");
}
/* Open dynamic library */
if (clish_plugin__get_rtld_global(this))
flag |= RTLD_GLOBAL;
else
flag |= RTLD_LOCAL;
this->dlhan = dlopen(file, flag);
lub_string_free(file);
if (!this->dlhan) {
fprintf(stderr, "Error: Can't open plugin \"%s\": %s\n",
this->name, dlerror());
return -1;
}
/* Get plugin init function */
lub_string_cat(&init_name, CLISH_PLUGIN_INIT_NAME_PREFIX);
lub_string_cat(&init_name, this->name);
lub_string_cat(&init_name, CLISH_PLUGIN_INIT_NAME_SUFFIX);
this->init = (clish_plugin_init_t *)dlsym(this->dlhan, init_name);
lub_string_free(init_name);
if (!this->init) {
fprintf(stderr, "Error: Can't get plugin \"%s\" init function: %s\n",
this->name, dlerror());
return -1;
}
return 0;
#else /* HAVE_DLFCN_H */
/* We have no any dl functions. */
fprintf(stderr, "Error: Can't get plugin \"%s\" init function.\n",
this->name);
return -1;
#endif /* HAVE_DLFCN_H */
}
/*--------------------------------------------------------- */
int clish_plugin_load(clish_plugin_t *this, void *userdata)
{
int res;
if (!this)
return -1;
assert(this->name);
/* Builtin plugins already have init function. */
if (!this->builtin_flag) {
if (clish_plugin_load_shared(this) < 0)
return -1;
}
if (!this->init) {
fprintf(stderr, "Error: PLUGIN %s has no init function\n",
this->name);
return -1;
}
/* Execute init function */
if ((res = this->init(userdata, this)))
fprintf(stderr, "Error: Plugin %s init retcode: %d\n",
this->name, res);
return res;
}
/*--------------------------------------------------------- */
char *clish_plugin__get_name(const clish_plugin_t *this)
{
return this->name;
}
/*--------------------------------------------------------- */
void clish_plugin__set_alias(clish_plugin_t *this, const char *alias)
{
lub_string_free(this->alias);
this->alias = lub_string_dup(alias);
}
/*--------------------------------------------------------- */
char *clish_plugin__get_alias(const clish_plugin_t *this)
{
return this->alias;
}
/*--------------------------------------------------------- */
char *clish_plugin__get_pubname(const clish_plugin_t *this)
{
return (this->alias ? this->alias : this->name);
}
/*--------------------------------------------------------- */
void clish_plugin__set_file(clish_plugin_t *this, const char *file)
{
lub_string_free(this->file);
this->file = lub_string_dup(file);
}
/*--------------------------------------------------------- */
char *clish_plugin__get_file(const clish_plugin_t *this)
{
return this->file;
}
/*--------------------------------------------------------- */
void clish_plugin__set_builtin_flag(clish_plugin_t *this, bool_t builtin_flag)
{
this->builtin_flag = builtin_flag;
}
/*--------------------------------------------------------- */
bool_t clish_plugin__get_builtin_flag(const clish_plugin_t *this)
{
return this->builtin_flag;
}
/*--------------------------------------------------------- */
void clish_plugin__set_conf(clish_plugin_t *this, const char *conf)
{
lub_string_free(this->conf);
this->conf = lub_string_dup(conf);
}
/*--------------------------------------------------------- */
char *clish_plugin__get_conf(const clish_plugin_t *this)
{
return this->conf;
}
/*--------------------------------------------------------- */
void clish_plugin__set_rtld_global(clish_plugin_t *this, bool_t rtld_global)
{
this->rtld_global = rtld_global;
}
/*--------------------------------------------------------- */
bool_t clish_plugin__get_rtld_global(const clish_plugin_t *this)
{
return this->rtld_global;
}
/*--------------------------------------------------------- */