新增插件系统: - plugin.h / plugin.c:dlopen/dlsym 动态加载,最多8个插件 - 插件接口:cocoon_plugin_init / cocoon_plugin_shutdown / cocoon_plugin_version - 插件可通过 cocoon_middleware_register() 注册中间件参与请求处理 - 命令行:--plugin <path>(可多次指定) - 配置文件:plugins 字段(字符串,单插件路径) - server.c 在 server_create 时加载,server_destroy 时逆序卸载 - plugins/hello.c:示例插件,演示中间件注册 - 集成测试:新增插件加载 + 日志验证测试(68项全部通过) - 单元测试:127个全部通过 - 编译:Makefile 添加 plugin.c 和 -ldl - .gitignore 排除编译产物和上传目录
122 lines
3.3 KiB
C
122 lines
3.3 KiB
C
/**
|
||
* plugin.c - 插件系统实现
|
||
*
|
||
* 使用 dlopen / dlsym 实现动态加载。
|
||
* 保持线程安全:加载/卸载在主线程完成。
|
||
*
|
||
* @author xfy
|
||
*/
|
||
|
||
#include "plugin.h"
|
||
#include "log.h"
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
|
||
/* dlopen 需要 RTLD_NOW 和 RTLD_LOCAL */
|
||
#include <dlfcn.h>
|
||
|
||
/* === 插件条目 === */
|
||
typedef struct {
|
||
void *handle; /**< dlopen 句柄 */
|
||
char path[256]; /**< 插件路径 */
|
||
cocoon_plugin_shutdown_func_t shutdown; /**< 关闭函数 */
|
||
cocoon_plugin_version_func_t version; /**< 版本函数 */
|
||
} plugin_entry_t;
|
||
|
||
static plugin_entry_t g_plugins[MAX_PLUGINS];
|
||
static size_t g_plugin_count = 0;
|
||
|
||
/**
|
||
* cocoon_plugin_load - 加载一个插件
|
||
*/
|
||
int cocoon_plugin_load(const char *path) {
|
||
if (!path || !path[0]) {
|
||
log_error("plugin: 空路径");
|
||
return -1;
|
||
}
|
||
if (g_plugin_count >= MAX_PLUGINS) {
|
||
log_error("plugin: 注册表已满(最多 %d 个)", MAX_PLUGINS);
|
||
return -1;
|
||
}
|
||
|
||
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
|
||
if (!handle) {
|
||
log_error("plugin: 无法加载 %s: %s", path, dlerror());
|
||
return -1;
|
||
}
|
||
|
||
/* 查找符号 */
|
||
cocoon_plugin_init_func_t init = (cocoon_plugin_init_func_t)dlsym(handle, "cocoon_plugin_init");
|
||
cocoon_plugin_shutdown_func_t shutdown = (cocoon_plugin_shutdown_func_t)dlsym(handle, "cocoon_plugin_shutdown");
|
||
cocoon_plugin_version_func_t version = (cocoon_plugin_version_func_t)dlsym(handle, "cocoon_plugin_version");
|
||
|
||
const char *ver_str = version ? version() : "unknown";
|
||
|
||
/* 如果存在 init,则调用 */
|
||
if (init) {
|
||
int ret = init();
|
||
if (ret != 0) {
|
||
log_error("plugin: %s 初始化失败(返回 %d),已卸载", path, ret);
|
||
dlclose(handle);
|
||
return -1;
|
||
}
|
||
} else {
|
||
log_warn("plugin: %s 缺少 cocoon_plugin_init,仅加载", path);
|
||
}
|
||
|
||
/* 保存到注册表 */
|
||
plugin_entry_t *entry = &g_plugins[g_plugin_count];
|
||
entry->handle = handle;
|
||
entry->shutdown = shutdown;
|
||
entry->version = version;
|
||
strncpy(entry->path, path, sizeof(entry->path) - 1);
|
||
entry->path[sizeof(entry->path) - 1] = '\0';
|
||
g_plugin_count++;
|
||
|
||
log_info("plugin: 已加载 %s (v%s, #%zu)", path, ver_str, g_plugin_count);
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* cocoon_plugin_unload_all - 卸载所有插件
|
||
*/
|
||
void cocoon_plugin_unload_all(void) {
|
||
/* 逆序卸载 */
|
||
for (size_t i = g_plugin_count; i > 0; i--) {
|
||
plugin_entry_t *entry = &g_plugins[i - 1];
|
||
if (entry->shutdown) {
|
||
entry->shutdown();
|
||
}
|
||
dlclose(entry->handle);
|
||
log_info("plugin: 已卸载 %s", entry->path);
|
||
}
|
||
g_plugin_count = 0;
|
||
}
|
||
|
||
/**
|
||
* cocoon_plugin_count - 已加载插件数量
|
||
*/
|
||
size_t cocoon_plugin_count(void) {
|
||
return g_plugin_count;
|
||
}
|
||
|
||
/**
|
||
* cocoon_plugin_get_version - 获取插件版本
|
||
*/
|
||
const char *cocoon_plugin_get_version(size_t index) {
|
||
if (index >= g_plugin_count) return "unknown";
|
||
if (g_plugins[index].version) {
|
||
return g_plugins[index].version();
|
||
}
|
||
return "unknown";
|
||
}
|
||
|
||
/**
|
||
* cocoon_plugin_get_path - 获取插件路径
|
||
*/
|
||
const char *cocoon_plugin_get_path(size_t index) {
|
||
if (index >= g_plugin_count) return NULL;
|
||
return g_plugins[index].path;
|
||
}
|