use crate::error::{MhoError, Result};
use crate::{log_debug, log_error, log_info};
use std::path::Path;
use std::process::{Command, Stdio};
pub fn start(exe_path: &Path, args: Vec<String>, envs: Vec<(&str, &str)>) -> Result<()> {
log_info!(
"Starting process: {} with args: {:?}",
exe_path.display(),
args
);
if !exe_path.exists() {
return Err(MhoError::Process(format!(
"Executable not found: {}",
exe_path.display()
)));
}
let mut c = Command::new(exe_path);
c.args(&args).envs(envs);
#[cfg(target_os = "windows")]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
c.creation_flags(CREATE_NO_WINDOW);
}
c.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|e| {
log_error!("Failed to start process {}: {}", exe_path.display(), e);
MhoError::Process(format!(
"Failed to start process {}: {}",
exe_path.display(),
e
))
})?;
log_info!("Process started successfully: {}", exe_path.display());
Ok(())
}
pub fn stop(exe_name: &str) -> Result<()> {
log_info!("Stopping process: {}", exe_name);
if !is_running(exe_name) {
log_debug!("Process {} is not running", exe_name);
return Ok(());
}
kill_process(exe_name).map_err(|e| {
log_error!("Failed to stop process {}: {}", exe_name, e);
e
})?;
log_info!("Process stopped successfully: {}", exe_name);
Ok(())
}
use crate::utils::command::execute;
#[cfg(target_os = "macos")]
pub fn get_pid(name: &str) -> Result<u32> {
let output = execute("pgrep", &["-x", name])?;
let pid_str = output
.trim()
.split_whitespace()
.next()
.ok_or_else(|| MhoError::Process(format!("No process found with name: {}", name)))?;
pid_str
.parse::<u32>()
.map_err(|e| MhoError::Process(format!("Failed to parse PID '{}': {}", pid_str, e)))
}
#[cfg(target_os = "linux")]
pub fn get_pid(name: &str) -> Result<u32> {
let output = execute("pidof", &[name])?;
let pid_str = output
.split_whitespace()
.next()
.ok_or_else(|| MhoError::Process(format!("No process found with name: {}", name)))?;
pid_str
.parse::<u32>()
.map_err(|e| MhoError::Process(format!("Failed to parse PID '{}': {}", pid_str, e)))
}
#[cfg(unix)]
pub fn kill_process(name_or_path: &str) -> Result<()> {
let process_name = Path::new(name_or_path)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(name_or_path);
if Command::new("pkill")
.args(["-f", process_name])
.output()
.is_ok()
{
return Ok(());
}
let output = Command::new("killall")
.arg(process_name)
.output()
.map_err(|e| MhoError::Process(format!("Failed to execute killall: {}", e)))?;
if output.status.success() {
Ok(())
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
Err(MhoError::Process(format!(
"Failed to kill process '{}': {}",
process_name, stderr
)))
}
}
#[cfg(windows)]
pub fn get_pid(name: &str) -> Result<u32> {
let output = execute(
"tasklist",
&[
"/FI",
&format!("IMAGENAME eq {}", name),
"/NH",
"/FO",
"CSV",
],
)?;
for line in output.lines() {
let line_lower = line.to_lowercase();
if line_lower.starts_with(&format!("\"{}\"", name.to_lowercase())) {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() >= 2 {
let pid_str = parts[1].trim().trim_matches('"');
return pid_str.parse::<u32>().map_err(|e| {
MhoError::Process(format!("Failed to parse PID '{}': {}", pid_str, e))
});
}
}
}
Err(MhoError::Process(format!("Process '{}' not found", name)))
}
#[cfg(windows)]
pub fn kill_process(name_or_path: &str) -> Result<()> {
let process_name = Path::new(name_or_path)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(name_or_path);
execute("taskkill", &["/F", "/IM", process_name])?;
Ok(())
}
pub fn is_running(name: &str) -> bool {
get_pid(name).is_ok()
}