Add func: log(with some bug)
This commit is contained in:
parent
9297451aa2
commit
5f9314052e
13
Cargo.toml
13
Cargo.toml
@ -7,11 +7,12 @@ description = "A process manager in Rust"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.0", features = ["derive"] }
|
clap = { version = "4.4.11", features = ["derive"] }
|
||||||
sysinfo = "0.33.1"
|
sysinfo = "0.29.11"
|
||||||
tabled = "0.17.0"
|
tabled = "0.14.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.193", features = ["derive"] }
|
||||||
dirs = "5.0"
|
dirs = "5.0.1"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.108"
|
||||||
crossterm = "0.28.1"
|
crossterm = "0.28.1"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
|
ctrlc = "3.4.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::config::dump::DumpConfig;
|
use crate::config::dump::DumpConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sysinfo::{System, Users};
|
use sysinfo::{ProcessExt, System, SystemExt, UserExt};
|
||||||
use tabled::{Table, Tabled};
|
use tabled::{Table, Tabled};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -83,8 +83,7 @@ pub fn read_pmr_processes() -> Vec<PmrProcess> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_processes(system: bool) {
|
pub fn list_processes(system: bool) {
|
||||||
let mut sys = System::new_all();
|
let mut sys = System::new();
|
||||||
let users = Users::new_with_refreshed_list();
|
|
||||||
sys.refresh_all();
|
sys.refresh_all();
|
||||||
|
|
||||||
if system {
|
if system {
|
||||||
@ -93,7 +92,7 @@ pub fn list_processes(system: bool) {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(&pid, process)| ProcessInfo {
|
.map(|(&pid, process)| ProcessInfo {
|
||||||
id: "0".to_string(),
|
id: "0".to_string(),
|
||||||
name: process.name().to_string_lossy().to_string(),
|
name: process.name().to_string(),
|
||||||
namespace: "default".to_string(),
|
namespace: "default".to_string(),
|
||||||
version: "N/A".to_string(),
|
version: "N/A".to_string(),
|
||||||
pid: pid.to_string(),
|
pid: pid.to_string(),
|
||||||
@ -104,7 +103,7 @@ pub fn list_processes(system: bool) {
|
|||||||
mem: format!("{:.1} MB", process.memory() as f64 / 1024.0 / 1024.0),
|
mem: format!("{:.1} MB", process.memory() as f64 / 1024.0 / 1024.0),
|
||||||
user: process
|
user: process
|
||||||
.user_id()
|
.user_id()
|
||||||
.and_then(|uid| users.get_user_by_id(uid))
|
.and_then(|uid| sys.get_user_by_id(uid))
|
||||||
.map_or("N/A".to_string(), |u| u.name().to_string()),
|
.map_or("N/A".to_string(), |u| u.name().to_string()),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -133,7 +132,7 @@ pub fn list_processes(system: bool) {
|
|||||||
mem: format!("{:.1} MB", sys_proc.memory() as f64 / 1024.0 / 1024.0),
|
mem: format!("{:.1} MB", sys_proc.memory() as f64 / 1024.0 / 1024.0),
|
||||||
user: sys_proc
|
user: sys_proc
|
||||||
.user_id()
|
.user_id()
|
||||||
.and_then(|uid| users.get_user_by_id(uid))
|
.and_then(|uid| sys.get_user_by_id(uid))
|
||||||
.map_or("N/A".to_string(), |u| u.name().to_string()),
|
.map_or("N/A".to_string(), |u| u.name().to_string()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
89
src/commands/log.rs
Normal file
89
src/commands/log.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use super::super::config::dump::DumpConfig;
|
||||||
|
use super::super::config::log_path;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, BufRead, BufReader, Seek, SeekFrom};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub fn tail_log(target: String) -> io::Result<()> {
|
||||||
|
let dump_config = DumpConfig::get_instance();
|
||||||
|
|
||||||
|
// 解析目标ID
|
||||||
|
let pmr_id = match target.parse::<u32>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(_) => {
|
||||||
|
// 如果不是数字,尝试按名称查找
|
||||||
|
match dump_config.list_processes() {
|
||||||
|
Ok(processes) => {
|
||||||
|
if let Some(process) = processes.iter().find(|p| p.name == target) {
|
||||||
|
process.pmr_id
|
||||||
|
} else {
|
||||||
|
eprintln!("找不到进程: {}", target);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("无法获取进程列表: {}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取日志文件路径
|
||||||
|
let log_path = match log_path::get_log_path(pmr_id) {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("无法获取日志文件路径: {}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查日志文件是否存在
|
||||||
|
if !log_path.exists() {
|
||||||
|
eprintln!("日志文件不存在: {:?}", log_path);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("正在查看日志文件: {:?}", log_path);
|
||||||
|
println!("按 Ctrl+C 退出日志查看...");
|
||||||
|
|
||||||
|
// 打开日志文件
|
||||||
|
let mut file = match File::open(&log_path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("无法打开日志文件: {}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移动到文件末尾
|
||||||
|
if let Err(e) = file.seek(SeekFrom::End(0)) {
|
||||||
|
eprintln!("无法定位到文件末尾: {}", e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
|
// 持续读取新的日志内容
|
||||||
|
loop {
|
||||||
|
match reader.read_line(&mut buffer) {
|
||||||
|
Ok(0) => {
|
||||||
|
// 没有新的内容,等待一下
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
// 打印新的内容
|
||||||
|
print!("{}", buffer);
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("读取日志时出错: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
|
pub mod log;
|
||||||
pub mod restart;
|
pub mod restart;
|
||||||
pub mod start;
|
pub mod start;
|
||||||
pub mod stop;
|
pub mod stop;
|
||||||
|
|
||||||
|
pub use log::tail_log;
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
use super::super::base::process::PmrProcessInfo;
|
use super::super::base::process::PmrProcessInfo;
|
||||||
use super::super::config::dump::DumpConfig;
|
use super::super::config::dump::DumpConfig;
|
||||||
|
use super::super::config::log_path;
|
||||||
use super::list::list_processes;
|
use super::list::list_processes;
|
||||||
use super::start::start_process;
|
use super::start::start_process;
|
||||||
use super::stop::stop_process;
|
use super::stop::stop_process;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
pub fn restart_process(
|
pub fn restart_process(
|
||||||
config: Option<PathBuf>,
|
config: Option<PathBuf>,
|
||||||
namespace: Option<String>,
|
namespace: Option<String>,
|
||||||
target: Option<String>,
|
target: Option<String>,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
) {
|
) -> io::Result<()> {
|
||||||
let dump_config = DumpConfig::get_instance();
|
let dump_config = DumpConfig::get_instance();
|
||||||
|
|
||||||
// 如果指定了target,先检查是否是已存在的进程
|
// 如果指定了target,先检查是否是已存在的进程
|
||||||
@ -19,34 +23,49 @@ pub fn restart_process(
|
|||||||
// 尝试将target解析为pmr_id
|
// 尝试将target解析为pmr_id
|
||||||
if let Ok(pmr_id) = target_str.parse::<u32>() {
|
if let Ok(pmr_id) = target_str.parse::<u32>() {
|
||||||
if let Some(process) = processes.iter().find(|p| p.pmr_id == pmr_id) {
|
if let Some(process) = processes.iter().find(|p| p.pmr_id == pmr_id) {
|
||||||
restart_existing_process(process);
|
restart_existing_process(process)?;
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按名称查找进程
|
// 按名称查找进程
|
||||||
if let Some(process) = processes.iter().find(|p| p.name == *target_str) {
|
if let Some(process) = processes.iter().find(|p| p.name == *target_str) {
|
||||||
restart_existing_process(process);
|
restart_existing_process(process)?;
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果不是重启已存在的进程,就当作普通的启动处理
|
// 如果不是重启已存在的进程,就当作普通的启动处理
|
||||||
start_process(config, namespace, "default".to_string(), target, args);
|
start_process(config, namespace, "default".to_string(), target, args)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restart_existing_process(process: &PmrProcessInfo) {
|
|
||||||
|
fn restart_existing_process(process: &PmrProcessInfo) -> io::Result<()> {
|
||||||
println!("正在重启进程 '{}'...", process.name);
|
println!("正在重启进程 '{}'...", process.name);
|
||||||
|
|
||||||
// 先停止进程
|
// 先停止进程
|
||||||
stop_process(&process.pmr_id.to_string(), false);
|
stop_process(&process.pmr_id.to_string(), false);
|
||||||
|
|
||||||
|
// 获取日志文件路径
|
||||||
|
let log_path = log_path::get_log_path(process.pmr_id)?;
|
||||||
|
|
||||||
|
// 打开日志文件(追加模式)
|
||||||
|
let log_file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&log_path)?;
|
||||||
|
|
||||||
|
// 使用同一个文件句柄来重定向标准输出和标准错误
|
||||||
|
let stdout_log = log_file.try_clone()?;
|
||||||
|
let stderr_log = log_file.try_clone()?;
|
||||||
|
|
||||||
// 重新启动进程
|
// 重新启动进程
|
||||||
let mut cmd = std::process::Command::new(&process.program);
|
let mut cmd = std::process::Command::new(&process.program);
|
||||||
cmd.args(&process.args)
|
cmd.args(&process.args)
|
||||||
.stdout(std::process::Stdio::inherit())
|
.stdout(Stdio::from(stdout_log))
|
||||||
.stderr(std::process::Stdio::inherit());
|
.stderr(Stdio::from(stderr_log));
|
||||||
|
|
||||||
match cmd.spawn() {
|
match cmd.spawn() {
|
||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
@ -65,9 +84,14 @@ fn restart_existing_process(process: &PmrProcessInfo) {
|
|||||||
|
|
||||||
// 显示进程列表
|
// 显示进程列表
|
||||||
list_processes(false);
|
list_processes(false);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("重启进程失败: {}", e);
|
eprintln!("重启进程失败: {}", e);
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("重启进程失败: {}", e),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use super::super::base::process::PmrProcessInfo;
|
use super::super::base::process::PmrProcessInfo;
|
||||||
|
use super::super::config::log_path;
|
||||||
use super::super::config::dump::DumpConfig;
|
use super::super::config::dump::DumpConfig;
|
||||||
use super::list::list_processes;
|
use super::list::list_processes;
|
||||||
|
use super::stop::stop_process;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Read;
|
use std::io::{self, Read};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
@ -21,77 +23,84 @@ pub fn start_process(
|
|||||||
namespace: String,
|
namespace: String,
|
||||||
target: Option<String>,
|
target: Option<String>,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
) {
|
) -> io::Result<()> {
|
||||||
let dump_config = DumpConfig::get_instance();
|
let dump_config = DumpConfig::get_instance();
|
||||||
// 获取当前工作目录
|
// 获取当前工作目录
|
||||||
let workdir = env::current_dir()
|
let workdir = env::current_dir()
|
||||||
.unwrap_or_else(|_| PathBuf::from("."))
|
.unwrap_or_else(|_| PathBuf::from("."))
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.to_string();
|
||||||
|
|
||||||
|
// 获取进程名称
|
||||||
|
let process_name = name.unwrap_or_else(|| {
|
||||||
|
target
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.split('/').last().unwrap_or(s))
|
||||||
|
.unwrap_or("unknown")
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
// 如果指定了target,先检查是否是已存在的进程
|
// 如果指定了target,先检查是否是已存在的进程
|
||||||
if let Some(ref target_str) = target {
|
if let Some(ref target_str) = target {
|
||||||
if let Ok(processes) = dump_config.list_processes() {
|
if let Ok(processes) = dump_config.list_processes() {
|
||||||
// 按pmr_id查找进程
|
// 尝试将target解析为pmr_id
|
||||||
if let Ok(pmr_id) = target_str.parse::<u32>() {
|
if let Ok(pmr_id) = target_str.parse::<u32>() {
|
||||||
if let Some(process) = processes.iter().find(|p| p.pmr_id == pmr_id) {
|
if let Some(process) = processes.iter().find(|p| p.pmr_id == pmr_id) {
|
||||||
start_existing_process(process);
|
start_existing_process(process)?;
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按name查找进程
|
// 按名称查找进程
|
||||||
if let Some(process) = processes.iter().find(|p| p.name == *target_str) {
|
if let Some(process) = processes.iter().find(|p| p.name == *target_str) {
|
||||||
start_existing_process(process);
|
start_existing_process(process)?;
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已存在同名进程在同一namespace中
|
// 如果指定了配置文件,从配置文件启动
|
||||||
let process_name = name.clone().unwrap_or_else(|| {
|
|
||||||
target.clone().unwrap_or_else(|| {
|
|
||||||
if let Some(ref config_path) = config {
|
|
||||||
let mut file = File::open(config_path).expect("Failed to open config file");
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents)
|
|
||||||
.expect("Failed to read config file");
|
|
||||||
let config: Config =
|
|
||||||
serde_json::from_str(&contents).expect("Failed to parse config file");
|
|
||||||
config.name
|
|
||||||
} else {
|
|
||||||
"unnamed".to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Ok(processes) = dump_config.list_processes() {
|
|
||||||
if let Some(_existing) = processes
|
|
||||||
.iter()
|
|
||||||
.find(|p| p.name == process_name && p.namespace == namespace)
|
|
||||||
{
|
|
||||||
println!(
|
|
||||||
"\n进程 '{}' 在命名空间 '{}' 中已经存在:",
|
|
||||||
process_name, namespace
|
|
||||||
);
|
|
||||||
list_processes(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(config_path) = config {
|
if let Some(config_path) = config {
|
||||||
// 从配置文件启动
|
if !config_path.exists() {
|
||||||
let mut file = File::open(config_path).expect("无法打开配置文件");
|
eprintln!("配置文件不存在: {:?}", config_path);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
let mut file = File::open(&config_path)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents)
|
file.read_to_string(&mut contents)?;
|
||||||
.expect("无法读取配置文件");
|
|
||||||
|
|
||||||
let config: Config = serde_json::from_str(&contents).expect("无法解析配置文件");
|
let config: Config = serde_json::from_str(&contents).expect("无法解析配置文件");
|
||||||
|
|
||||||
|
let new_id = dump_config.add_process(
|
||||||
|
process_name.clone(),
|
||||||
|
namespace.clone(),
|
||||||
|
workdir.clone(),
|
||||||
|
config.program.clone(),
|
||||||
|
0,
|
||||||
|
"starting".to_string(),
|
||||||
|
config.args.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// 获取日志文件路径
|
||||||
|
let log_path = log_path::get_log_path(new_id)?;
|
||||||
|
|
||||||
|
// 打开日志文件(追加模式)
|
||||||
|
let log_file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&log_path)?;
|
||||||
|
|
||||||
|
// 使用同一个文件句柄来重定向标准输出和标准错误
|
||||||
|
let stdout_log = log_file.try_clone()?;
|
||||||
|
let stderr_log = log_file.try_clone()?;
|
||||||
|
|
||||||
|
// 启动进程
|
||||||
let mut cmd = Command::new(&config.program);
|
let mut cmd = Command::new(&config.program);
|
||||||
cmd.args(&config.args)
|
cmd.args(&config.args)
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::from(stdout_log))
|
||||||
.stderr(Stdio::inherit());
|
.stderr(Stdio::from(stderr_log));
|
||||||
|
|
||||||
match cmd.spawn() {
|
match cmd.spawn() {
|
||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
@ -99,27 +108,49 @@ pub fn start_process(
|
|||||||
println!("启动进程 '{}' PID: {}", process_name, pid);
|
println!("启动进程 '{}' PID: {}", process_name, pid);
|
||||||
|
|
||||||
dump_config
|
dump_config
|
||||||
.add_process(
|
.update_process_status(new_id, pid, "running".to_string())
|
||||||
process_name,
|
.expect("无法更新进程状态");
|
||||||
namespace,
|
|
||||||
workdir,
|
list_processes(false);
|
||||||
config.program,
|
|
||||||
pid,
|
|
||||||
"running".to_string(),
|
|
||||||
config.args,
|
|
||||||
)
|
|
||||||
.expect("无法将进程添加到配置文件");
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("启动进程失败: {}", e);
|
eprintln!("启动进程失败: {}", e);
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("启动进程失败: {}", e),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(target_program) = target {
|
} else if let Some(target_program) = target {
|
||||||
// 直接启动程序
|
// 直接启动程序
|
||||||
|
let new_id = dump_config.add_process(
|
||||||
|
process_name.clone(),
|
||||||
|
namespace.clone(),
|
||||||
|
workdir.clone(),
|
||||||
|
target_program.clone(),
|
||||||
|
0,
|
||||||
|
"starting".to_string(),
|
||||||
|
args.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// 获取日志文件路径
|
||||||
|
let log_path = log_path::get_log_path(new_id)?;
|
||||||
|
|
||||||
|
// 打开日志文件(追加模式)
|
||||||
|
let log_file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&log_path)?;
|
||||||
|
|
||||||
|
// 使用同一个文件句柄来重定向标准输出和标准错误
|
||||||
|
let stdout_log = log_file.try_clone()?;
|
||||||
|
let stderr_log = log_file.try_clone()?;
|
||||||
|
|
||||||
|
// 启动进程
|
||||||
let mut cmd = Command::new(&target_program);
|
let mut cmd = Command::new(&target_program);
|
||||||
cmd.args(&args)
|
cmd.args(&args)
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::from(stdout_log))
|
||||||
.stderr(Stdio::inherit());
|
.stderr(Stdio::from(stderr_log));
|
||||||
|
|
||||||
match cmd.spawn() {
|
match cmd.spawn() {
|
||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
@ -127,46 +158,52 @@ pub fn start_process(
|
|||||||
println!("启动进程 '{}' PID: {}", process_name, pid);
|
println!("启动进程 '{}' PID: {}", process_name, pid);
|
||||||
|
|
||||||
dump_config
|
dump_config
|
||||||
.add_process(
|
.update_process_status(new_id, pid, "running".to_string())
|
||||||
process_name,
|
.expect("无法更新进程状态");
|
||||||
namespace,
|
|
||||||
workdir,
|
|
||||||
target_program,
|
|
||||||
pid,
|
|
||||||
"running".to_string(),
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.expect("无法将进程添加到配置文件");
|
|
||||||
list_processes(false);
|
list_processes(false);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("启动进程失败: {}", e);
|
eprintln!("启动进程失败: {}", e);
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("启动进程失败: {}", e),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("错误: 必须指定 --config 或 target");
|
eprintln!("错误: 必须指定 --config 或 target");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_existing_process(process: &PmrProcessInfo) {
|
fn start_existing_process(process: &PmrProcessInfo) -> io::Result<()> {
|
||||||
if process.status == "running" {
|
if process.status == "running" {
|
||||||
println!("进程 '{}' 已经在运行中,PID: {}", process.name, process.pid);
|
println!("进程 '{}' 已经在运行中,PID: {}", process.name, process.pid);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存当前工作目录
|
// 先停止进程
|
||||||
let original_dir = env::current_dir().expect("无法获取当前工作目录");
|
stop_process(&process.pmr_id.to_string(), false);
|
||||||
|
|
||||||
// 切换到进程的工作目录
|
// 获取日志文件路径
|
||||||
if let Err(e) = env::set_current_dir(&process.workdir) {
|
let log_path = log_path::get_log_path(process.pmr_id)?;
|
||||||
eprintln!("无法切换到工作目录 {}: {}", process.workdir, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 打开日志文件(追加模式)
|
||||||
|
let log_file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&log_path)?;
|
||||||
|
|
||||||
|
// 使用同一个文件句柄来重定向标准输出和标准错误
|
||||||
|
let stdout_log = log_file.try_clone()?;
|
||||||
|
let stderr_log = log_file.try_clone()?;
|
||||||
|
|
||||||
|
// 重新启动进程
|
||||||
let mut cmd = Command::new(&process.program);
|
let mut cmd = Command::new(&process.program);
|
||||||
cmd.args(&process.args)
|
cmd.args(&process.args)
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::from(stdout_log))
|
||||||
.stderr(Stdio::inherit());
|
.stderr(Stdio::from(stderr_log));
|
||||||
|
|
||||||
match cmd.spawn() {
|
match cmd.spawn() {
|
||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
@ -180,14 +217,14 @@ fn start_existing_process(process: &PmrProcessInfo) {
|
|||||||
|
|
||||||
// 显示进程列表
|
// 显示进程列表
|
||||||
list_processes(false);
|
list_processes(false);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("启动进程失败: {}", e);
|
eprintln!("启动进程失败: {}", e);
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("启动进程失败: {}", e),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复原始工作目录
|
|
||||||
if let Err(e) = env::set_current_dir(&original_dir) {
|
|
||||||
eprintln!("警告:无法恢复原始工作目录: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ impl DumpConfig {
|
|||||||
let json: serde_json::Value = serde_json::from_str(&file_contents)?;
|
let json: serde_json::Value = serde_json::from_str(&file_contents)?;
|
||||||
|
|
||||||
// 手动构建进程列表
|
// 手动构建进程列表
|
||||||
let processes = if let Some(processes) = json.get("processes").and_then(|v| v.as_array()) {
|
let processes =
|
||||||
|
if let Some(processes) = json.get("processes").and_then(|v| v.as_array()) {
|
||||||
processes
|
processes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| PmrProcessInfo {
|
.map(|p| PmrProcessInfo {
|
||||||
@ -93,7 +94,7 @@ impl DumpConfig {
|
|||||||
pid: u32,
|
pid: u32,
|
||||||
status: String,
|
status: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<u32> {
|
||||||
let mut data = self.data.lock().unwrap();
|
let mut data = self.data.lock().unwrap();
|
||||||
let new_id = data.processes.iter().map(|p| p.pmr_id).max().unwrap_or(0) + 1;
|
let new_id = data.processes.iter().map(|p| p.pmr_id).max().unwrap_or(0) + 1;
|
||||||
|
|
||||||
@ -109,7 +110,8 @@ impl DumpConfig {
|
|||||||
restarts: 0, // 初始化重启次数为0
|
restarts: 0, // 初始化重启次数为0
|
||||||
});
|
});
|
||||||
|
|
||||||
self.save_data(&data)
|
self.save_data(&data)?;
|
||||||
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_process(&self, id: u32) -> io::Result<()> {
|
pub fn delete_process(&self, id: u32) -> io::Result<()> {
|
||||||
|
17
src/config/log_path.rs
Normal file
17
src/config/log_path.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use dirs;
|
||||||
|
|
||||||
|
pub fn get_log_path(pmr_id: u32) -> io::Result<PathBuf> {
|
||||||
|
let home_dir = dirs::home_dir()
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Home directory not found"))?;
|
||||||
|
let log_dir = home_dir.join(".pmr").join("logs");
|
||||||
|
|
||||||
|
// 确保日志目录存在
|
||||||
|
if !log_dir.exists() {
|
||||||
|
fs::create_dir_all(&log_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(log_dir.join(format!("{}.log", pmr_id)))
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
pub mod dump;
|
pub mod dump;
|
||||||
|
pub mod log_path;
|
||||||
|
43
src/main.rs
43
src/main.rs
@ -8,6 +8,7 @@ use commands::list::list_processes;
|
|||||||
use commands::restart::restart_process;
|
use commands::restart::restart_process;
|
||||||
use commands::start::start_process;
|
use commands::start::start_process;
|
||||||
use commands::stop::stop_process;
|
use commands::stop::stop_process;
|
||||||
|
use commands::tail_log;
|
||||||
use config::dump::DumpConfig;
|
use config::dump::DumpConfig;
|
||||||
|
|
||||||
fn config_init() -> std::io::Result<()> {
|
fn config_init() -> std::io::Result<()> {
|
||||||
@ -49,7 +50,12 @@ enum Commands {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// List running processes
|
/// List running processes
|
||||||
#[command(alias = "ls")]
|
#[command(
|
||||||
|
alias = "ls",
|
||||||
|
alias = "ps",
|
||||||
|
alias = "status",
|
||||||
|
about = "List running processes. Alias: ls, ps, status"
|
||||||
|
)]
|
||||||
List {
|
List {
|
||||||
/// Show all system processes
|
/// Show all system processes
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
@ -57,7 +63,11 @@ enum Commands {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// Delete a process
|
/// Delete a process
|
||||||
#[command(alias = "rm")]
|
#[command(
|
||||||
|
alias = "rm",
|
||||||
|
alias = "del",
|
||||||
|
about = "Delete a process. Alias: rm, del"
|
||||||
|
)]
|
||||||
Delete {
|
Delete {
|
||||||
/// Process ID or name
|
/// Process ID or name
|
||||||
target: String,
|
target: String,
|
||||||
@ -86,6 +96,13 @@ enum Commands {
|
|||||||
#[arg(last = true)]
|
#[arg(last = true)]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// View logs of a process
|
||||||
|
#[command(alias = "logs")]
|
||||||
|
Log {
|
||||||
|
/// Process ID or name
|
||||||
|
target: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -108,7 +125,13 @@ fn main() {
|
|||||||
eprintln!("错误: 必须指定 --config 或 target");
|
eprintln!("错误: 必须指定 --config 或 target");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
start_process(config, name, namespace, target, args);
|
match start_process(config, name, namespace, target, args) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("启动进程失败: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Commands::List { system } => {
|
Commands::List { system } => {
|
||||||
list_processes(system);
|
list_processes(system);
|
||||||
@ -129,7 +152,19 @@ fn main() {
|
|||||||
eprintln!("错误: 必须指定 --config 或 target");
|
eprintln!("错误: 必须指定 --config 或 target");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
restart_process(config, Some(namespace), target, args);
|
match restart_process(config, Some(namespace), target, args) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("重启进程失败: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Log { target } => {
|
||||||
|
if let Err(e) = tail_log(target) {
|
||||||
|
eprintln!("查看日志失败: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user