use crate::service::{ServiceConfig, ServiceManager, ServiceStatus};
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use std::process::Command;
const SYSTEMD_SERVICE_TEMPLATE: &str = include_str!("templates/systemd.service");
pub struct LinuxServiceManager {
config: ServiceConfig,
}
impl LinuxServiceManager {
pub fn new(config: ServiceConfig) -> Self {
Self { config }
}
fn service_file_path(&self) -> String {
format!("/etc/systemd/system/{}.service", self.config.name)
}
fn create_service_file(&self) -> Result<()> {
let executable = self.config.executable_path.to_string_lossy();
let args = self.config.args.join(" ");
let service_content = SYSTEMD_SERVICE_TEMPLATE
.replace("{DESCRIPTION}", &self.config.description)
.replace("{EXECUTABLE}", &executable)
.replace("{ARGS}", &args);
fs::write(self.service_file_path(), service_content)
.context("Failed to write systemd service file")
}
}
impl ServiceManager for LinuxServiceManager {
fn install(&self) -> Result<()> {
self.config.copy_config_to_system()?;
self.create_service_file()?;
Command::new("systemctl")
.args(["daemon-reload"])
.status()
.context("Failed to reload systemd")?;
Command::new("systemctl")
.args(["enable", &self.config.name])
.status()
.context("Failed to enable service")?;
Ok(())
}
fn uninstall(&self) -> Result<()> {
let _ = self.stop();
Command::new("systemctl")
.args(["disable", &self.config.name])
.status()
.context("Failed to disable service")?;
if Path::new(&self.service_file_path()).exists() {
fs::remove_file(self.service_file_path()).context("Failed to remove service file")?;
}
Command::new("systemctl")
.args(["daemon-reload"])
.status()
.context("Failed to reload systemd")?;
Ok(())
}
fn start(&self) -> Result<()> {
Command::new("systemctl")
.args(["start", &self.config.name])
.status()
.context("Failed to start service")?;
Ok(())
}
fn stop(&self) -> Result<()> {
Command::new("systemctl")
.args(["stop", &self.config.name])
.status()
.context("Failed to stop service")?;
Ok(())
}
fn status(&self) -> Result<ServiceStatus> {
let service_path = self.service_file_path();
let service_file = Path::new(&service_path);
if !service_file.exists() {
return Ok(ServiceStatus::NotInstalled);
}
let output = Command::new("systemctl")
.args(["is-active", &self.config.name])
.output()
.context("Failed to check service status")?;
let status_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
match status_str.as_str() {
"active" => Ok(ServiceStatus::Running),
"inactive" => Ok(ServiceStatus::Stopped),
_ => Ok(ServiceStatus::Unknown),
}
}
}