This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
niubinghui-luapluginmanage/src/lua_plugin_manage.c

650 lines
22 KiB
C

/*************************************************************************
> 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 <time.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <toml.h>
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;
}