wx-cli/src/cli/daemon_cmd.rs

124 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

use anyhow::Result;
use crate::config;
use crate::cli::DaemonCommands;
use crate::cli::transport;
pub fn cmd_daemon(cmd: DaemonCommands, tcp_addr: Option<&str>) -> Result<()> {
match cmd {
DaemonCommands::Status => cmd_status(tcp_addr),
DaemonCommands::Stop => cmd_stop(tcp_addr),
DaemonCommands::Logs { follow, lines } => cmd_logs(follow, lines),
DaemonCommands::Start { tcp } => crate::daemon::run_start(tcp),
}
}
fn cmd_status(tcp_addr: Option<&str>) -> Result<()> {
if transport::is_alive(tcp_addr) {
let pid_path = config::pid_path();
let pid = std::fs::read_to_string(&pid_path)
.map(|s| s.trim().to_string())
.unwrap_or_else(|_| "?".into());
if let Some(addr) = tcp_addr {
println!("wx-daemon 运行中 (TCP {})", addr);
} else {
println!("wx-daemon 运行中 (PID {})", pid);
}
} else {
println!("wx-daemon 未运行");
}
Ok(())
}
fn cmd_stop(tcp_addr: Option<&str>) -> Result<()> {
// TCP daemon is a separate process — cannot stop via PID file
if let Some(addr) = tcp_addr {
eprintln!(
"⚠ TCP daemon ({}) 是一个独立进程,无法通过 `wx daemon stop` 停止。\n\
请手动关闭该进程(例如 kill / taskkill PID",
addr
);
return Ok(());
}
let pid_path = config::pid_path();
if !pid_path.exists() {
println!("daemon 未运行");
return Ok(());
}
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(())
}
fn cmd_logs(follow: bool, lines: usize) -> Result<()> {
let log_path = config::log_path();
if !log_path.exists() {
println!("暂无日志");
return Ok(());
}
if follow {
#[cfg(unix)]
{
std::process::Command::new("tail")
.args([&format!("-{}", lines), "-f", &log_path.to_string_lossy()])
.status()?;
}
#[cfg(windows)]
{
use std::io::{Read, Seek, SeekFrom};
let mut file = std::fs::File::open(&log_path)?;
let len = file.seek(SeekFrom::End(0))?;
let start = len.saturating_sub((lines as u64) * 200);
file.seek(SeekFrom::Start(start))?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let all_lines: Vec<&str> = content.lines().collect();
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
for line in show { println!("{}", line); }
loop {
std::thread::sleep(std::time::Duration::from_millis(500));
let mut buf = String::new();
file.read_to_string(&mut buf)?;
if !buf.is_empty() { print!("{}", buf); }
}
}
} else {
let content = std::fs::read_to_string(&log_path)?;
let all_lines: Vec<&str> = content.lines().collect();
let show = &all_lines[all_lines.len().saturating_sub(lines)..];
for line in show { println!("{}", line); }
}
Ok(())
}