cocoon/plugin.c
xfy911 952cc663fe feat(plugin): 实现动态插件系统(.so 加载 + 中间件注册)
新增插件系统:
- 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 排除编译产物和上传目录
2026-06-06 03:13:59 +08:00

122 lines
3.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
}