mirror of https://github.com/jackwener/wx-cli.git
fix(init): --force 时停止 daemon 并清空解密缓存
在成功扫描密钥、drop 到用户身份之后,若使用 --force,先调用与 `wx daemon stop` 相同的 stop_daemon 逻辑,再删除并重建 ~/.wx-cli/cache,避免 mtime 未变时仍复用旧密钥解出的缓存。 将停止 daemon 的逻辑提取到 transport::stop_daemon,供 init 与 daemon stop 子命令复用。 Co-authored-by: Cursor <cursoragent@cursor.com>pull/26/head
parent
6659f48984
commit
a179b50227
|
|
@ -125,6 +125,8 @@ sudo wx init
|
||||||
wx init
|
wx init
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**重新扫描密钥**(`sudo wx init --force` 或 `wx init --force`)时,会在写入新密钥前**停止正在运行的 wx-daemon**,并**清空 `~/.wx-cli/cache` 解密缓存**(含 `_mtimes.json`),避免仅因 mtime 未变而继续复用按旧密钥解出的缓存文件。
|
||||||
|
|
||||||
验证安装:
|
验证安装:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
|
||||||
use crate::config;
|
|
||||||
use crate::cli::DaemonCommands;
|
|
||||||
use crate::cli::transport;
|
use crate::cli::transport;
|
||||||
|
use crate::cli::DaemonCommands;
|
||||||
|
use crate::config;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
pub fn cmd_daemon(cmd: DaemonCommands) -> Result<()> {
|
pub fn cmd_daemon(cmd: DaemonCommands) -> Result<()> {
|
||||||
match cmd {
|
match cmd {
|
||||||
|
|
@ -25,42 +25,13 @@ fn cmd_status() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd_stop() -> Result<()> {
|
fn cmd_stop() -> Result<()> {
|
||||||
let pid_path = config::pid_path();
|
match transport::stop_daemon()? {
|
||||||
if !pid_path.exists() {
|
transport::StopDaemonOutcome::NoPidFile => println!("daemon 未运行"),
|
||||||
println!("daemon 未运行");
|
transport::StopDaemonOutcome::Stopped(pid) => println!("已停止 wx-daemon (PID {})", pid),
|
||||||
return Ok(());
|
transport::StopDaemonOutcome::StalePid(pid) => {
|
||||||
}
|
println!("wx-daemon (PID {}) 已不在运行,清理残留文件", pid);
|
||||||
|
|
||||||
let pid_str = std::fs::read_to_string(&pid_path)?;
|
|
||||||
let pid: u32 = pid_str.trim().parse()
|
|
||||||
.map_err(|_| anyhow::anyhow!("PID 文件格式错误"))?;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
|
|
||||||
if ret != 0 {
|
|
||||||
let errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
|
|
||||||
if errno == libc::ESRCH {
|
|
||||||
println!("wx-daemon (PID {}) 已不在运行,清理残留文件", pid);
|
|
||||||
} else {
|
|
||||||
anyhow::bail!("发送 SIGTERM 失败 (errno {})", errno);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("已停止 wx-daemon (PID {})", pid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
std::process::Command::new("taskkill")
|
|
||||||
.args(["/PID", &pid.to_string(), "/F"])
|
|
||||||
.output()?;
|
|
||||||
println!("已停止 wx-daemon (PID {})", pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = std::fs::remove_file(config::sock_path());
|
|
||||||
let _ = std::fs::remove_file(&pid_path);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,19 +60,25 @@ fn cmd_logs(follow: bool, lines: usize) -> Result<()> {
|
||||||
file.read_to_string(&mut content)?;
|
file.read_to_string(&mut content)?;
|
||||||
let all_lines: Vec<&str> = content.lines().collect();
|
let all_lines: Vec<&str> = content.lines().collect();
|
||||||
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
|
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
|
||||||
for line in show { println!("{}", line); }
|
for line in show {
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
file.read_to_string(&mut buf)?;
|
file.read_to_string(&mut buf)?;
|
||||||
if !buf.is_empty() { print!("{}", buf); }
|
if !buf.is_empty() {
|
||||||
|
print!("{}", buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let content = std::fs::read_to_string(&log_path)?;
|
let content = std::fs::read_to_string(&log_path)?;
|
||||||
let all_lines: Vec<&str> = content.lines().collect();
|
let all_lines: Vec<&str> = content.lines().collect();
|
||||||
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
|
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
|
||||||
for line in show { println!("{}", line); }
|
for line in show {
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::cli::transport::{stop_daemon, StopDaemonOutcome};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::scanner;
|
use crate::scanner;
|
||||||
|
|
||||||
|
|
@ -14,14 +15,20 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
if let Ok(content) = std::fs::read_to_string(&config_path) {
|
if let Ok(content) = std::fs::read_to_string(&config_path) {
|
||||||
if let Ok(cfg) = serde_json::from_str::<serde_json::Value>(&content) {
|
if let Ok(cfg) = serde_json::from_str::<serde_json::Value>(&content) {
|
||||||
let db_dir = cfg.get("db_dir").and_then(|v| v.as_str()).unwrap_or("");
|
let db_dir = cfg.get("db_dir").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let keys_file = cfg.get("keys_file").and_then(|v| v.as_str()).unwrap_or("all_keys.json");
|
let keys_file = cfg
|
||||||
|
.get("keys_file")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("all_keys.json");
|
||||||
let keys_path = if std::path::Path::new(keys_file).is_absolute() {
|
let keys_path = if std::path::Path::new(keys_file).is_absolute() {
|
||||||
std::path::PathBuf::from(keys_file)
|
std::path::PathBuf::from(keys_file)
|
||||||
} else {
|
} else {
|
||||||
config_path.parent().unwrap_or(std::path::Path::new("."))
|
config_path
|
||||||
|
.parent()
|
||||||
|
.unwrap_or(std::path::Path::new("."))
|
||||||
.join(keys_file)
|
.join(keys_file)
|
||||||
};
|
};
|
||||||
if !db_dir.is_empty() && !db_dir.contains("your_wxid")
|
if !db_dir.is_empty()
|
||||||
|
&& !db_dir.contains("your_wxid")
|
||||||
&& std::path::Path::new(db_dir).exists()
|
&& std::path::Path::new(db_dir).exists()
|
||||||
&& keys_path.exists()
|
&& keys_path.exists()
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +57,20 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
drop_privileges_if_sudo()?;
|
drop_privileges_if_sudo()?;
|
||||||
|
|
||||||
|
// --force:先停 daemon 并清空解密缓存,避免旧缓存与新密钥 mtime 一致仍被复用
|
||||||
|
if force {
|
||||||
|
println!("(--force) 停止 wx-daemon 并清空解密缓存…");
|
||||||
|
match stop_daemon()? {
|
||||||
|
StopDaemonOutcome::NoPidFile => {}
|
||||||
|
StopDaemonOutcome::Stopped(pid) => println!("已停止 wx-daemon (PID {})", pid),
|
||||||
|
StopDaemonOutcome::StalePid(pid) => {
|
||||||
|
println!("wx-daemon (PID {}) 已不在运行,已清理残留文件", pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_decrypt_cache()?;
|
||||||
|
println!("已清空 {}", config::cache_dir().display());
|
||||||
|
}
|
||||||
|
|
||||||
// 确保父目录存在(如 ~/.wx-cli/),必须在任何写入之前
|
// 确保父目录存在(如 ~/.wx-cli/),必须在任何写入之前
|
||||||
if let Some(parent) = config_path.parent() {
|
if let Some(parent) = config_path.parent() {
|
||||||
std::fs::create_dir_all(parent)
|
std::fs::create_dir_all(parent)
|
||||||
|
|
@ -57,15 +78,19 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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("."))
|
||||||
.join("all_keys.json");
|
.join("all_keys.json");
|
||||||
|
|
||||||
let mut keys_json = serde_json::Map::new();
|
let mut keys_json = serde_json::Map::new();
|
||||||
for entry in &entries {
|
for entry in &entries {
|
||||||
keys_json.insert(entry.db_name.clone(), json!({
|
keys_json.insert(
|
||||||
"enc_key": entry.enc_key,
|
entry.db_name.clone(),
|
||||||
}));
|
json!({
|
||||||
|
"enc_key": entry.enc_key,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
std::fs::write(&keys_file_path, serde_json::to_string_pretty(&keys_json)?)
|
std::fs::write(&keys_file_path, serde_json::to_string_pretty(&keys_json)?)
|
||||||
.context("写入 all_keys.json 失败")?;
|
.context("写入 all_keys.json 失败")?;
|
||||||
|
|
@ -85,8 +110,10 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.insert("db_dir".into(), json!(db_dir.to_string_lossy()));
|
cfg.insert("db_dir".into(), json!(db_dir.to_string_lossy()));
|
||||||
cfg.entry("keys_file".into()).or_insert_with(|| json!("all_keys.json"));
|
cfg.entry("keys_file".into())
|
||||||
cfg.entry("decrypted_dir".into()).or_insert_with(|| json!("decrypted"));
|
.or_insert_with(|| json!("all_keys.json"));
|
||||||
|
cfg.entry("decrypted_dir".into())
|
||||||
|
.or_insert_with(|| json!("decrypted"));
|
||||||
|
|
||||||
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 失败")?;
|
||||||
|
|
@ -96,6 +123,18 @@ pub fn cmd_init(force: bool) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 删除 `~/.wx-cli/cache`(含 `_mtimes.json` 与已解密 DB),并重建空目录。
|
||||||
|
fn clear_decrypt_cache() -> Result<()> {
|
||||||
|
let dir = config::cache_dir();
|
||||||
|
if dir.exists() {
|
||||||
|
std::fs::remove_dir_all(&dir)
|
||||||
|
.with_context(|| format!("删除解密缓存目录失败: {}", dir.display()))?;
|
||||||
|
}
|
||||||
|
std::fs::create_dir_all(&dir)
|
||||||
|
.with_context(|| format!("创建解密缓存目录失败: {}", dir.display()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// 如果当前以 root 身份运行且是通过 sudo 启动的,drop 到调用用户身份,
|
/// 如果当前以 root 身份运行且是通过 sudo 启动的,drop 到调用用户身份,
|
||||||
/// 并迁移旧版本遗留的 root 属主 `~/.wx-cli/`。
|
/// 并迁移旧版本遗留的 root 属主 `~/.wx-cli/`。
|
||||||
///
|
///
|
||||||
|
|
@ -129,7 +168,9 @@ fn drop_privileges_if_sudo() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 umask,让后续 create 出来的文件/目录默认是 0600 / 0700。
|
// 设置 umask,让后续 create 出来的文件/目录默认是 0600 / 0700。
|
||||||
unsafe { libc::umask(0o077); }
|
unsafe {
|
||||||
|
libc::umask(0o077);
|
||||||
|
}
|
||||||
|
|
||||||
// 必须先 setgid 再 setuid:一旦 uid 降下来就没法再改 gid 了。
|
// 必须先 setgid 再 setuid:一旦 uid 降下来就没法再改 gid 了。
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -153,8 +194,9 @@ fn drop_privileges_if_sudo() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn chown_one(path: &Path, uid: u32, gid: u32) -> std::io::Result<()> {
|
fn chown_one(path: &Path, uid: u32, gid: u32) -> std::io::Result<()> {
|
||||||
let c = CString::new(path.as_os_str().as_bytes())
|
let c = CString::new(path.as_os_str().as_bytes()).map_err(|_| {
|
||||||
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "path contains NUL"))?;
|
std::io::Error::new(std::io::ErrorKind::InvalidInput, "path contains NUL")
|
||||||
|
})?;
|
||||||
if unsafe { libc::chown(c.as_ptr(), uid, gid) } != 0 {
|
if unsafe { libc::chown(c.as_ptr(), uid, gid) } != 0 {
|
||||||
return Err(std::io::Error::last_os_error());
|
return Err(std::io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,68 @@ pub fn is_alive() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`stop_daemon`] 的返回值,供 CLI 提示或 `wx init --force` 选用。
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum StopDaemonOutcome {
|
||||||
|
/// 无 `daemon.pid`
|
||||||
|
NoPidFile,
|
||||||
|
/// 已向进程发送停止信号并清理 pid/socket
|
||||||
|
Stopped(u32),
|
||||||
|
/// 进程已不存在,仅清理 pid/socket
|
||||||
|
StalePid(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 停止 wx-daemon(与 `wx daemon stop` 同一套逻辑)。
|
||||||
|
pub fn stop_daemon() -> Result<StopDaemonOutcome> {
|
||||||
|
let pid_path = config::pid_path();
|
||||||
|
if !pid_path.exists() {
|
||||||
|
return Ok(StopDaemonOutcome::NoPidFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pid_str = std::fs::read_to_string(&pid_path)?;
|
||||||
|
let pid: u32 = pid_str
|
||||||
|
.trim()
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| anyhow::anyhow!("PID 文件格式错误"))?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
|
||||||
|
if ret != 0 {
|
||||||
|
let errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
|
||||||
|
if errno == libc::ESRCH {
|
||||||
|
let _ = std::fs::remove_file(config::sock_path());
|
||||||
|
let _ = std::fs::remove_file(&pid_path);
|
||||||
|
return Ok(StopDaemonOutcome::StalePid(pid));
|
||||||
|
}
|
||||||
|
anyhow::bail!("发送 SIGTERM 失败 (errno {})", errno);
|
||||||
|
}
|
||||||
|
let _ = std::fs::remove_file(config::sock_path());
|
||||||
|
let _ = std::fs::remove_file(&pid_path);
|
||||||
|
return Ok(StopDaemonOutcome::Stopped(pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let out = std::process::Command::new("taskkill")
|
||||||
|
.args(["/PID", &pid.to_string(), "/F"])
|
||||||
|
.output();
|
||||||
|
let ok = matches!(&out, Ok(o) if o.status.success());
|
||||||
|
let _ = std::fs::remove_file(config::sock_path());
|
||||||
|
let _ = std::fs::remove_file(&pid_path);
|
||||||
|
return Ok(if ok {
|
||||||
|
StopDaemonOutcome::Stopped(pid)
|
||||||
|
} else {
|
||||||
|
StopDaemonOutcome::StalePid(pid)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(unix, windows)))]
|
||||||
|
{
|
||||||
|
anyhow::bail!("当前平台不支持 stop_daemon");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 确保 daemon 运行,必要时自动启动
|
/// 确保 daemon 运行,必要时自动启动
|
||||||
pub fn ensure_daemon() -> Result<()> {
|
pub fn ensure_daemon() -> Result<()> {
|
||||||
if is_alive() {
|
if is_alive() {
|
||||||
|
|
@ -113,7 +175,8 @@ fn start_daemon() -> Result<()> {
|
||||||
let _ = std::fs::create_dir_all(parent);
|
let _ = std::fs::create_dir_all(parent);
|
||||||
}
|
}
|
||||||
let (stdout_stdio, stderr_stdio) = std::fs::OpenOptions::new()
|
let (stdout_stdio, stderr_stdio) = std::fs::OpenOptions::new()
|
||||||
.create(true).append(true)
|
.create(true)
|
||||||
|
.append(true)
|
||||||
.open(&log_path)
|
.open(&log_path)
|
||||||
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
||||||
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
||||||
|
|
@ -124,7 +187,12 @@ fn start_daemon() -> Result<()> {
|
||||||
.stdout(stdout_stdio)
|
.stdout(stdout_stdio)
|
||||||
.stderr(stderr_stdio);
|
.stderr(stderr_stdio);
|
||||||
// SAFETY: setsid() 在 fork 后的子进程中调用,使 daemon 脱离控制终端
|
// SAFETY: setsid() 在 fork 后的子进程中调用,使 daemon 脱离控制终端
|
||||||
unsafe { cmd.pre_exec(|| { libc::setsid(); Ok(()) }); }
|
unsafe {
|
||||||
|
cmd.pre_exec(|| {
|
||||||
|
libc::setsid();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
let _ = cmd.spawn().context("无法启动 daemon 进程")?;
|
let _ = cmd.spawn().context("无法启动 daemon 进程")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +204,8 @@ fn start_daemon() -> Result<()> {
|
||||||
let _ = std::fs::create_dir_all(parent);
|
let _ = std::fs::create_dir_all(parent);
|
||||||
}
|
}
|
||||||
let (stdout_stdio, stderr_stdio) = std::fs::OpenOptions::new()
|
let (stdout_stdio, stderr_stdio) = std::fs::OpenOptions::new()
|
||||||
.create(true).append(true)
|
.create(true)
|
||||||
|
.append(true)
|
||||||
.open(&log_path)
|
.open(&log_path)
|
||||||
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
||||||
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
||||||
|
|
@ -189,10 +258,11 @@ pub fn send(req: Request) -> Result<Response> {
|
||||||
fn send_unix(req: Request) -> Result<Response> {
|
fn send_unix(req: Request) -> Result<Response> {
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
let sock_path = config::sock_path();
|
let sock_path = config::sock_path();
|
||||||
let mut stream = UnixStream::connect(&sock_path)
|
let mut stream = UnixStream::connect(&sock_path).context("连接 daemon socket 失败")?;
|
||||||
.context("连接 daemon socket 失败")?;
|
|
||||||
stream.set_read_timeout(Some(Duration::from_secs(120))).ok();
|
stream.set_read_timeout(Some(Duration::from_secs(120))).ok();
|
||||||
stream.set_write_timeout(Some(Duration::from_secs(120))).ok();
|
stream
|
||||||
|
.set_write_timeout(Some(Duration::from_secs(120)))
|
||||||
|
.ok();
|
||||||
|
|
||||||
let req_str = serde_json::to_string(&req)? + "\n";
|
let req_str = serde_json::to_string(&req)? + "\n";
|
||||||
stream.write_all(req_str.as_bytes())?;
|
stream.write_all(req_str.as_bytes())?;
|
||||||
|
|
@ -201,8 +271,7 @@ fn send_unix(req: Request) -> Result<Response> {
|
||||||
let mut reader = BufReader::new(&stream);
|
let mut reader = BufReader::new(&stream);
|
||||||
reader.read_line(&mut line)?;
|
reader.read_line(&mut line)?;
|
||||||
|
|
||||||
let resp: Response = serde_json::from_str(&line)
|
let resp: Response = serde_json::from_str(&line).context("解析 daemon 响应失败")?;
|
||||||
.context("解析 daemon 响应失败")?;
|
|
||||||
|
|
||||||
if !resp.ok {
|
if !resp.ok {
|
||||||
bail!("{}", resp.error.as_deref().unwrap_or("未知错误"));
|
bail!("{}", resp.error.as_deref().unwrap_or("未知错误"));
|
||||||
|
|
@ -215,10 +284,10 @@ fn send_unix(req: Request) -> Result<Response> {
|
||||||
fn send_windows(req: Request) -> Result<Response> {
|
fn send_windows(req: Request) -> Result<Response> {
|
||||||
use interprocess::local_socket::{prelude::*, GenericNamespaced, Stream};
|
use interprocess::local_socket::{prelude::*, GenericNamespaced, Stream};
|
||||||
|
|
||||||
let name = "wx-cli-daemon".to_ns_name::<GenericNamespaced>()
|
let name = "wx-cli-daemon"
|
||||||
|
.to_ns_name::<GenericNamespaced>()
|
||||||
.context("构造 pipe name 失败")?;
|
.context("构造 pipe name 失败")?;
|
||||||
let stream = Stream::connect(name)
|
let stream = Stream::connect(name).context("连接 daemon named pipe 失败")?;
|
||||||
.context("连接 daemon named pipe 失败")?;
|
|
||||||
|
|
||||||
// interprocess::Stream 同时实现 Read + Write,但需要拆分读写端
|
// interprocess::Stream 同时实现 Read + Write,但需要拆分读写端
|
||||||
let mut reader = BufReader::new(stream);
|
let mut reader = BufReader::new(stream);
|
||||||
|
|
@ -229,8 +298,7 @@ fn send_windows(req: Request) -> Result<Response> {
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
reader.read_line(&mut line)?;
|
reader.read_line(&mut line)?;
|
||||||
|
|
||||||
let resp: Response = serde_json::from_str(&line)
|
let resp: Response = serde_json::from_str(&line).context("解析 daemon 响应失败")?;
|
||||||
.context("解析 daemon 响应失败")?;
|
|
||||||
|
|
||||||
if !resp.ok {
|
if !resp.ok {
|
||||||
bail!("{}", resp.error.as_deref().unwrap_or("未知错误"));
|
bail!("{}", resp.error.as_deref().unwrap_or("未知错误"));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue