cloudpub_client/service/
linux.rs1use crate::service::{ServiceConfig, ServiceManager, ServiceStatus};
2use anyhow::{Context, Result};
3use std::fs;
4use std::path::Path;
5use std::process::Command;
6
7const SYSTEMD_SERVICE_TEMPLATE: &str = include_str!("templates/systemd.service");
9
10pub struct LinuxServiceManager {
11 config: ServiceConfig,
12}
13
14impl LinuxServiceManager {
15 pub fn new(config: ServiceConfig) -> Self {
16 Self { config }
17 }
18
19 fn service_file_path(&self) -> String {
20 format!("/etc/systemd/system/{}.service", self.config.name)
21 }
22
23 fn create_service_file(&self) -> Result<()> {
24 let executable = self.config.executable_path.to_string_lossy();
25 let args = self.config.args.join(" ");
26
27 let service_content = SYSTEMD_SERVICE_TEMPLATE
29 .replace("{DESCRIPTION}", &self.config.description)
30 .replace("{EXECUTABLE}", &executable)
31 .replace("{ARGS}", &args);
32
33 fs::write(self.service_file_path(), service_content)
34 .context("Failed to write systemd service file")
35 }
36}
37
38impl ServiceManager for LinuxServiceManager {
39 fn install(&self) -> Result<()> {
40 self.config.copy_config_to_system()?;
42
43 self.create_service_file()?;
45
46 Command::new("systemctl")
48 .args(["daemon-reload"])
49 .status()
50 .context("Failed to reload systemd")?;
51
52 Command::new("systemctl")
54 .args(["enable", &self.config.name])
55 .status()
56 .context("Failed to enable service")?;
57
58 Ok(())
59 }
60
61 fn uninstall(&self) -> Result<()> {
62 let _ = self.stop();
64
65 Command::new("systemctl")
67 .args(["disable", &self.config.name])
68 .status()
69 .context("Failed to disable service")?;
70
71 if Path::new(&self.service_file_path()).exists() {
73 fs::remove_file(self.service_file_path()).context("Failed to remove service file")?;
74 }
75
76 Command::new("systemctl")
78 .args(["daemon-reload"])
79 .status()
80 .context("Failed to reload systemd")?;
81
82 Ok(())
83 }
84
85 fn start(&self) -> Result<()> {
86 Command::new("systemctl")
87 .args(["start", &self.config.name])
88 .status()
89 .context("Failed to start service")?;
90 Ok(())
91 }
92
93 fn stop(&self) -> Result<()> {
94 Command::new("systemctl")
95 .args(["stop", &self.config.name])
96 .status()
97 .context("Failed to stop service")?;
98 Ok(())
99 }
100
101 fn status(&self) -> Result<ServiceStatus> {
102 let service_path = self.service_file_path();
103 let service_file = Path::new(&service_path);
104 if !service_file.exists() {
105 return Ok(ServiceStatus::NotInstalled);
106 }
107
108 let output = Command::new("systemctl")
109 .args(["is-active", &self.config.name])
110 .output()
111 .context("Failed to check service status")?;
112
113 let status_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
114
115 match status_str.as_str() {
116 "active" => Ok(ServiceStatus::Running),
117 "inactive" => Ok(ServiceStatus::Stopped),
118 _ => Ok(ServiceStatus::Unknown),
119 }
120 }
121}