/************************************************************************* > File Name: lua_plugin_manage.c > Author: > Created Time: 2024-08 > Encoding : UTF-8 ************************************************************************/ /************************************************************************* * version * [ v0.1 ] * 08-05 * 1. 实现函数 * void specific_instance_copy; * void specific_instance_destory; * int script_instance_init_byname; * int script_instance_init_byrefid; * void script_instance_clean; * int script_execute; (暂未实现) * int session_plugin_instance_init; * void session_plugin_instance_destory; * int plugin_env_instance_init; * void plugin_env_instance_destory; * int thread_state_instance_init; * void thread_state_instance_destory; * int thread_state_instance_load; * int lua_plugin_manage_config_load; * int lua_plugin_manage_thread_load; ************************************************************************/ #include "lua_plugin_manage_internal.h" #include "lpm_log.h" #include #include #include #include #include static UT_icd specific_icd = {sizeof(struct lua_config_specific), NULL, specific_instance_copy, specific_instance_destory}; static UT_icd session_plugin_icd = {sizeof(struct lua_session_plugin), NULL, NULL, session_plugin_instance_destory}; static UT_icd plugin_env_icd = {sizeof(struct lua_plugin_env), NULL, NULL, plugin_env_instance_destory}; static UT_icd thread_state_icd = {sizeof(struct lua_thread_state), NULL, NULL, thread_state_instance_destory}; /* * Function: specific_instance_copy * Input: | void * | dst | 拷贝数据目的地址 * | void * | src | 拷贝数据源地址 * Output: * Return: * Description: 配置的插件信息拷贝函数, 由于可能存在多线程, 将数据完整拷贝一次 */ void specific_instance_copy(void *dst, const void *src) { struct lua_config_specific *dst_spec = (struct lua_config_specific *)dst; struct lua_config_specific *src_spec = (struct lua_config_specific *)src; if (__glibc_unlikely(!dst_spec || !src_spec)) return; dst_spec->lua_config_specific_file = strdup(src_spec->lua_config_specific_file); // dst_spec->lua_config_specific_name = strdup(src_spec->lua_config_specific_name); dst_spec->lua_config_specific_load_func = strdup(src_spec->lua_config_specific_load_func); dst_spec->lua_config_specific_unload_func = strdup(src_spec->lua_config_specific_unload_func); return; } /* * Function: specific_instance_destory * Input: | void * | elt | 待销毁的specific实例 * Output: * Return: * Description: 销毁一个specific实例, 并释放内部占用的内存 */ void specific_instance_destory(void *elt) { struct lua_config_specific *element = (struct lua_config_specific *)elt; if (!element) return; if (element->lua_config_specific_file) free(element->lua_config_specific_file); // if (element->lua_config_specific_name) // free(element->lua_config_specific_name); if (element->lua_config_specific_load_func) free(element->lua_config_specific_load_func); if (element->lua_config_specific_unload_func) free(element->lua_config_specific_unload_func); return; } /* * Function: script_instance_init_byname * Input: | struct lua_script * | script | 需要进行初始化的脚本结构 * | lua_State * | state | 脚本注册使用的状态机 * | const char * | filepath | 脚本加载的文件路径 * | const char * | funcname | 脚本加载的函数名称 * Output: * Return: | 0 | 加载成功 * | -1 | 参数错误 * | -3 | 状态机读取文件失败 * | -4 | 状态机加载文件后运行失败 * | -5 | 加载函数过程中未找到该函数 * | -6 | 创建函数引用ID失败 * Description: 将一个lua文件中的函数创建成一个lua_script结构, 方便调用运行 */ int script_instance_init_byname( struct lua_script *script, lua_State *state, const char *filepath, const char *funcname) { if (__glibc_unlikely(!script || !state || !filepath || !funcname)) return -1; #if 0 /* 在加载配置文件的时候检查过了, 这里不检查 */ if (access(filepath, F_OK)) /* 文件不存在 */ return -2; #endif if (luaL_loadfile(state, filepath)) /* 加载文件失败 */ return -3; /* 仅加载不运行 */ // lua_settop(state, 0); /* Bugfix: 这里还是需要运行一次, 否则可能加载不完整 */ if (lua_pcall(state, 0, 0, 0)) { /* 文件加载完成后检查存在错误 */ lua_settop(state, 0); return -4; } lua_settop(state, 0); /* 加载全局变量, 找到函数后进行类型判断 */ lua_getglobal(state, funcname); if (lua_type(state, -1) != LUA_TFUNCTION) { /* 在已经加载的文件中未找到对应的函数名称 */ lua_settop(state, 0); return -5; } /* 创建索引 */ int ref_id = luaL_ref(state, LUA_REGISTRYINDEX); if (ref_id == LUA_REFNIL) { /* 索引创建失败 */ lua_settop(state, 0); return -6; } script_instance_init_byrefid(script, state, ref_id); lua_settop(state, 0); return 0; } /* * Function: script_instance_init_byrefid * Input: | struct lua_script * | script | 需要进行初始化的脚本结构 * | lua_State * | state | 脚本注册使用的状态机 * | int | ref_id | 代码已经在状态机中生成的refid * Output: * Return: | 0 | 初始化成功 * | -1 | 参数错误 * Description: 根据已经生成的refid初始化一个脚本结构实例 */ int script_instance_init_byrefid( struct lua_script *script, lua_State *state, int ref_id) { if (__glibc_unlikely(!script || !state || ref_id == LUA_REFNIL)) return -1; script->script_state = state; script->script_ref_id = ref_id; script->script_run_success = 0; script->script_run_failed = 0; script->script_total_ms = 0; return 0; } /* * Function: script_instance_clean * Input: | struct lua_script * | script | 需要清理的脚本实例 * Output: * Return: * Description: 清理一个脚本, 并在状态机中释放该脚本的引用 */ void script_instance_clean(struct lua_script *script) { if (__glibc_unlikely(!script)) return; lua_State *state = script->script_state; luaL_unref(state, LUA_REGISTRYINDEX, script->script_ref_id); lua_settop(state, 0); return; } /* * Function: * Input: * Output: * Return: * Description: */ int script_execute(struct lua_script *script) { if (!script) return -1; return 0; } /* * Function: session_plugin_instance_init * Input: | struct lua_session_plugin * | session_plugin | 需要进行初始化的插件实例 * | lua_State * | state | 插件注册使用的状态机 * | int | plugin_id | 插件注册完成后得到的插件编号 * | int | new_refid | ctx_new函数在状态机中的引用 * | int | free_refid | ctx_free函数在状态机中的引用 * Output: * Return: | 0 | 初始化成功 * | -1 | 参数错误 * Description: 初始化一个插件实例 */ int session_plugin_instance_init( struct lua_session_plugin *session_plugin, lua_State *state, int plugin_id, int new_refid, int free_refid) { if (__glibc_unlikely(!session_plugin || !state)) return -1; session_plugin->plugin_id = plugin_id; script_instance_init_byrefid(&session_plugin->plugin_ctx_new_script, state, new_refid); script_instance_init_byrefid(&session_plugin->plugin_ctx_free_script, state, free_refid); return 0; } /* * Function: session_plugin_instance_destory * Input: | void * | elt | 待销毁的插件实例 * Output: * Return: * Description: 销毁一个插件实例, 并将插件中引用的函数在状态机中释放 */ void session_plugin_instance_destory(void *elt) { if (__glibc_unlikely(!elt)) return; struct lua_session_plugin *session_plugin = (struct lua_session_plugin *)elt; script_instance_clean(&session_plugin->plugin_ctx_new_script); script_instance_clean(&session_plugin->plugin_ctx_free_script); return; } /* * Function: plugin_env_instance_init * Input: | struct lua_plugin_env * | plugin_env | 插件的运行环境实例 * | lua_State * | state | 插件注册的状态机 * | struct lua_config_specific * | specific | 插件需要加载的配置实例 * Output: * Return: | 0 | 初始化成功 * | -1 | 参数错误 * | -2 | 加载函数加载错误 * | -3 | 卸载函数加载错误 * | -4 | 创建全局函数变量失败 * Description: 初始化一个插件的运行环境变量 */ int plugin_env_instance_init( struct lua_plugin_env *plugin_env, lua_State *state, struct lua_config_specific *specific) { if (__glibc_unlikely(!plugin_env || !state || !specific)) return -1; memset(plugin_env, 0, sizeof(struct lua_plugin_env)); plugin_env->lua_plugin_env_state = state; plugin_env->lua_plugin_env_plugin_array = NULL; // plugin_env->lua_plugin_env_name = strdup(specific->lua_config_specific_name); /* 从文件中加载load函数, 但此时不调用 */ if (script_instance_init_byname(&plugin_env->lua_plugin_env_load_func, state, specific->lua_config_specific_file, specific->lua_config_specific_load_func)) { return -2; } /* 从文件中加载unload函数 */ if (script_instance_init_byname(&plugin_env->lua_plugin_env_unload_func, state, specific->lua_config_specific_file, specific->lua_config_specific_unload_func)) { script_instance_clean(&plugin_env->lua_plugin_env_load_func); return -3; } /* 创建私有数据空间 */ lua_newtable(plugin_env->lua_plugin_env_state); int ref_id = luaL_ref(plugin_env->lua_plugin_env_state, LUA_REGISTRYINDEX); if (ref_id == LUA_REFNIL) return -4; plugin_env->lua_plugin_env_ref_id = ref_id; return 0; } /* * Function: plugin_env_instance_destory * Input: | void * | elt | 待销毁的插件运行环境实例 * Output: * Return: * Description: 销毁一个插件运行环境实例, 并在状态机中释放其引用 */ void plugin_env_instance_destory(void *elt) { if (__glibc_unlikely(!elt)) return; struct lua_plugin_env *plugin_env = (struct lua_plugin_env *)elt; /* 释放已经注册的插件 */ if (plugin_env->lua_plugin_env_plugin_array) utarray_free(plugin_env->lua_plugin_env_plugin_array); /* 释放插件的加载函数与卸载函数 */ script_instance_clean(&plugin_env->lua_plugin_env_load_func); script_instance_clean(&plugin_env->lua_plugin_env_unload_func); /* 将lua中的私有环境数据取消引用 */ if (plugin_env->lua_plugin_env_ref_id) luaL_unref(plugin_env->lua_plugin_env_state, LUA_REGISTRYINDEX, plugin_env->lua_plugin_env_ref_id); return; } /* * Function: thread_state_instance_init * Input: | struct lua_thread_state * | thread_state | 待初始化的线程状态机 * | int | state_id | 线程的ID号 * Output: * Return: | 0 | 初始化成功 * | -1 | 参数错误 * | -2 | 创建新的状态机失败 * Description: 在线程中创建一个状态机, 并进行初始化 */ int thread_state_instance_init( struct lua_thread_state *thread_state, int state_id) { if (__glibc_unlikely(!thread_state)) return -1; lua_State *new_state = luaL_newstate(); if (__glibc_unlikely(!new_state)) return -2; luaL_openlibs(new_state); /* 绑定所有注册函数 */ lua_cbinding_functions(new_state); /* 绑定所有全局变量 */ lua_cbinding_datas(new_state); lua_settop(new_state, 0); memset(thread_state, 0, sizeof(struct lua_thread_state)); thread_state->lua_thread_id = state_id; thread_state->lua_thread_plugin_count = 0; thread_state->lua_thread_state = new_state; thread_state->lua_thread_env_array = NULL; thread_state->lua_thread_begin_time = time(NULL); return 0; } /* * Function: thread_state_instance_destory * Input: | void * | elt | 待销毁的状态机 * Output: * Return: * Description: 销毁一个线程状态机 */ void thread_state_instance_destory(void *elt) { if (__glibc_unlikely(!elt)) return; struct lua_thread_state *thread_state = (struct lua_thread_state *)elt; /* 卸载所有已经注册的插件 */ if (thread_state->lua_thread_env_array) utarray_free(thread_state->lua_thread_env_array); /* 关闭状态机 */ if (thread_state->lua_thread_state) lua_close(thread_state->lua_thread_state); return; } /* * Function: thread_state_instance_load * Input: | struct lua_thread_state * | thread_state | 进行初始化加载的状态机 * | struct lua_plugin_manage_schema * | schema | 已经完成配置读取的全局实例 * Output: * Return: | 0 | 加载成功 * | -1 | 参数错误 * | -2 | 根据加载的配置初始化插件失败 * Description: 在线程的状态机中依次加载插件 */ int thread_state_instance_load( struct lua_thread_state *thread_state, struct lua_plugin_manage_schema *schema) { if (__glibc_unlikely(!thread_state || !schema)) return -1; lua_State *state = thread_state->lua_thread_state; utarray_new(thread_state->lua_thread_env_array, &plugin_env_icd); utarray_reserve(thread_state->lua_thread_env_array, schema->lua_config_specific_count); struct lua_config_specific *specific = NULL; /* 读取所有插件并加入插件列表中 */ struct lua_plugin_env new_plugin_env; while ((specific = utarray_next(schema->lua_config_specific_array, specific))) { memset(&new_plugin_env, 0, sizeof(new_plugin_env)); if (plugin_env_instance_init(&new_plugin_env, state, specific)) return -2; utarray_push_back(thread_state->lua_thread_env_array, &new_plugin_env); } /* 依次执行插件加载函数, 调用lua中load函数 */ struct lua_plugin_env *plugin_env = NULL; while ((plugin_env = utarray_next(thread_state->lua_thread_env_array, plugin_env))) { utarray_new(plugin_env->lua_plugin_env_plugin_array, &session_plugin_icd); lua_rawgeti(state, LUA_REGISTRYINDEX, plugin_env->lua_plugin_env_ref_id); /* 在lua中的全局参数中加入plugin_env指针 */ lua_pushlightuserdata(state, plugin_env); lua_setfield(state, -2, PLUGIN_ENV_DEFAULT_KEY); lua_settop(state, 0); script_execute(&plugin_env->lua_plugin_env_load_func); } return 0; } /* * Function: specific_check * Input: | struct lua_config_specific * | specific | 待减产的配置项 * Output: * Return: | 0 | 检查无问题 * | -1 | 参数错误 * | -2 | 配置中的插件文件不存在 * Description: 检查配置是否有明显问题 * TODO: 目前仅检查文件是否存在, 后续逐步补充, 比如文件内是否有不应该使用的函数等 */ static int specific_check(struct lua_config_specific *specific) { if (__glibc_unlikely(!specific)) return -1; if (access(specific->lua_config_specific_file, F_OK)) return -2; return 0; } /* * Function: lua_plugin_manage_config_load * Input: | struct lua_plugin_manage_schema * | scheme | 加载插件的全局变量 * | const char * | config_file_name | 插件配置文件 * Output: * Return: | >=0 | 加载成功, 返回读取的配置插件数量 * | -1 | 参数错误 * | -2 | 配置文件不存在或打开文件错误 * | -3 | toml加载失败 * | -4 | 获取插件列表失败 * | -5 | 插件配置项错误 * | -6 | 插件配置检查出现问题 * Description: 从配置文件中读取所有插件配置 */ int lua_plugin_manage_config_load( struct lua_plugin_manage_schema *schema, const char *config_file_name) { if (__glibc_unlikely(!schema || !config_file_name)) return -1; int specific_num = 0; char errbuff[256] = {0}; if (access(config_file_name, F_OK)) return -2; FILE *fp = fopen(config_file_name, "r"); if (!fp) return -2; toml_table_t *conf = toml_parse_file(fp, errbuff, sizeof(errbuff)); if (fp) fclose(fp); if (!conf) { LOGERROR("parse config file failed, filename %s, err %s\n", config_file_name, errbuff); return -3; } toml_array_t *plugin_array = toml_array_in(conf, "plugin"); if (!plugin_array) return -4; /* 获取配置中配置的插件数量 */ specific_num = toml_array_nelem(plugin_array); utarray_new(schema->lua_config_specific_array, &specific_icd); utarray_reserve(schema->lua_config_specific_array, specific_num); struct lua_config_specific *specific = (struct lua_config_specific *)calloc(sizeof(struct lua_config_specific), 1); for (int i = 0; i < specific_num; ++i) { toml_table_t *plugin = toml_table_at(plugin_array, i); const char *raw_filepath = toml_raw_in(plugin, "path"); // const char *raw_name = toml_raw_in(plugin, "name"); const char *raw_load_func_name = toml_raw_in(plugin, "init"); const char *raw_unload_func_name = toml_raw_in(plugin, "exit"); /* 从配置中拷贝字符串到specific实例中 */ if (toml_rtos(raw_filepath, &specific->lua_config_specific_file) || // toml_rtos(raw_name, &specific->lua_config_specific_name) || toml_rtos(raw_load_func_name, &specific->lua_config_specific_load_func) || toml_rtos(raw_unload_func_name, &specific->lua_config_specific_unload_func)) { toml_free(conf); free(specific); return -5; } /* 插件配置检查 */ int spec_check_ret = specific_check(specific); if (spec_check_ret) { LOGERROR("check specific failed, ret is %d\n", spec_check_ret); toml_free(conf); free(specific); return -6; } /* 插入到队列中 */ utarray_push_back(schema->lua_config_specific_array, specific); specific_instance_destory(specific); } free(specific); /* 返回加载的插件数量 */ return specific_num; } /* * Function: lua_plugin_manage_thread_load * Input: | struct lua_plugin_manage_schema * | schema | 初始化的全局变量 * | int | thread_count | 初始化的线程数量 * Output: * Return: | >= 0 | 创建完成, 并返回创建成功数量 * | -1 | 参数错误 * | -2 | 线程状态机初始化失败 * | -3 | 线程状态机加载过程失败 * Description: 依次创建好每个线程中的状态机 */ int lua_plugin_manage_thread_load( struct lua_plugin_manage_schema *schema, int thread_count) { if (__glibc_unlikely(!schema || thread_count <= 0 || thread_count >= LUA_PLUGIN_MANAGE_MAX_THREAD_COUNT)) return -1; utarray_new(schema->lua_plugin_thread_array, &thread_state_icd); utarray_reserve(schema->lua_plugin_thread_array, thread_count); struct lua_thread_state new_thread_state; for (int i = 1; i <= thread_count; ++i) { memset(&new_thread_state, 0, sizeof(new_thread_state)); if (thread_state_instance_init(&new_thread_state, i)) return -2; utarray_push_back(schema->lua_plugin_thread_array, &new_thread_state); } struct lua_thread_state *thread_state = NULL; while ((thread_state = utarray_next(schema->lua_plugin_thread_array, thread_state))) { if (thread_state_instance_load(thread_state, schema)) return -3; } return thread_count; } /* * Function: * Input: * Output: * Return: * Description: */ struct lua_plugin_manage_schema *lua_plugin_manage_init( struct stellar *st, const char *config_file_path) { if (__glibc_unlikely(!st || !config_file_path)) return NULL; struct lua_plugin_manage_schema *new_schema = (struct lua_plugin_manage_schema *)calloc(1, sizeof(struct lua_plugin_manage_schema)); if (__glibc_unlikely(!new_schema)) return NULL; memset(new_schema, 0, sizeof(struct lua_plugin_manage_schema)); new_schema->lua_plugin_schema_st = st; /* 从配置文件中加载插件 */ new_schema->lua_config_specific_count = lua_plugin_manage_config_load(new_schema, config_file_path); LOGDEBUG("load config finish, load count is %d, file path is %s", new_schema->lua_config_specific_count, config_file_path); if (new_schema->lua_config_specific_count < 0) { /* 可以没有插件配置, 但是不能在加载过程中出错 */ free(new_schema); return NULL; } #ifdef LUAPLUGIN_BASIC_UNITTEST struct lua_config_specific *specific = NULL; while ((specific = utarray_next(new_schema->lua_config_specific_array, specific))) { printf("path is %s\n", specific->lua_config_specific_file); printf("load is %s\n", specific->lua_config_specific_load_func); printf("unload is %s\n", specific->lua_config_specific_unload_func); } #endif int thread_count = 1; /* 依次创建线程状态机, 并加入链表 */ new_schema->lua_plugin_thread_count = lua_plugin_manage_thread_load(new_schema, thread_count); return new_schema; } /* * Function: * Input: * Output: * Return: * Description: */ void lua_plugin_manage_exit(struct lua_plugin_manage_schema *lua_plug_mgr) { if (__glibc_unlikely(!lua_plug_mgr)) return; if (lua_plug_mgr->lua_config_specific_array) utarray_free(lua_plug_mgr->lua_config_specific_array); if (lua_plug_mgr->lua_plugin_thread_array) utarray_free(lua_plug_mgr->lua_plugin_thread_array); return; }