Plugin Management support C plugin
This commit is contained in:
@@ -1,106 +1,313 @@
|
||||
#include "deps/uthash/uthash.h"
|
||||
#include "sdk/include/session.h"
|
||||
#include "sdk/include/plugin.h"
|
||||
#include "session_manager.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "plugin_manager_util.h"
|
||||
#include "plugin_manager_config.h"
|
||||
#include "plugin_manager_module.h"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
/******************************************************************************
|
||||
* CallBack Static Hash Table (For Global)
|
||||
* CallBack Runtime (For Per Session)
|
||||
******************************************************************************/
|
||||
|
||||
struct session_event_callback_static
|
||||
struct eventcb_runtime
|
||||
{
|
||||
enum session_event_type event;
|
||||
fn_session_event_callback *callback;
|
||||
};
|
||||
|
||||
/*
|
||||
* Hast table
|
||||
*
|
||||
* key: string -> session_type
|
||||
* val: array -> event_cbs
|
||||
*/
|
||||
struct managed_session_event_callback
|
||||
{
|
||||
char session_type[16];
|
||||
struct session_event_callback_static event_cbs[0]; // dynamic array
|
||||
// Record the number of callback functions that want to process the current session
|
||||
int event_cbs_num;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* CallBack Runtime Array (For Per Session)
|
||||
******************************************************************************/
|
||||
|
||||
struct session_event_callback_runtime
|
||||
{
|
||||
int skip; // for skip this plugin callback
|
||||
void *callback_args;
|
||||
int skip;
|
||||
void *cb_args;
|
||||
|
||||
enum session_event_type event;
|
||||
fn_session_event_callback *callback;
|
||||
fn_session_event_callback *cb;
|
||||
};
|
||||
|
||||
struct session_plugin_ctx
|
||||
{
|
||||
int event_cbs_num;
|
||||
struct session_event_callback_runtime event_cbs[0]; // dynamic array
|
||||
int current_plugin_index;
|
||||
int eventcb_num;
|
||||
struct eventcb_runtime *eventcbs;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* struct plugin_manager
|
||||
* CallBack Static (For Per Plugin Manager)
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* Each plugin has an init_cb and an exit_cb, but may have multiple entry_cb.
|
||||
* entry_cb is stored in the global_session_callback_ctx.
|
||||
*/
|
||||
struct plugin_ctx
|
||||
struct eventcb_static
|
||||
{
|
||||
char *name;
|
||||
char *library;
|
||||
/*
|
||||
* Stores the context generated by the plugin initialization,
|
||||
* which is used to release resources when the plugin ends.
|
||||
*/
|
||||
void *pme;
|
||||
plugin_init_callback *init_cb;
|
||||
plugin_exit_callback *exit_cb;
|
||||
enum session_event_type event;
|
||||
fn_session_event_callback *cb;
|
||||
};
|
||||
|
||||
struct plugin_manager_eventcb
|
||||
{
|
||||
char session_name[MAX_SESSION_NAME_LENGTH]; // key
|
||||
|
||||
int eventcb_num; // val size
|
||||
struct eventcb_static *eventcbs; // val: dynamic array
|
||||
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Struct plugin_manager
|
||||
******************************************************************************/
|
||||
|
||||
struct plugin_manager
|
||||
{
|
||||
struct managed_session_event_callback *cb_table;
|
||||
struct plugin_ctx plugins[MAX_PLUGIN_NUM];
|
||||
int used_module_num;
|
||||
int used_config_num;
|
||||
int used_evencb_num; // only plugin register eventcb numbers
|
||||
|
||||
struct plugin_manager_module *modules[MAX_PLUGIN_NUM];
|
||||
struct plugin_manager_config *configs[MAX_PLUGIN_NUM];
|
||||
struct plugin_manager_eventcb *evcb_htable;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Private API
|
||||
* Create/Destory plugin ctx (per session)
|
||||
******************************************************************************/
|
||||
|
||||
static void plugin_manager_handle_opening_event(struct plugin_manager *plug_mgr, struct stellar_event *event)
|
||||
static struct session_plugin_ctx *plugin_manager_create_plugin_ctx(struct plugin_manager *plug_mgr, const char *session_name)
|
||||
{
|
||||
if (session_name == NULL || strlen(session_name) == 0)
|
||||
{
|
||||
plugin_manager_log(ERROR, "invalid parameter, session name is empty");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct plugin_manager_eventcb *elem;
|
||||
HASH_FIND_STR(plug_mgr->evcb_htable, session_name, elem);
|
||||
if (elem == NULL)
|
||||
{
|
||||
plugin_manager_log(ERROR, "can't find event callback for session name '%s'", session_name);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct session_plugin_ctx *plug_ctx = safe_alloc(struct session_plugin_ctx, 1);
|
||||
plug_ctx->eventcb_num = elem->eventcb_num;
|
||||
plug_ctx->eventcbs = safe_alloc(struct eventcb_runtime, plug_ctx->eventcb_num);
|
||||
|
||||
for (int i = 0; i < plug_ctx->eventcb_num; i++)
|
||||
{
|
||||
plug_ctx->eventcbs[i].skip = 0;
|
||||
plug_ctx->eventcbs[i].event = elem->eventcbs[i].event;
|
||||
plug_ctx->eventcbs[i].cb = elem->eventcbs[i].cb;
|
||||
plug_ctx->eventcbs[i].cb_args = NULL;
|
||||
}
|
||||
|
||||
return plug_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_manager_handle_data_event(struct plugin_manager *plug_mgr, struct stellar_event *event)
|
||||
{
|
||||
}
|
||||
|
||||
static void plugin_manager_handle_closing_event(struct plugin_manager *plug_mgr, struct stellar_event *event)
|
||||
static void plugin_manager_destory_plugin_ctx(struct session_plugin_ctx *plug_ctx)
|
||||
{
|
||||
if (plug_ctx)
|
||||
{
|
||||
safe_free(plug_ctx->eventcbs);
|
||||
safe_free(plug_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Public API
|
||||
* Tools for managing plugins
|
||||
******************************************************************************/
|
||||
|
||||
static int plugin_manager_parse_plugins(struct plugin_manager *plug_mgr, const char *prefix, const char *file)
|
||||
{
|
||||
char plugin_inf[4096] = {0};
|
||||
char line_buffer[4096] = {0};
|
||||
if (strlen(prefix) <= 0)
|
||||
{
|
||||
plugin_manager_log(ERROR, "Invalid parameter, plugin config file prefix cannot be empty");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(file) <= 0)
|
||||
{
|
||||
plugin_manager_log(ERROR, "Invalid parameter, plugin config file name cannot be empty");
|
||||
return -1;
|
||||
}
|
||||
strcat_prefix_and_file(prefix, file, plugin_inf, sizeof(plugin_inf));
|
||||
|
||||
FILE *fp = fopen(plugin_inf, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
plugin_manager_log(ERROR, "can't open %s, %s", plugin_inf, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(line_buffer, sizeof(line_buffer), fp))
|
||||
{
|
||||
if ('#' == line_buffer[0] || '\n' == line_buffer[0])
|
||||
{
|
||||
memset(line_buffer, 0, sizeof(line_buffer));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plug_mgr->used_config_num >= MAX_PLUGIN_NUM)
|
||||
{
|
||||
plugin_manager_log(ERROR, "the number of registered plugins exceeds the limit and cannot exceed %d", MAX_PLUGIN_NUM);
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct plugin_manager_config *config = plugin_mangager_config_create();
|
||||
if (plugin_mangager_config_parse(config, prefix, line_buffer) == -1)
|
||||
{
|
||||
plugin_mangager_config_destory(config);
|
||||
goto err;
|
||||
}
|
||||
|
||||
plug_mgr->configs[plug_mgr->used_config_num] = config;
|
||||
plug_mgr->used_config_num++;
|
||||
memset(line_buffer, 0, sizeof(line_buffer));
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int plugin_manager_open_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_config_num; i++)
|
||||
{
|
||||
struct plugin_manager_config *config = plug_mgr->configs[i];
|
||||
struct plugin_manager_module *module = plugin_manager_module_open(config);
|
||||
if (module == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
plug_mgr->modules[plug_mgr->used_module_num] = module;
|
||||
plug_mgr->used_module_num++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plugin_manager_register_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_module_num; i++)
|
||||
{
|
||||
struct plugin_manager_module *module = plug_mgr->modules[i];
|
||||
if (plugin_manager_module_register(plug_mgr, module) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
plug_mgr->used_evencb_num++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plugin_manager_init_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_module_num; i++)
|
||||
{
|
||||
struct plugin_manager_module *module = plug_mgr->modules[i];
|
||||
if (plugin_manager_module_init(module) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
double percentage = ((double)(i + 1)) / ((double)plug_mgr->used_module_num) * ((double)100);
|
||||
plugin_manager_log(INFO, "Plugin initialization progress: [%.2f%]", percentage);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void plugin_manager_exit_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
if (plug_mgr && plug_mgr->used_module_num)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_module_num; i++)
|
||||
{
|
||||
struct plugin_manager_module *module = plug_mgr->modules[i];
|
||||
plugin_manager_module_exit(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_manager_close_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
if (plug_mgr && plug_mgr->used_module_num)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_module_num; i++)
|
||||
{
|
||||
struct plugin_manager_module *module = plug_mgr->modules[i];
|
||||
plugin_manager_module_close(module);
|
||||
}
|
||||
plug_mgr->used_module_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// deparse for destory parse
|
||||
static void plugin_manager_deparse_plugins(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
if (plug_mgr && plug_mgr->used_config_num)
|
||||
{
|
||||
for (int i = 0; i < plug_mgr->used_config_num; i++)
|
||||
{
|
||||
struct plugin_manager_config *config = plug_mgr->configs[i];
|
||||
plugin_mangager_config_destory(config);
|
||||
}
|
||||
plug_mgr->used_config_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Public API for managing plugins
|
||||
******************************************************************************/
|
||||
|
||||
int plugin_manager_load(struct plugin_manager *plug_mgr, const char *prefix, const char *file)
|
||||
{
|
||||
if (plugin_manager_parse_plugins(plug_mgr, prefix, file) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (plugin_manager_open_plugins(plug_mgr) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (plugin_manager_register_plugins(plug_mgr) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (plugin_manager_init_plugins(plug_mgr) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void plugin_manager_unload(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
plugin_manager_exit_plugins(plug_mgr);
|
||||
plugin_manager_close_plugins(plug_mgr);
|
||||
plugin_manager_deparse_plugins(plug_mgr);
|
||||
}
|
||||
|
||||
struct plugin_manager *plugin_manager_create()
|
||||
{
|
||||
struct plugin_manager *plug_mgr = NULL;
|
||||
struct plugin_manager *plug_mgr = safe_alloc(struct plugin_manager, 1);
|
||||
|
||||
plug_mgr->used_module_num = 0;
|
||||
plug_mgr->used_config_num = 0;
|
||||
plug_mgr->used_evencb_num = 0;
|
||||
|
||||
plug_mgr->evcb_htable = NULL;
|
||||
|
||||
return plug_mgr;
|
||||
}
|
||||
@@ -109,45 +316,138 @@ void plugin_manager_destory(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
if (plug_mgr)
|
||||
{
|
||||
if (plug_mgr->evcb_htable)
|
||||
{
|
||||
struct plugin_manager_eventcb *elem;
|
||||
struct plugin_manager_eventcb *tmp;
|
||||
HASH_ITER(hh, plug_mgr->evcb_htable, elem, tmp)
|
||||
{
|
||||
HASH_DEL(plug_mgr->evcb_htable, elem);
|
||||
|
||||
safe_free(elem->eventcbs);
|
||||
safe_free(elem);
|
||||
}
|
||||
|
||||
plug_mgr->evcb_htable = NULL;
|
||||
plug_mgr->used_evencb_num = 0;
|
||||
}
|
||||
|
||||
plugin_manager_unload(plug_mgr);
|
||||
safe_free(plug_mgr);
|
||||
}
|
||||
}
|
||||
|
||||
int plugin_manager_load(struct plugin_manager *plug_mgr, const char *plugin_file)
|
||||
int plugin_manager_register(struct plugin_manager *plug_mgr, const char *session_name, enum session_event_type event, fn_session_event_callback *cb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (strlen(session_name) <= 0)
|
||||
{
|
||||
plugin_manager_log(ERROR, "invalid parameter, session name is empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void plugin_manager_unload(struct plugin_manager *plug_mgr)
|
||||
{
|
||||
}
|
||||
if (strlen(session_name) > MAX_SESSION_NAME_LENGTH)
|
||||
{
|
||||
plugin_manager_log(ERROR, "invalid parameter, session name '%s' is too long and exceeds '%d' bytes", session_name, MAX_SESSION_NAME_LENGTH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cb == NULL)
|
||||
{
|
||||
plugin_manager_log(ERROR, "invalid parameter, the callback corresponding to the session name '%s' is null", session_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct plugin_manager_eventcb *elem;
|
||||
HASH_FIND_STR(plug_mgr->evcb_htable, session_name, elem);
|
||||
// session_name exists, add a new cb to the end of the eventcbs dynamic array
|
||||
if (elem)
|
||||
{
|
||||
elem->eventcbs = (struct eventcb_static *)realloc(elem->eventcbs, (elem->eventcb_num + 1) * sizeof(struct eventcb_static));
|
||||
|
||||
elem->eventcbs[elem->eventcb_num].event = event;
|
||||
elem->eventcbs[elem->eventcb_num].cb = cb;
|
||||
|
||||
elem->eventcb_num++;
|
||||
}
|
||||
// session_name does not exist, allocate a new node elem, and add elem to the hash table
|
||||
else
|
||||
{
|
||||
elem = safe_alloc(struct plugin_manager_eventcb, 1);
|
||||
memcpy(elem->session_name, session_name, strlen(session_name));
|
||||
|
||||
elem->eventcbs = (struct eventcb_static *)realloc(elem->eventcbs, (elem->eventcb_num + 1) * sizeof(struct eventcb_static));
|
||||
|
||||
elem->eventcbs[elem->eventcb_num].event = event;
|
||||
elem->eventcbs[elem->eventcb_num].cb = cb;
|
||||
|
||||
elem->eventcb_num++;
|
||||
|
||||
HASH_ADD_STR(plug_mgr->evcb_htable, session_name, elem);
|
||||
}
|
||||
|
||||
int plugin_manager_register(struct plugin_manager *plug_mgr, const char *session_type, fn_session_event_callback *callback)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void plugin_manager_dispatch(struct plugin_manager *plug_mgr, struct stellar_event *event)
|
||||
{
|
||||
assert(event);
|
||||
struct stellar_session_event_data *event_data = event->session_event_data;
|
||||
assert(event_data);
|
||||
session_event_type type = event_data->type;
|
||||
const struct stellar_session *seesion = stellar_event_get_session(event);
|
||||
struct session_plugin_ctx *plug_ctx = stellar_event_get_plugin_ctx(event);
|
||||
enum session_event_type event_type = stellar_event_get_type(event);
|
||||
const char *session_name = stellar_event_get_session_name(event);
|
||||
struct stellar_packet *packet = stellar_event_get_packet(event);
|
||||
uint16_t payload_len = stellar_event_get_payload_length(event);
|
||||
const char *payload = stellar_event_get_payload(event);
|
||||
|
||||
switch (type)
|
||||
assert(seesion);
|
||||
assert(session_name);
|
||||
|
||||
// the same session may trigger multi times opening events
|
||||
if (event_type & SESSION_EVENT_OPENING)
|
||||
{
|
||||
case SESSION_EVENT_OPENING:
|
||||
plugin_manager_handle_opening_event(plug_mgr, event);
|
||||
break;
|
||||
case SESSION_EVENT_RAWPKT: /* fall through */
|
||||
case SESSION_EVENT_ORDPKT: /* fall through */
|
||||
case SESSION_EVENT_META: /* fall through */
|
||||
plugin_manager_handle_data_event(plug_mgr, event);
|
||||
break;
|
||||
case SESSION_EVENT_CLOSING:
|
||||
plugin_manager_handle_closing_event(plug_mgr, event);
|
||||
break;
|
||||
default:
|
||||
// TODO log error
|
||||
break;
|
||||
if (plug_ctx == NULL)
|
||||
{
|
||||
plug_ctx = plugin_manager_create_plugin_ctx(plug_mgr, session_name);
|
||||
if (plug_ctx == NULL)
|
||||
{
|
||||
plugin_manager_log(ERROR, "can't create runtime plugin ctx for session '%s', Please check whether the callback is registered in the current session");
|
||||
return;
|
||||
}
|
||||
stellar_event_set_plugin_ctx(event, plug_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (plug_ctx)
|
||||
{
|
||||
for (int i = 0; i < plug_ctx->eventcb_num; i++)
|
||||
{
|
||||
plug_ctx->current_plugin_index = i;
|
||||
struct eventcb_runtime *runtime = &plug_ctx->eventcbs[i];
|
||||
if (runtime->skip)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (runtime->event & event_type)
|
||||
{
|
||||
runtime->cb(seesion, event_type, packet, payload, payload_len, &runtime->cb_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin_manager_log(ERROR, "session '%s' runtime plugin ctx is null when running event callback", session_name);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (event_type & SESSION_EVENT_CLOSING)
|
||||
{
|
||||
plugin_manager_destory_plugin_ctx(plug_ctx);
|
||||
stellar_event_set_plugin_ctx(event, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Suppport LUA plugins
|
||||
******************************************************************************/
|
||||
|
||||
// TODO
|
||||
Reference in New Issue
Block a user