mirror of https://github.com/jackwener/wx-cli.git
parent
7d2a54c416
commit
107af74a72
|
|
@ -114,6 +114,8 @@ pub fn ensure_daemon(tcp_addr: Option<&str>) -> Result<()> {
|
|||
}
|
||||
|
||||
/// 启动 daemon 进程(自身二进制,设置 WX_DAEMON_MODE=1)
|
||||
///
|
||||
/// tracing 已在子进程 main() 中直接写入 daemon.log,无需重定向 stdout/stderr。
|
||||
fn start_daemon() -> Result<()> {
|
||||
let exe = std::env::current_exe().context("无法获取当前可执行文件路径")?;
|
||||
|
||||
|
|
@ -124,23 +126,11 @@ fn start_daemon() -> Result<()> {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::CommandExt;
|
||||
// 日志文件:~/.wx-cli/daemon.log
|
||||
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)
|
||||
.open(&log_path)
|
||||
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
||||
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
||||
.unwrap_or_else(|_| (std::process::Stdio::null(), std::process::Stdio::null()));
|
||||
let mut cmd = std::process::Command::new(&exe);
|
||||
cmd.env("WX_DAEMON_MODE", "1")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(stdout_stdio)
|
||||
.stderr(stderr_stdio);
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null());
|
||||
// SAFETY: setsid() 在 fork 后的子进程中调用,使 daemon 脱离控制终端
|
||||
unsafe { cmd.pre_exec(|| { libc::setsid(); Ok(()) }); }
|
||||
let _ = cmd.spawn().context("无法启动 daemon 进程")?;
|
||||
|
|
@ -149,50 +139,16 @@ fn start_daemon() -> Result<()> {
|
|||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
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)
|
||||
.open(&log_path)
|
||||
.and_then(|f| f.try_clone().map(|g| (f, g)))
|
||||
.map(|(f, g)| (std::process::Stdio::from(f), std::process::Stdio::from(g)))
|
||||
.unwrap_or_else(|_| (std::process::Stdio::null(), std::process::Stdio::null()));
|
||||
let _ = std::process::Command::new(&exe)
|
||||
.env("WX_DAEMON_MODE", "1")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(stdout_stdio)
|
||||
.stderr(stderr_stdio)
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.creation_flags(0x00000008) // DETACHED_PROCESS
|
||||
.spawn()
|
||||
.context("无法启动 daemon 进程")?;
|
||||
}
|
||||
|
||||
// 等待 daemon 就绪(最多 STARTUP_TIMEOUT_SECS 秒)
|
||||
let deadline = std::time::Instant::now() + Duration::from_secs(STARTUP_TIMEOUT_SECS);
|
||||
while std::time::Instant::now() < deadline {
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
if is_alive(None) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
bail!(
|
||||
"wx-daemon 启动超时(>{}s)\n请查看日志: {}",
|
||||
STARTUP_TIMEOUT_SECS,
|
||||
config::log_path().display()
|
||||
)
|
||||
}
|
||||
|
||||
/// 启动 daemon 前检查 `~/.wx-cli/` 可写,给出比"超时"更明确的错误。
|
||||
///
|
||||
/// 典型坑:旧版本 `sudo wx init` 把目录留成 root 属主,非 root 的 daemon
|
||||
/// 连 socket/log 都建不了,会静默失败 15s 超时。
|
||||
fn preflight_cli_dir_writable() -> Result<()> {
|
||||
let cli_dir = config::cli_dir();
|
||||
std::fs::create_dir_all(&cli_dir)
|
||||
.with_context(|| format!("创建 {} 失败", cli_dir.display()))?;
|
||||
|
||||
let probe = cli_dir.join(".daemon_probe");
|
||||
match std::fs::File::create(&probe) {
|
||||
|
|
|
|||
|
|
@ -23,21 +23,15 @@ pub fn run() {
|
|||
/// 从 CLI `wx daemon start [--tcp ADDR]` 调用
|
||||
///
|
||||
/// 查找当前可执行文件路径,设置 WX_DAEMON_MODE=1,后台启动新进程。
|
||||
/// tracing 已在子进程 main() 中直接写入 daemon.log,无需重定向 stdout/stderr。
|
||||
pub fn run_start(tcp_addr: Option<String>) -> Result<()> {
|
||||
let exe = std::env::current_exe()?;
|
||||
let log = config::log_path();
|
||||
|
||||
let mut cmd = std::process::Command::new(&exe);
|
||||
cmd.env("WX_DAEMON_MODE", "1");
|
||||
if let Some(addr) = &tcp_addr {
|
||||
cmd.env("WX_DAEMON_TCP_ADDR", addr);
|
||||
}
|
||||
// 日志重定向
|
||||
let log_file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&log)?;
|
||||
cmd.stdout(log_file.try_clone()?).stderr(log_file);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
|
|
@ -88,7 +82,7 @@ pub async fn start_daemon(tcp_addr: Option<String>) -> Result<()> {
|
|||
// 收集消息 DB 列表
|
||||
let msg_db_keys: Vec<String> = all_keys.keys()
|
||||
.filter(|k| {
|
||||
let k = k.replace('\\', "/");
|
||||
let k = k.replace('\', "/");
|
||||
k.contains("message/message_") && k.ends_with(".db")
|
||||
&& !k.contains("_fts") && !k.contains("_resource")
|
||||
})
|
||||
|
|
@ -154,7 +148,7 @@ fn extract_keys(json: &serde_json::Value) -> HashMap<String, String> {
|
|||
};
|
||||
if !enc_key.is_empty() {
|
||||
// 统一路径分隔符
|
||||
let rel = k.replace('\\', "/");
|
||||
let rel = k.replace('\', "/");
|
||||
result.insert(rel, enc_key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
40
src/main.rs
40
src/main.rs
|
|
@ -7,8 +7,8 @@ mod cli;
|
|||
pub mod transport;
|
||||
|
||||
fn main() {
|
||||
init_logging();
|
||||
if std::env::var("WX_DAEMON_MODE").is_ok() {
|
||||
init_logging();
|
||||
daemon::run();
|
||||
} else {
|
||||
cli::run();
|
||||
|
|
@ -17,14 +17,38 @@ fn main() {
|
|||
|
||||
fn init_logging() {
|
||||
use tracing_subscriber::EnvFilter;
|
||||
// 默认只输出 WARN+ 到 stderr(不污染用户可见的 stdout)。
|
||||
// 通过 `RUST_LOG=info` 或 `RUST_LOG=debug` 开启详细日志。
|
||||
|
||||
// CLI 路径不需要 tracing — 只输出用户可见的 stdout/stderr。
|
||||
// daemon 路径:tracing 直接写入 ~/.wx-cli/daemon.log,
|
||||
// 不依赖父进程的 stderr 重定向(避免重复写入)。
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
EnvFilter::new("info")
|
||||
});
|
||||
tracing_subscriber::fmt()
|
||||
.with_target(false)
|
||||
.with_level(true)
|
||||
.with_env_filter(env_filter)
|
||||
.init();
|
||||
|
||||
let _ = std::fs::create_dir_all(config::cli_dir());
|
||||
let log_file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(config::log_path())
|
||||
.ok();
|
||||
|
||||
match log_file {
|
||||
Some(file) => {
|
||||
tracing_subscriber::fmt()
|
||||
.with_target(false)
|
||||
.with_level(true)
|
||||
.with_env_filter(env_filter)
|
||||
.with_writer(file)
|
||||
.init();
|
||||
}
|
||||
None => {
|
||||
// 文件打开失败时退回到 stderr,确保日志不会静默丢失
|
||||
tracing_subscriber::fmt()
|
||||
.with_target(false)
|
||||
.with_level(true)
|
||||
.with_env_filter(env_filter)
|
||||
.with_writer(std::io::stderr)
|
||||
.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue