#include "deps/uthash/uthash.h" #include "session_manager.h" #include "plugin_manager_util.h" #include "plugin_manager_config.h" #include "plugin_manager_module.h" #include #include /****************************************************************************** * CallBack Runtime (For Per Session) ******************************************************************************/ struct eventcb_runtime { int skip; void *cb_args; enum session_event_type event; fn_session_event_callback *cb; }; struct session_plugin_ctx { int current_plugin_index; int eventcb_num; struct eventcb_runtime *eventcbs; }; /****************************************************************************** * CallBack Static (For Per Plugin Manager) ******************************************************************************/ struct eventcb_static { 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 { 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; }; /****************************************************************************** * Create/Destory plugin ctx (per session) ******************************************************************************/ 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_destory_plugin_ctx(struct session_plugin_ctx *plug_ctx) { if (plug_ctx) { safe_free(plug_ctx->eventcbs); safe_free(plug_ctx); } } /****************************************************************************** * 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 = 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; } 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_register(struct plugin_manager *plug_mgr, const char *session_name, enum session_event_type event, fn_session_event_callback *cb) { if (strlen(session_name) <= 0) { plugin_manager_log(ERROR, "invalid parameter, session name is empty"); return -1; } 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); } return 0; } void plugin_manager_dispatch(struct plugin_manager *plug_mgr, struct stellar_event *event) { 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); assert(seesion); assert(session_name); // the same session may trigger multi times opening events if (event_type & SESSION_EVENT_OPENING) { 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