use std::path::Path;
use crate::proxy_lock;
pub enum StopResult {
Stopped { name: String, pid: u32 },
StaleCleaned { name: String },
}
pub fn start_proxy(name: &str) -> Result<(), String> {
proxy_lock::read_snapshot(name)
.map_err(|e| format!("no config snapshot for proxy \"{name}\": {e}"))?;
let config_path = proxy_lock::config_snapshot_path(name).display().to_string();
let exe = std::env::current_exe().map_err(|e| format!("cannot find mcpr binary: {e}"))?;
let status = std::process::Command::new(exe)
.args(["proxy", "run", "--config", &config_path])
.status()
.map_err(|e| format!("failed to spawn proxy \"{name}\": {e}"))?;
if !status.success() {
return Err(format!("proxy \"{name}\" failed to start"));
}
Ok(())
}
pub fn stop_proxy(name: &str) -> Result<StopResult, String> {
match proxy_lock::check_lock(name) {
proxy_lock::LockStatus::Held(info) => {
let pid = info.pid;
proxy_lock::stop_proxy(name);
Ok(StopResult::Stopped {
name: name.to_string(),
pid,
})
}
proxy_lock::LockStatus::Stale(_) => {
proxy_lock::remove_lock(name);
Ok(StopResult::StaleCleaned {
name: name.to_string(),
})
}
proxy_lock::LockStatus::Free => Err(format!("proxy \"{}\" is not running.", name)),
}
}
pub fn stop_all_proxies() -> Vec<String> {
proxy_lock::stop_all_proxies()
}
pub fn restart_proxy(name: &str, config_path: Option<&Path>) -> Result<(), String> {
let new_snapshot = match config_path {
Some(path) => Some(read_config_file(path)?),
None => None,
};
proxy_lock::stop_proxy(name);
if let Some(contents) = new_snapshot {
proxy_lock::snapshot_config(name, &contents)
.map_err(|e| format!("failed to write config snapshot for \"{name}\": {e}"))?;
}
start_proxy(name)
}
pub fn restart_all_proxies() -> Result<usize, String> {
let proxies = proxy_lock::list_proxies();
let mut restarted = 0;
for (name, status) in &proxies {
match status {
proxy_lock::LockStatus::Held(_) | proxy_lock::LockStatus::Stale(_) => {
restart_proxy(name, None)?;
restarted += 1;
}
proxy_lock::LockStatus::Free => {}
}
}
Ok(restarted)
}
pub fn reload_proxy(name: &str, config_path: &Path) -> Result<(), String> {
let info = match proxy_lock::check_lock(name) {
proxy_lock::LockStatus::Held(info) => info,
proxy_lock::LockStatus::Stale(_) | proxy_lock::LockStatus::Free => {
return Err(format!("proxy \"{name}\" is not running"));
}
};
let contents = read_config_file(config_path)?;
proxy_lock::snapshot_config(name, &contents)
.map_err(|e| format!("failed to write config snapshot for \"{name}\": {e}"))?;
send_sighup(info.pid)
}
fn read_config_file(path: &Path) -> Result<String, String> {
std::fs::read_to_string(path).map_err(|e| format!("failed to read {}: {e}", path.display()))
}
#[cfg(unix)]
fn send_sighup(pid: u32) -> Result<(), String> {
use nix::sys::signal::{Signal, kill};
use nix::unistd::Pid;
kill(Pid::from_raw(pid as i32), Signal::SIGHUP)
.map_err(|e| format!("failed to send SIGHUP to pid {pid}: {e}"))
}
#[cfg(not(unix))]
fn send_sighup(_pid: u32) -> Result<(), String> {
Err("reload is not supported on this platform".to_string())
}
pub fn list_proxies() -> Vec<(String, proxy_lock::LockStatus)> {
proxy_lock::list_proxies()
}