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
7pub struct LinuxServiceManager {
8 config: ServiceConfig,
9}
10
11impl LinuxServiceManager {
12 pub fn new(config: ServiceConfig) -> Self {
13 Self { config }
14 }
15
16 fn service_file_path(&self) -> String {
17 format!("/etc/systemd/system/{}.service", self.config.name)
18 }
19
20 fn create_service_file(&self) -> Result<()> {
21 let executable = self.config.executable_path.to_string_lossy();
22 let args = self.config.args.join(" ");
23
24 let service_content = format!(
25 r#"[Unit]
26Description={}
27After=network.target
28
29[Service]
30Type=simple
31ExecStart={} {}
32Restart=on-failure
33RestartSec=5s
34
35[Install]
36WantedBy=multi-user.target
37"#,
38 self.config.description, executable, args
39 );
40
41 fs::write(self.service_file_path(), service_content)
42 .context("Failed to write systemd service file")
43 }
44}
45
46impl ServiceManager for LinuxServiceManager {
47 fn install(&self) -> Result<()> {
48 self.create_service_file()?;
50
51 Command::new("systemctl")
53 .args(["daemon-reload"])
54 .status()
55 .context("Failed to reload systemd")?;
56
57 Command::new("systemctl")
59 .args(["enable", &self.config.name])
60 .status()
61 .context("Failed to enable service")?;
62
63 Ok(())
64 }
65
66 fn uninstall(&self) -> Result<()> {
67 let _ = self.stop();
69
70 Command::new("systemctl")
72 .args(["disable", &self.config.name])
73 .status()
74 .context("Failed to disable service")?;
75
76 if Path::new(&self.service_file_path()).exists() {
78 fs::remove_file(self.service_file_path()).context("Failed to remove service file")?;
79 }
80
81 Command::new("systemctl")
83 .args(["daemon-reload"])
84 .status()
85 .context("Failed to reload systemd")?;
86
87 Ok(())
88 }
89
90 fn start(&self) -> Result<()> {
91 Command::new("systemctl")
92 .args(["start", &self.config.name])
93 .status()
94 .context("Failed to start service")?;
95 Ok(())
96 }
97
98 fn stop(&self) -> Result<()> {
99 Command::new("systemctl")
100 .args(["stop", &self.config.name])
101 .status()
102 .context("Failed to stop service")?;
103 Ok(())
104 }
105
106 fn status(&self) -> Result<ServiceStatus> {
107 let service_path = self.service_file_path();
108 let service_file = Path::new(&service_path);
109 if !service_file.exists() {
110 return Ok(ServiceStatus::NotInstalled);
111 }
112
113 let output = Command::new("systemctl")
114 .args(["is-active", &self.config.name])
115 .output()
116 .context("Failed to check service status")?;
117
118 let status_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
119
120 match status_str.as_str() {
121 "active" => Ok(ServiceStatus::Running),
122 "inactive" => Ok(ServiceStatus::Stopped),
123 _ => Ok(ServiceStatus::Unknown),
124 }
125 }
126}