use super::types::{CreatePluginFn, PluginInstance};
use super::validation::{sanitize_path_string, sanitize_plugin_name, validate_path_within_base};
use crate::error::{PluginManagerError, PluginManagerResult};
use crate::status::ChildPluginInfo;
use crate::status::PluginStatus;
use libloading::{Library, Symbol};
use secra_pluginctl as pluginctl;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tracing::{debug, error, info, trace, warn};
use tracing_shared::SharedLogger;
pub type FnSetupLogger = fn(&SharedLogger);
pub async fn scan_plugin_directory(plugin_dir: &str) -> PluginManagerResult<Vec<String>> {
trace!("开始扫描插件目录: {}", plugin_dir);
let mut plugin_files = Vec::new();
let mut entries = tokio::fs::read_dir(plugin_dir).await?;
debug!("成功打开插件目录: {}", plugin_dir);
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
trace!("检查文件: {}", path.display());
if path.is_file() {
if let Some(extension) = path.extension() {
let ext_str = extension.to_str().unwrap_or("");
if ext_str == "spk" {
if let Some(path_str) = path.to_str() {
debug!("找到插件文件: {}", path_str);
plugin_files.push(path_str.to_string());
}
}
}
}
}
info!("扫描完成,共找到 {} 个插件文件", plugin_files.len());
Ok(plugin_files)
}
pub fn validate_keys(
ed25519_public_key_path: Option<String>,
rsa_private_key_path: Option<String>,
) -> PluginManagerResult<(String, String)> {
trace!("开始验证密钥配置");
let ed25519_key = ed25519_public_key_path.ok_or_else(|| {
error!("未配置 Ed25519 公钥路径");
PluginManagerError::ConfigError("未配置 Ed25519 公钥路径,无法验证插件签名".to_string())
})?;
debug!("Ed25519 公钥路径: {}", ed25519_key);
let rsa_key = rsa_private_key_path.ok_or_else(|| {
error!("未配置 RSA 私钥路径");
PluginManagerError::ConfigError("未配置 RSA 私钥路径,无法解密插件".to_string())
})?;
debug!("RSA 私钥路径: {}", rsa_key);
if !Path::new(&ed25519_key).exists() {
error!("Ed25519 公钥文件不存在: {}", ed25519_key);
return Err(PluginManagerError::ConfigError(
"Ed25519 公钥文件不存在".to_string(),
));
}
trace!("Ed25519 公钥文件存在: {}", ed25519_key);
if !Path::new(&rsa_key).exists() {
error!("RSA 私钥文件不存在: {}", rsa_key);
return Err(PluginManagerError::ConfigError(
"RSA 私钥文件不存在".to_string(),
));
}
trace!("RSA 私钥文件存在: {}", rsa_key);
info!("密钥验证通过");
Ok((ed25519_key, rsa_key))
}
pub fn verify_plugin_signature(
plugin_file: &str,
ed25519_key: &str,
rsa_key: &str,
) -> PluginManagerResult<()> {
trace!("开始验证插件签名: {}", plugin_file);
info!("验证插件签名和完整性");
let unpacker = pluginctl::Unpacker::new(PathBuf::from(ed25519_key), PathBuf::from(rsa_key))
.map_err(|e| {
error!(
"创建 Unpacker 失败: {} - 公钥: {}, 私钥: {}",
e, ed25519_key, rsa_key
);
PluginManagerError::LoadFailed(format!("创建 Unpacker 失败: {}", e))
})?;
debug!("Unpacker 创建成功");
unpacker.verify(plugin_file).map_err(|e| {
error!("插件签名验证失败: {} - {}", plugin_file, e);
PluginManagerError::SignatureCheckFailed(e.to_string())
})?;
info!("插件签名验证通过: {}", plugin_file);
trace!("签名验证完成: {}", plugin_file);
Ok(())
}
pub async fn prepare_plugin_temp_dir(
temp_dir: &str,
plugin_name: &str,
) -> PluginManagerResult<PathBuf> {
trace!(
"准备插件临时目录 - 插件: {}, 临时目录: {}",
plugin_name, temp_dir
);
let safe_plugin_name = plugin_name
.chars()
.filter(|c| c.is_alphanumeric() || *c == '.' || *c == '-' || *c == '_')
.collect::<String>();
debug!("插件名称清理: {} -> {}", plugin_name, safe_plugin_name);
if safe_plugin_name.is_empty() {
error!("插件名称无效:清理后为空 - 原始名称: {}", plugin_name);
return Err(PluginManagerError::ValidationFailed(
"插件名称无效:清理后为空".to_string(),
));
}
if safe_plugin_name.len() > super::constants::MAX_PLUGIN_NAME_LEN {
error!(
"插件名称过长: {} (最大长度: {})",
safe_plugin_name.len(),
super::constants::MAX_PLUGIN_NAME_LEN
);
return Err(PluginManagerError::ValidationFailed(format!(
"插件名称过长:超过{}个字符",
super::constants::MAX_PLUGIN_NAME_LEN
)));
}
let temp_dir_path = Path::new(temp_dir);
if !temp_dir_path.exists() {
debug!("基础临时目录不存在,创建: {}", temp_dir);
tokio::fs::create_dir_all(temp_dir_path).await?;
tokio::fs::create_dir_all(temp_dir_path)
.await
.map_err(|e| {
error!("创建基础临时目录失败: {} - {}", temp_dir, e);
PluginManagerError::Io(e)
})?;
} else {
trace!("基础临时目录已存在: {}", temp_dir);
}
let plugin_temp_dir = Path::new(temp_dir).join(&safe_plugin_name);
debug!("插件临时目录路径: {}", plugin_temp_dir.display());
info!("解包插件到临时目录: {}", plugin_temp_dir.display());
tokio::fs::create_dir_all(&plugin_temp_dir)
.await
.map_err(|e| {
error!(
"创建插件临时目录失败: {} - {}",
plugin_temp_dir.display(),
e
);
PluginManagerError::Io(e)
})?;
trace!("插件临时目录创建成功");
if !plugin_temp_dir.exists() {
error!("插件临时目录创建后不存在: {}", plugin_temp_dir.display());
return Err(PluginManagerError::LoadFailed(format!(
"插件临时目录创建失败: {}",
plugin_temp_dir.display()
)));
}
validate_path_within_base(temp_dir_path, &plugin_temp_dir)?;
trace!("路径验证通过");
Ok(plugin_temp_dir)
}
pub fn unpack_plugin(
plugin_file: &str,
output_dir: &Path,
ed25519_key: &str,
rsa_key: &str,
) -> PluginManagerResult<()> {
trace!("开始解包插件: {} -> {}", plugin_file, output_dir.display());
let canonical_output_dir = output_dir.canonicalize().map_err(|e| {
error!("无法规范化输出目录路径: {} - {}", output_dir.display(), e);
PluginManagerError::Io(e)
})?;
debug!("规范化后的输出目录: {}", canonical_output_dir.display());
let output_dir_str = canonical_output_dir.to_str().ok_or_else(|| {
error!(
"插件临时目录路径无法转换为字符串: {}",
canonical_output_dir.display()
);
PluginManagerError::LoadFailed("插件临时目录路径无法转换为字符串".to_string())
})?;
if !canonical_output_dir.exists() {
error!("输出目录不存在: {}", canonical_output_dir.display());
return Err(PluginManagerError::LoadFailed(format!(
"输出目录不存在: {}",
canonical_output_dir.display()
)));
}
trace!("输出目录存在: {}", canonical_output_dir.display());
let metadata = std::fs::metadata(&canonical_output_dir).map_err(|e| {
error!(
"无法获取输出目录元数据: {} - {}",
canonical_output_dir.display(),
e
);
PluginManagerError::Io(e)
})?;
if !metadata.is_dir() {
error!("输出路径不是目录: {}", canonical_output_dir.display());
return Err(PluginManagerError::LoadFailed(format!(
"输出路径不是目录: {}",
canonical_output_dir.display()
)));
}
debug!("输出目录验证通过");
info!("准备解包插件: {} -> {}", plugin_file, output_dir_str);
let unpacker = pluginctl::Unpacker::new(PathBuf::from(ed25519_key), PathBuf::from(rsa_key))
.map_err(|e| {
error!(
"创建 Unpacker 失败: {} - 公钥: {}, 私钥: {}",
e, ed25519_key, rsa_key
);
PluginManagerError::LoadFailed(format!("创建 Unpacker 失败: {}", e))
})?;
debug!("Unpacker 创建成功,开始解包");
let output_dir_str = output_dir.to_str().ok_or_else(|| {
error!("插件临时目录路径无法转换为字符串: {}", output_dir.display());
PluginManagerError::LoadFailed("插件临时目录路径无法转换为字符串".to_string())
})?;
unpacker.unpack(plugin_file, output_dir_str).map_err(|e| {
error!(
"插件解包失败: {} -> {} - {}",
plugin_file, output_dir_str, e
);
PluginManagerError::LoadFailed(format!("插件解包失败: {}", e))
})?;
info!("插件解包成功: {} -> {}", plugin_file, output_dir_str);
trace!("解包完成");
Ok(())
}
fn get_target_triple() -> String {
let arch = if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else if cfg!(target_arch = "arm") {
"arm"
} else {
"unknown"
};
let os = if cfg!(target_os = "windows") {
"pc-windows-gnu"
} else if cfg!(target_os = "macos") {
"apple-darwin"
} else if cfg!(target_os = "linux") {
let env = if cfg!(target_env = "musl") {
"musl"
} else if cfg!(target_env = "gnu") {
"gnu"
} else {
"unknown"
};
return format!("{}-unknown-linux-{}", arch, env);
} else {
"unknown"
};
format!("{}-unknown-{}", arch, os)
}
pub fn get_library_file_path(
plugin_temp_dir: &Path,
plugin_name: &str,
library_path: Option<&String>,
) -> PluginManagerResult<PathBuf> {
trace!(
"获取动态库文件路径 - 插件: {}, 临时目录: {}, 库路径: {:?}",
plugin_name,
plugin_temp_dir.display(),
library_path
);
let sanitized_name = sanitize_plugin_name(plugin_name)?;
debug!("插件名称清理: {} -> {}", plugin_name, sanitized_name);
let extension = if cfg!(target_os = "windows") {
"dll"
} else if cfg!(target_os = "macos") {
"dylib"
} else {
"so"
};
debug!("目标平台动态库扩展名: {}", extension);
let safe_plugin_name = plugin_name
.chars()
.filter(|c| c.is_alphanumeric() || *c == '.' || *c == '-' || *c == '_')
.collect::<String>();
let library_filename = format!("lib{}.{}", safe_plugin_name, extension);
debug!("动态库文件名: {}", library_filename);
let library_file_path = if let Some(lib_path) = library_path {
let sanitized_lib_path = sanitize_path_string(lib_path, true);
debug!("清理后的库路径: {}", sanitized_lib_path);
plugin_temp_dir
.join(&sanitized_lib_path)
.join(&library_filename)
} else {
let target_triple = get_target_triple();
debug!("自动使用目标三元组作为库路径: {}", target_triple);
plugin_temp_dir
.join(&target_triple)
.join(&library_filename)
};
trace!("构建的动态库路径: {}", library_file_path.display());
validate_path_within_base(plugin_temp_dir, &library_file_path)?;
trace!("路径安全性验证通过");
if !library_file_path.exists() {
error!("动态库文件不存在: {}", library_file_path.display());
error!("插件临时目录: {}", plugin_temp_dir.display());
error!("插件名称: {} (清理后: {})", plugin_name, sanitized_name);
error!("库路径: {:?}", library_path);
error!("库文件名: {}", library_filename);
return Err(PluginManagerError::LoadFailed(format!(
"动态库文件不存在: {}",
library_file_path.display()
)));
}
trace!("动态库文件存在");
if !library_file_path.is_file() {
error!("动态库路径不是文件: {}", library_file_path.display());
return Err(PluginManagerError::LoadFailed(
"动态库路径不是文件".to_string(),
));
}
info!("动态库文件验证通过: {}", library_file_path.display());
debug!("动态库路径: {}", library_file_path.display());
Ok(library_file_path)
}
pub fn load_dynamic_library(library_file_path: &Path) -> PluginManagerResult<Arc<Library>> {
trace!("开始加载动态库: {}", library_file_path.display());
info!("加载动态库: {}", library_file_path.display());
let library = unsafe {
Library::new(library_file_path).map_err(|e| {
error!("加载动态库失败: {} - {}", library_file_path.display(), e);
PluginManagerError::LoadFailed(format!("加载动态库失败: {}", e))
})?
};
debug!("动态库句柄创建成功");
let library = Arc::new(library);
info!("动态库加载成功: {}", library_file_path.display());
trace!("动态库加载完成");
Ok(library)
}
pub fn get_plugin_factory_function(
library: &Arc<Library>,
plugin_name: &str,
) -> PluginManagerResult<CreatePluginFn> {
trace!("开始获取插件工厂函数: {}", plugin_name);
info!("获取插件工厂函数: {}", plugin_name);
let create_plugin_fn: CreatePluginFn = unsafe {
let symbol: Symbol<CreatePluginFn> = library.get(b"create_plugin").map_err(|e| {
error!("获取插件工厂函数失败: {} - {}", plugin_name, e);
PluginManagerError::LoadFailed(format!(
"获取插件工厂函数失败: 动态库中未找到 create_plugin 函数 - {}",
e
))
})?;
debug!("成功从动态库中获取 create_plugin 符号");
*symbol
};
info!("成功获取插件工厂函数: {}", plugin_name);
trace!("插件工厂函数获取完成");
Ok(create_plugin_fn)
}
pub fn create_plugin_instance(
create_plugin_fn: CreatePluginFn,
library: Arc<Library>,
artifact: &pluginctl::PluginArtifact,
library_file_path: &Path,
plugin_name: &str,
plugin_temp_dir: PathBuf,
) -> PluginManagerResult<PluginInstance> {
trace!("开始创建插件实例: {}", plugin_name);
info!("创建插件实例: {}", plugin_name);
let factory = create_plugin_fn();
debug!("插件工厂创建成功");
let plugin = factory.create();
debug!("插件对象创建成功");
let metadata = plugin.metadata().clone();
trace!(
"插件元数据: ID={}, 名称={}, 版本={}",
metadata.id, metadata.name, metadata.version
);
let fingerprint = artifact.header.signature.signature.clone();
debug!("插件指纹: {}", fingerprint);
let plugin_instance = PluginInstance {
plugin,
metadata,
status: PluginStatus::Loaded,
child_plugins: Vec::new(),
_library: library,
fingerprint,
temp_library_path: Some(library_file_path.to_path_buf()),
plugin_temp_dir,
};
info!(
"插件实例创建成功: {} (ID: {}, 版本: {})",
plugin_name, plugin_instance.metadata.id, plugin_instance.metadata.version
);
trace!("插件实例创建完成");
Ok(plugin_instance)
}
pub async fn load_plugin(
plugin_file: &str,
ed25519_public_key_path: Option<String>,
rsa_private_key_path: Option<String>,
temp_dir: &str,
library_path: Option<&String>,
logger: SharedLogger,
) -> PluginManagerResult<PluginInstance> {
unsafe {
trace!("开始加载插件流程: {}", plugin_file);
info!("开始加载插件: {}", plugin_file);
debug!("步骤 1/7: 验证密钥配置");
let (ed25519_key, rsa_key) = validate_keys(ed25519_public_key_path, rsa_private_key_path)?;
debug!("步骤 2/7: 验证插件签名");
verify_plugin_signature(plugin_file, &ed25519_key, &rsa_key)?;
debug!("步骤 3/7: 加载插件包元数据");
let artifact = pluginctl::PluginArtifact::load_from_file(plugin_file).map_err(|e| {
error!("加载插件包失败: {} - {}", plugin_file, e);
PluginManagerError::LoadFailed(format!("加载插件包失败: {}", e))
})?;
let plugin_name = artifact.header.metadata.name.clone();
let plugin_id = artifact.header.metadata.package_id.to_string();
info!(
"插件信息 - 名称: {}, ID: {}, 版本: {}",
plugin_name, plugin_id, artifact.header.metadata.version
);
debug!("插件元数据加载成功");
debug!("步骤 4/7: 准备插件临时目录");
let plugin_temp_dir = prepare_plugin_temp_dir(temp_dir, &plugin_name).await?;
debug!("步骤 5/7: 解包插件文件");
unpack_plugin(plugin_file, &plugin_temp_dir, &ed25519_key, &rsa_key)?;
debug!("步骤 6/7: 加载动态库");
let library_file_path =
get_library_file_path(&plugin_temp_dir, &plugin_name, library_path)?;
let library = load_dynamic_library(&library_file_path)?;
info!("动态库加载成功---------1");
debug!("尝试获取 setup_shared_logger_ref 符号以共享日志");
match library.get::<FnSetupLogger>(b"setup_shared_logger_ref") {
Ok(symbol) => {
let setup_logger: FnSetupLogger = *symbol;
setup_logger(&logger);
info!("插件 {} 日志共享设置成功", plugin_name);
}
Err(e) => {
warn!(
"插件 {} 未导出 setup_shared_logger_ref 符号,将使用独立日志系统: {}",
plugin_name, e
);
}
}
info!("动态库加载成功---------2");
let create_plugin_fn = get_plugin_factory_function(&library, &plugin_name)?;
debug!("步骤 7/7: 创建插件实例");
let plugin_instance = create_plugin_instance(
create_plugin_fn,
library,
&artifact,
&library_file_path,
&plugin_name,
plugin_temp_dir,
)?;
info!(
"插件加载成功: {} (ID: {}, 版本: {})",
plugin_name, plugin_id, artifact.header.metadata.version
);
trace!("插件加载流程完成");
Ok(plugin_instance)
}
}