mirror of https://github.com/jackwener/wx-cli.git
fix: Windows init and daemon startup (issue #5)
Three related bugs caused "wx init" and daemon startup to fail on Windows: 1. init.rs: create ~/.wx-cli/ before writing all_keys.json (was created only before config.json, so first write failed with ENOENT) 2. transport.rs (Windows): daemon.log was always empty because stderr was never redirected, and log file open silently fell back to null when parent dir didn't exist. Now mirror the Unix version: create parent dir, try_clone to redirect both stdout and stderr. 3. server.rs (Windows): interprocess GenericNamespaced auto-prepends \\.\pipe\ on Windows. Passing the full path caused a double-prefixed pipe name that clients (using raw \\.\pipe\wx-cli-daemon) could never connect to, leading to the 15s startup timeout.pull/9/head
parent
2c9df70d44
commit
18daf5b22e
|
|
@ -43,6 +43,12 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
println!("扫描加密密钥(需要 root 权限)...");
|
println!("扫描加密密钥(需要 root 权限)...");
|
||||||
let entries = scanner::scan_keys(&db_dir)?;
|
let entries = scanner::scan_keys(&db_dir)?;
|
||||||
|
|
||||||
|
// 确保父目录存在(如 ~/.wx-cli/),必须在任何写入之前
|
||||||
|
if let Some(parent) = config_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent)
|
||||||
|
.with_context(|| format!("创建目录失败: {}", parent.display()))?;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 3: 保存 all_keys.json
|
// Step 3: 保存 all_keys.json
|
||||||
let keys_file_path = config_path.parent()
|
let keys_file_path = config_path.parent()
|
||||||
.unwrap_or(std::path::Path::new("."))
|
.unwrap_or(std::path::Path::new("."))
|
||||||
|
|
@ -75,11 +81,6 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
cfg.entry("keys_file".into()).or_insert_with(|| json!("all_keys.json"));
|
cfg.entry("keys_file".into()).or_insert_with(|| json!("all_keys.json"));
|
||||||
cfg.entry("decrypted_dir".into()).or_insert_with(|| json!("decrypted"));
|
cfg.entry("decrypted_dir".into()).or_insert_with(|| json!("decrypted"));
|
||||||
|
|
||||||
// 确保父目录存在(如 ~/.wx-cli/)
|
|
||||||
if let Some(parent) = config_path.parent() {
|
|
||||||
std::fs::create_dir_all(parent)
|
|
||||||
.with_context(|| format!("创建目录失败: {}", parent.display()))?;
|
|
||||||
}
|
|
||||||
std::fs::write(&config_path, serde_json::to_string_pretty(&cfg)?)
|
std::fs::write(&config_path, serde_json::to_string_pretty(&cfg)?)
|
||||||
.context("写入 config.json 失败")?;
|
.context("写入 config.json 失败")?;
|
||||||
println!("配置已保存: {}", config_path.display());
|
println!("配置已保存: {}", config_path.display());
|
||||||
|
|
|
||||||
|
|
@ -92,15 +92,21 @@ fn start_daemon() -> Result<()> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
let log_file = std::fs::OpenOptions::new()
|
let log_path = config::log_path();
|
||||||
|
if let Some(parent) = log_path.parent() {
|
||||||
|
let _ = std::fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
let (stdout_stdio, stderr_stdio) = std::fs::OpenOptions::new()
|
||||||
.create(true).append(true)
|
.create(true).append(true)
|
||||||
.open(config::log_path())
|
.open(&log_path)
|
||||||
.ok()
|
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
||||||
.map(std::process::Stdio::from)
|
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
||||||
.unwrap_or_else(std::process::Stdio::null);
|
.unwrap_or_else(|_| (std::process::Stdio::null(), std::process::Stdio::null()));
|
||||||
let _ = std::process::Command::new(&exe)
|
let _ = std::process::Command::new(&exe)
|
||||||
.env("WX_DAEMON_MODE", "1")
|
.env("WX_DAEMON_MODE", "1")
|
||||||
.stdout(log_file)
|
.stdin(std::process::Stdio::null())
|
||||||
|
.stdout(stdout_stdio)
|
||||||
|
.stderr(stderr_stdio)
|
||||||
.creation_flags(0x00000008) // DETACHED_PROCESS
|
.creation_flags(0x00000008) // DETACHED_PROCESS
|
||||||
.spawn()
|
.spawn()
|
||||||
.context("无法启动 daemon 进程")?;
|
.context("无法启动 daemon 进程")?;
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,13 @@ async fn serve_windows(
|
||||||
tokio::prelude::*, GenericNamespaced, ListenerOptions,
|
tokio::prelude::*, GenericNamespaced, ListenerOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipe_name = r"\\.\pipe\wx-cli-daemon";
|
// interprocess 的 GenericNamespaced 在 Windows 上会自动拼接 `\\.\pipe\` 前缀,
|
||||||
let name = pipe_name.to_ns_name::<GenericNamespaced>()?;
|
// 这里必须传相对名;client 端用 `\\.\pipe\wx-cli-daemon` 直接打开可以对上
|
||||||
|
let name = "wx-cli-daemon".to_ns_name::<GenericNamespaced>()?;
|
||||||
let opts = ListenerOptions::new().name(name);
|
let opts = ListenerOptions::new().name(name);
|
||||||
let listener = opts.create_tokio()?;
|
let listener = opts.create_tokio()?;
|
||||||
|
|
||||||
eprintln!("[server] 监听 {}", pipe_name);
|
eprintln!("[server] 监听 \\\\.\\pipe\\wx-cli-daemon");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let conn = listener.accept().await?;
|
let conn = listener.accept().await?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue