Skip to main content

futu_opend/
main.rs

1// v1.4.110 P1-2: 拆 1267-LoC `async fn main()` body 到 startup/ + hints.rs.
2// 类型 (cli) + 配置 (config) + 凭据 (credentials) + crash log (crash_log) 已 pre-stage 抽出.
3
4use anyhow::Result;
5use clap::Parser;
6
7mod cli;
8mod config;
9mod crash_log;
10mod credentials;
11mod hints;
12mod startup;
13
14use cli::Args;
15use config::merge_config;
16use crash_log::{warn_if_previous_crash, write_crash_log_file};
17
18fn main() -> Result<()> {
19    // v1.4.109 broker-auth WebTCP: rustls 0.23 needs an explicit global
20    // CryptoProvider before the first direct TLS config is built.
21    futu_backend::auth::install_default_rustls_crypto_provider();
22
23    // v1.4.41 (external reviewer v1.4.40 报告 P3.1 修): 装全局 panic hook,让 daemon
24    // silent crash 时在 log 里留 "PANIC: ..." + 位置 + backtrace。
25    //
26    // 之前 v1.4.39 external reviewer 测期权 place-order 时 daemon 一次性 silent crash
27    // (log 末尾无 panic / SIGABRT / shutdown trace),无法定位。
28    //
29    // 做法:
30    // 1. 尽量早装(main 第一行)—— 在 tracing init 之前装的 hook 只能
31    //    eprintln(还没 subscriber),但至少 stderr 有记录
32    // 2. tracing init 之后**重新装一次**(覆盖)—— 走 tracing::error! 进
33    //    audit log + JSON log,便于 grep
34    std::panic::set_hook(Box::new(|info| {
35        let location = info
36            .location()
37            .map(|l| format!("{}:{}", l.file(), l.line()))
38            .unwrap_or_else(|| "<unknown>".to_string());
39        let payload = info
40            .payload()
41            .downcast_ref::<&str>()
42            .copied()
43            .or_else(|| info.payload().downcast_ref::<String>().map(|s| s.as_str()))
44            .unwrap_or("<non-string panic payload>");
45        // 双 write:stderr + process exit。即使 tracing subscriber 还没起来,
46        // 用户都能在 stderr 看到。
47        eprintln!("╔═══ PANIC at {location} ═══");
48        eprintln!("║ {payload}");
49        eprintln!("║ thread={:?}", std::thread::current().name());
50        eprintln!("╚═══ process will abort ═══");
51        // v1.4.97 P1-D-D: also write dated crash log to disk for forensics.
52        write_crash_log_file(info);
53    }));
54
55    let args = Args::parse();
56
57    // v1.4.97 P1-D-D: warn if previous run crashed (scans
58    // ~/.futu-opend-rs/crashes/crash-*.log). Keep this after clap parsing so
59    // `--help`, `--version`, and parse errors remain clean fast paths.
60    warn_if_previous_crash();
61
62    // codex 0547 F6 (P3): dev-only feature flag 在 merge_config consume args
63    // 前 capture (这是 CLI-only 字段, 不进 RuntimeConfig).
64    #[cfg(feature = "dev-flags")]
65    let inject_auth_failure_every = args.inject_auth_failure_every;
66    // v1.4.110 P1-2: 没启用 dev-flags feature 时也要绑 None, 让 run_daemon
67    // signature 统一 (`Option<u64>`).
68    #[cfg(not(feature = "dev-flags"))]
69    let inject_auth_failure_every: Option<u64> = None;
70
71    // codex 0547 F6 (P3) fix: merge_config 提前到 args 仍可用阶段, 让 TZ /
72    // 安全 keys / audit_log 走 RuntimeConfig 统一来源 (CLI 优先 + TOML
73    // fallback). 之前 args.rest_keys_file / args.tz 直接 read, TOML 配置
74    // 无法影响这些字段 — 与 `--config` help 文 "字段与 CLI 一致" 不符.
75    let config = merge_config(args)?;
76    startup::apply_pre_runtime_tz(&config);
77
78    let runtime = tokio::runtime::Builder::new_multi_thread()
79        .enable_all()
80        .build()?;
81    runtime.block_on(startup::run_daemon(config, inject_auth_failure_every))
82}
83
84#[cfg(test)]
85mod tests;