secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
//! 插件加载相关方法

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);

/// 扫描插件目录,查找所有插件文件
///
/// 异步扫描指定的插件目录,查找所有 `.spk` 格式的插件文件。
/// 返回所有找到的插件文件的完整路径列表。
///
/// # 参数
/// * `plugin_dir` - 要扫描的插件目录路径
///
/// # 返回值
/// * `PluginManagerResult<Vec<String>>` - 成功时返回插件文件路径列表
///
/// # 错误
/// * `PluginManagerError::Io` - 如果目录读取失败
///
/// # 行为
/// * 递归读取目录中的所有文件
/// * 只识别扩展名为 `.spk` 的文件
/// * 返回文件的完整路径字符串
///
/// # 示例
/// ```no_run
/// # async fn example() {
/// let plugin_files = scan_plugin_directory("/opt/secra/plugins").await?;
/// for file in plugin_files {
///     println!("找到插件文件: {}", file);
/// }
/// # }
/// ```
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("");
                // 支持 .spk (Secra Plugin Package) 扩展名
                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)
}

/// 验证密钥配置和文件存在性
///
/// 验证 Ed25519 公钥和 RSA 私钥的配置和文件存在性。
/// 这两个密钥是插件加载过程中必需的:公钥用于验证签名,私钥用于解密。
///
/// # 参数
/// * `ed25519_public_key_path` - Ed25519 公钥文件路径(可选)
/// * `rsa_private_key_path` - RSA 私钥文件路径(可选)
///
/// # 返回值
/// * `PluginManagerResult<(String, String)>` - 成功时返回元组:
///   - `String`: Ed25519 公钥文件路径
///   - `String`: RSA 私钥文件路径
///
/// # 错误
/// * `PluginManagerError::ConfigError` - 如果密钥未配置或文件不存在
///
/// # 行为
/// * 检查两个密钥路径是否都已配置
/// * 验证密钥文件是否真实存在
/// * 返回验证通过的密钥路径
///
/// # 示例
/// ```no_run
/// # fn example() {
/// let (ed25519_key, rsa_key) = validate_keys(
///     Some("/path/to/ed25519.pub".to_string()),
///     Some("/path/to/rsa.key".to_string()),
/// )?;
/// println!("Ed25519 公钥: {}", ed25519_key);
/// println!("RSA 私钥: {}", rsa_key);
/// # }
/// ```
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))
}

/// 验证插件签名和完整性
///
/// 使用 Ed25519 公钥验证插件包的数字签名,确保插件的完整性和来源可信。
/// 这是插件加载过程中的关键安全步骤。
///
/// # 参数
/// * `plugin_file` - 插件文件路径
/// * `ed25519_key` - Ed25519 公钥文件路径
/// * `rsa_key` - RSA 私钥文件路径(用于创建 Unpacker,虽然验证签名只需要公钥)
///
/// # 返回值
/// * `PluginManagerResult<()>` - 验证通过返回 `Ok(())`
///
/// # 错误
/// * `PluginManagerError::LoadFailed` - 如果创建 Unpacker 失败
/// * `PluginManagerError::SignatureCheckFailed` - 如果签名验证失败
///
/// # 行为
/// * 创建 Unpacker 实例用于验证
/// * 调用 Unpacker 的 `verify` 方法验证签名
/// * 验证通过后记录信息日志
///
/// # 安全
/// 签名验证失败意味着插件可能被篡改或来源不可信,此时会拒绝加载。
///
/// # 示例
/// ```no_run
/// # fn example() {
/// verify_plugin_signature(
///     "/path/to/plugin.spk",
///     "/path/to/ed25519.pub",
///     "/path/to/rsa.key",
/// )?;
/// println!("插件签名验证通过");
/// # }
/// ```
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(())
}

/// 准备插件临时目录
///
/// 为插件创建专用的临时目录,用于解包插件文件。
/// 目录名称基于插件名称,并经过安全清理以防止路径遍历攻击。
///
/// # 参数
/// * `temp_dir` - 基础临时目录路径
/// * `plugin_name` - 插件名称,用于创建子目录
///
/// # 返回值
/// * `PluginManagerResult<PathBuf>` - 成功时返回插件临时目录的路径
///
/// # 错误
/// * `PluginManagerError::ValidationFailed` - 如果插件名称验证失败
/// * `PluginManagerError::Io` - 如果目录创建失败
/// * `PluginManagerError::LoadFailed` - 如果目录创建后验证失败
///
/// # 行为
/// * 清理插件名称,移除危险字符
/// * 确保基础临时目录存在
/// * 创建插件专用的子目录
/// * 验证路径安全性,防止路径遍历
/// * 验证目录是否成功创建
///
/// # 安全
/// 使用 `sanitize_plugin_name` 和 `validate_path_within_base` 确保路径安全。
///
/// # 示例
/// ```no_run
/// # async fn example() {
/// let plugin_temp_dir = prepare_plugin_temp_dir(
///     "/opt/secra/plugins/temp",
///     "my-plugin",
/// ).await?;
/// println!("插件临时目录: {}", plugin_temp_dir.display());
/// # }
/// ```
pub async fn prepare_plugin_temp_dir(
    temp_dir: &str,
    plugin_name: &str,
) -> PluginManagerResult<PathBuf> {
    trace!(
        "准备插件临时目录 - 插件: {}, 临时目录: {}",
        plugin_name, temp_dir
    );

    // 清理插件名称,防止路径遍历攻击
    // 允许点号,因为插件名称可能包含点号(如 com.example.child-plugin)
    // 但移除所有路径分隔符和其他危险字符
    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()
        )));
    }

    // 创建目录后再验证路径(canonicalize 要求路径存在)
    validate_path_within_base(temp_dir_path, &plugin_temp_dir)?;
    trace!("路径验证通过");

    Ok(plugin_temp_dir)
}

/// 解包插件
///
/// 将加密的插件包(.spk 文件)解包到指定的输出目录。
/// 解包过程包括解密和文件提取,使用 RSA 私钥解密 AES 密钥。
///
/// # 参数
/// * `plugin_file` - 插件文件路径(.spk 格式)
/// * `output_dir` - 解包输出的目标目录
/// * `ed25519_key` - Ed25519 公钥文件路径(用于创建 Unpacker)
/// * `rsa_key` - RSA 私钥文件路径(用于解密插件包)
///
/// # 返回值
/// * `PluginManagerResult<()>` - 解包成功返回 `Ok(())`
///
/// # 错误
/// * `PluginManagerError::Io` - 如果路径规范化或元数据获取失败
/// * `PluginManagerError::LoadFailed` - 如果创建 Unpacker 失败或解包失败
///
/// # 行为
/// * 规范化输出目录路径
/// * 验证输出目录存在且为目录类型
/// * 创建 Unpacker 实例
/// * 调用 Unpacker 的 `unpack` 方法解包插件
/// * 记录解包成功的日志
///
/// # 注意
/// 解包前会验证输出目录的有效性,确保解包过程的安全性。
///
/// # 示例
/// ```no_run
/// use std::path::Path;
///
/// # fn example() {
/// unpack_plugin(
///     "/path/to/plugin.spk",
///     Path::new("/opt/secra/plugins/temp/my-plugin"),
///     "/path/to/ed25519.pub",
///     "/path/to/rsa.key",
/// )?;
/// println!("插件解包成功");
/// # }
/// ```
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(())
}

/// 获取当前编译目标的三元组(target triple)
///
/// 根据编译时的目标架构、操作系统和环境,构建目标三元组字符串。
/// 用于确定插件包中平台特定的库文件子目录。
///
/// # 返回值
/// * `String` - 目标三元组字符串,例如:
///   * `"x86_64-unknown-linux-gnu"` - Linux GNU
///   * `"x86_64-unknown-linux-musl"` - Linux musl
///   * `"aarch64-apple-darwin"` - macOS ARM
///   * `"x86_64-pc-windows-gnu"` - Windows
///
/// # 平台差异
/// * Linux: 区分 `gnu` 和 `musl` 环境
/// * macOS: 使用 `apple-darwin`
/// * Windows: 使用 `pc-windows-gnu`
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") {
        // Linux 需要区分 musl 和 gnu
        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)
}

/// 获取动态库文件路径并验证文件是否存在
///
/// 根据插件名称和配置的库路径,构建动态库文件的完整路径。
/// 路径构建会考虑不同操作系统的动态库扩展名(.dll, .dylib, .so)。
/// 同时进行路径安全验证,防止路径遍历攻击。
///
/// # 参数
/// * `plugin_temp_dir` - 插件临时目录路径
/// * `plugin_name` - 插件名称,用于构建库文件名
/// * `library_path` - 可选的动态库子路径,如果库文件不在临时目录根目录
///                     如果为 None,将自动使用当前目标三元组作为子目录
///
/// # 返回值
/// * `PluginManagerResult<PathBuf>` - 成功时返回动态库文件的完整路径
///
/// # 错误
/// * `PluginManagerError::ValidationFailed` - 如果插件名称验证失败或路径验证失败
/// * `PluginManagerError::LoadFailed` - 如果动态库文件不存在或不是文件
///
/// # 行为
/// * 清理插件名称,防止路径遍历
/// * 根据操作系统确定动态库扩展名
/// * 构建动态库文件名:`lib{plugin_name}.{ext}`
/// * 如果未配置 `library_path`,自动使用目标三元组作为子目录
/// * 如果配置了 `library_path`,则使用指定的路径
/// * 验证路径安全性,确保在插件临时目录内
/// * 验证文件存在且为文件类型
///
/// # 平台差异
/// * Windows: 扩展名为 `.dll`,目标三元组如 `x86_64-pc-windows-gnu`
/// * macOS: 扩展名为 `.dylib`,目标三元组如 `aarch64-apple-darwin`
/// * Linux GNU: 扩展名为 `.so`,目标三元组如 `x86_64-unknown-linux-gnu`
/// * Linux musl: 扩展名为 `.so`,目标三元组如 `x86_64-unknown-linux-musl`
///
/// # 示例
/// ```no_run
/// use std::path::Path;
///
/// # fn example() {
/// // 自动使用目标三元组作为子目录
/// let library_path = get_library_file_path(
///     Path::new("/opt/secra/plugins/temp/my-plugin"),
///     "my-plugin",
///     None,
/// )?;
/// println!("动态库路径: {}", library_path.display());
///
/// // 使用指定的库路径
/// let library_path = get_library_file_path(
///     Path::new("/opt/secra/plugins/temp/my-plugin"),
///     "my-plugin",
///     Some(&"custom/path".to_string()),
/// )?;
/// println!("动态库路径: {}", library_path.display());
/// # }
/// ```
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 {
        // 清理 library_path,防止路径遍历
        let sanitized_lib_path = sanitize_path_string(lib_path, true);
        debug!("清理后的库路径: {}", sanitized_lib_path);

        // 如果配置了 library_path,则组合:plugin_temp_dir / library_path / library_filename
        plugin_temp_dir
            .join(&sanitized_lib_path)
            .join(&library_filename)
    } else {
        // 如果未配置 library_path,自动使用目标三元组作为子目录
        // 这样可以自动匹配插件包中的平台特定目录结构
        let target_triple = get_target_triple();
        debug!("自动使用目标三元组作为库路径: {}", target_triple);
        
        // 组合路径:plugin_temp_dir / target_triple / library_filename
        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)
}

/// 加载动态库
///
/// 使用 `libloading` 库加载插件的动态库文件。
/// 返回的 `Arc<Library>` 会保持库的加载状态,直到所有引用被释放。
///
/// # 参数
/// * `library_file_path` - 动态库文件的完整路径
///
/// # 返回值
/// * `PluginManagerResult<Arc<Library>>` - 成功时返回包装在 `Arc` 中的库句柄
///
/// # 错误
/// * `PluginManagerError::LoadFailed` - 如果动态库加载失败(文件不存在、格式错误等)
///
/// # 行为
/// * 使用 `unsafe` 块调用 `Library::new` 加载动态库
/// * 将库句柄包装在 `Arc` 中以便共享
/// * 记录加载成功的日志
///
/// # 安全
/// 使用 `unsafe` 是必要的,因为动态库加载涉及系统调用。
/// 库句柄会保存在 `PluginInstance` 中,确保库在插件生命周期内保持加载。
///
/// # 示例
/// ```no_run
/// use std::path::Path;
/// use std::sync::Arc;
///
/// # fn example() {
/// let library = load_dynamic_library(
///     Path::new("/opt/secra/plugins/temp/my-plugin/libmy-plugin.so"),
/// )?;
/// println!("动态库加载成功");
/// # }
/// ```
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)
}

/// 获取插件工厂函数
///
/// 从已加载的动态库中获取 `create_plugin` 导出函数。
/// 这个函数是插件必须导出的标准入口点,用于创建插件工厂实例。
///
/// # 参数
/// * `library` - 已加载的动态库句柄
/// * `plugin_name` - 插件名称,用于日志记录
///
/// # 返回值
/// * `PluginManagerResult<CreatePluginFn>` - 成功时返回插件工厂函数指针
///
/// # 错误
/// * `PluginManagerError::LoadFailed` - 如果动态库中未找到 `create_plugin` 函数
///
/// # 行为
/// * 使用 `unsafe` 块从库中获取 `create_plugin` 符号
/// * 验证函数签名是否正确
/// * 返回函数指针
///
/// # 安全
/// 使用 `unsafe` 是必要的,因为需要从动态库中获取函数指针。
/// 函数签名必须匹配 `CreatePluginFn` 类型定义。
///
/// # 示例
/// ```no_run
/// use std::sync::Arc;
///
/// # fn example() {
/// // 假设 library 是已加载的库
/// let create_plugin_fn = get_plugin_factory_function(&library, "my-plugin")?;
/// let factory = create_plugin_fn();
/// # }
/// ```
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)
}

/// 创建插件实例
///
/// 使用插件工厂函数创建插件实例,并构建完整的 `PluginInstance` 结构。
/// 这是插件加载流程的最后一步,将动态库中的插件代码转换为可用的插件实例。
///
/// # 参数
/// * `create_plugin_fn` - 插件工厂函数指针
/// * `library` - 动态库句柄,用于保持库的加载状态
/// * `artifact` - 已加载的插件包对象,用于获取指纹信息
/// * `library_file_path` - 动态库文件路径
/// * `plugin_name` - 插件名称
/// * `plugin_temp_dir` - 插件临时目录路径
///
/// # 返回值
/// * `PluginManagerResult<PluginInstance>` - 成功时返回完整的插件实例
///
/// # 行为
/// * 调用工厂函数创建插件工厂
/// * 通过工厂创建插件实例
/// * 从插件实例获取元数据
/// * 从 artifact 获取插件指纹(避免重复加载)
/// * 创建 `PluginInstance` 结构,初始状态为 `Loaded`
///
/// # 状态
/// 创建的插件实例状态为 `PluginStatus::Loaded`,需要进一步初始化和启动。
///
/// # 示例
/// ```no_run
/// use std::path::PathBuf;
/// use std::sync::Arc;
///
/// # fn example() {
/// // 假设所有参数都已准备好
/// let plugin_instance = create_plugin_instance(
///     create_plugin_fn,
///     library,
///     &artifact,
///     &library_file_path,
///     "my-plugin",
///     plugin_temp_dir,
/// )?;
/// println!("插件实例创建成功: {}", plugin_instance.metadata.name);
/// # }
/// ```
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
    );

    // 从已加载的 artifact 中获取指纹(避免重复加载)
    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)
}

/// 加载单个插件
///
/// 完整的插件加载流程,包括签名验证、解包、动态库加载和实例创建。
/// 这是插件加载的核心函数,协调所有加载步骤。
///
/// # 参数
/// * `plugin_file` - 插件文件路径(.spk 格式)
/// * `ed25519_public_key_path` - Ed25519 公钥文件路径,用于验证签名
/// * `rsa_private_key_path` - RSA 私钥文件路径,用于解密插件包
/// * `temp_dir` - 插件解包的临时目录路径
/// * `library_path` - 可选的动态库子路径
///
/// # 返回值
/// * `PluginManagerResult<PluginInstance>` - 成功时返回完整的插件实例
///
/// # 错误
/// * `PluginManagerError::ConfigError` - 如果密钥配置错误
/// * `PluginManagerError::SignatureCheckFailed` - 如果签名验证失败
/// * `PluginManagerError::LoadFailed` - 如果加载过程中的任何步骤失败
/// * `PluginManagerError::ValidationFailed` - 如果路径或名称验证失败
/// * `PluginManagerError::Io` - 如果文件操作失败
///
/// # 加载流程
/// 1. 验证密钥配置和文件存在性
/// 2. 验证插件签名和完整性
/// 3. 加载插件包(获取 artifact)
/// 4. 准备插件临时目录
/// 5. 解包插件到临时目录
/// 6. 获取动态库文件路径
/// 7. 加载动态库
/// 8. 获取插件工厂函数
/// 9. 创建插件实例
///
/// # 状态
/// 加载成功后,插件实例状态为 `PluginStatus::Loaded`,需要调用 `initialize` 和 `start` 才能运行。
///
/// # 示例
/// ```no_run
/// # async fn example() {
/// let plugin_instance = load_plugin(
///     "/opt/secra/plugins/my-plugin.spk",
///     Some("/path/to/ed25519.pub".to_string()),
///     Some("/path/to/rsa.key".to_string()),
///     "/opt/secra/plugins/temp",
///     None,
/// ).await?;
/// println!("插件加载成功: {}", plugin_instance.metadata.name);
/// # }
/// ```
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))
        })?;

        // 从 artifact 获取插件名称
        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)?;

        // 第四步:创建插件实例(传递 artifact 避免重复加载)
        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)
    }
}