ant_service_management/
control.rs1use crate::error::{Error, Result};
10use service_manager::{
11 ServiceInstallCtx, ServiceLabel, ServiceLevel, ServiceManager, ServiceStartCtx, ServiceStopCtx,
12 ServiceUninstallCtx,
13};
14use std::{
15 net::{SocketAddr, TcpListener},
16 path::Path,
17};
18use sysinfo::System;
19
20pub trait ServiceControl: Sync {
28 fn create_service_user(&self, username: &str) -> Result<()>;
29 fn get_available_port(&self) -> Result<u16>;
30 fn install(&self, install_ctx: ServiceInstallCtx, user_mode: bool) -> Result<()>;
31 fn get_process_pid(&self, path: &Path) -> Result<u32>;
32 fn start(&self, service_name: &str, user_mode: bool) -> Result<()>;
33 fn stop(&self, service_name: &str, user_mode: bool) -> Result<()>;
34 fn uninstall(&self, service_name: &str, user_mode: bool) -> Result<()>;
35 fn wait(&self, delay: u64);
36}
37
38pub struct ServiceController {}
39
40impl ServiceControl for ServiceController {
41 #[cfg(target_os = "linux")]
42 fn create_service_user(&self, username: &str) -> Result<()> {
43 use std::process::Command;
44
45 let output = Command::new("id")
46 .arg("-u")
47 .arg(username)
48 .output()
49 .inspect_err(|err| error!("Failed to execute id -u: {err:?}"))?;
50 if output.status.success() {
51 println!("The {username} user already exists");
52 return Ok(());
53 }
54
55 let useradd_exists = Command::new("which")
56 .arg("useradd")
57 .output()
58 .inspect_err(|err| error!("Failed to execute which useradd: {err:?}"))?
59 .status
60 .success();
61 let adduser_exists = Command::new("which")
62 .arg("adduser")
63 .output()
64 .inspect_err(|err| error!("Failed to execute which adduser: {err:?}"))?
65 .status
66 .success();
67
68 let output = if useradd_exists {
69 Command::new("useradd")
70 .arg("-m")
71 .arg("-s")
72 .arg("/bin/bash")
73 .arg(username)
74 .output()
75 .inspect_err(|err| error!("Failed to execute useradd: {err:?}"))?
76 } else if adduser_exists {
77 Command::new("adduser")
78 .arg("-s")
79 .arg("/bin/busybox")
80 .arg("-D")
81 .arg(username)
82 .output()
83 .inspect_err(|err| error!("Failed to execute adduser: {err:?}"))?
84 } else {
85 error!("Neither useradd nor adduser is available. ServiceUserAccountCreationFailed");
86 return Err(Error::ServiceUserAccountCreationFailed);
87 };
88
89 if !output.status.success() {
90 error!("Failed to create {username} user account: {output:?}");
91 return Err(Error::ServiceUserAccountCreationFailed);
92 }
93 println!("Created {username} user account for running the service");
94 info!("Created {username} user account for running the service");
95 Ok(())
96 }
97
98 #[cfg(target_os = "macos")]
99 fn create_service_user(&self, username: &str) -> Result<()> {
100 use std::process::Command;
101 use std::str;
102
103 let output = Command::new("dscl")
104 .arg(".")
105 .arg("-list")
106 .arg("/Users")
107 .output()
108 .inspect_err(|err| error!("Failed to execute dscl: {err:?}"))?;
109 let output_str = str::from_utf8(&output.stdout)
110 .inspect_err(|err| error!("Error while converting output to utf8: {err:?}"))?;
111 if output_str.lines().any(|line| line == username) {
112 return Ok(());
113 }
114
115 let output = Command::new("dscl")
116 .arg(".")
117 .arg("-list")
118 .arg("/Users")
119 .arg("UniqueID")
120 .output()
121 .inspect_err(|err| error!("Failed to execute dscl: {err:?}"))?;
122 let output_str = str::from_utf8(&output.stdout)
123 .inspect_err(|err| error!("Error while converting output to utf8: {err:?}"))?;
124 let mut max_id = 0;
125
126 for line in output_str.lines() {
127 let parts: Vec<&str> = line.split_whitespace().collect();
128 if parts.len() == 2 {
129 if let Ok(id) = parts[1].parse::<u32>() {
130 if id > max_id {
131 max_id = id;
132 }
133 }
134 }
135 }
136 let new_unique_id = max_id + 1;
137
138 let commands = vec![
139 format!("dscl . -create /Users/{}", username),
140 format!(
141 "dscl . -create /Users/{} UserShell /usr/bin/false",
142 username
143 ),
144 format!(
145 "dscl . -create /Users/{} UniqueID {}",
146 username, new_unique_id
147 ),
148 format!("dscl . -create /Users/{} PrimaryGroupID 20", username),
149 ];
150 for cmd in commands {
151 let status = Command::new("sh")
152 .arg("-c")
153 .arg(&cmd)
154 .status()
155 .inspect_err(|err| error!("Error while executing dscl command: {err:?}"))?;
156 if !status.success() {
157 error!("The command {cmd} failed to execute. ServiceUserAccountCreationFailed");
158 return Err(Error::ServiceUserAccountCreationFailed);
159 }
160 }
161 Ok(())
162 }
163
164 #[cfg(target_os = "windows")]
165 fn create_service_user(&self, _username: &str) -> Result<()> {
166 Ok(())
167 }
168
169 fn get_available_port(&self) -> Result<u16> {
170 let addr: SocketAddr = "127.0.0.1:0".parse()?;
171
172 let socket = TcpListener::bind(addr)?;
173 let port = socket.local_addr()?.port();
174 drop(socket);
175 trace!("Got available port: {port}");
176
177 Ok(port)
178 }
179
180 fn get_process_pid(&self, bin_path: &Path) -> Result<u32> {
181 debug!(
182 "Searching for process with binary at {}",
183 bin_path.to_string_lossy()
184 );
185 let system = System::new_all();
186 for (pid, process) in system.processes() {
187 if let Some(path) = process.exe() {
188 if bin_path == path {
189 trace!("Found process {bin_path:?} with PID: {pid}");
192 return Ok(pid.to_string().parse::<u32>()?);
193 }
194 }
195 }
196 error!(
197 "No process was located with a path at {}",
198 bin_path.to_string_lossy()
199 );
200 Err(Error::ServiceProcessNotFound(
201 bin_path.to_string_lossy().to_string(),
202 ))
203 }
204
205 fn install(&self, install_ctx: ServiceInstallCtx, user_mode: bool) -> Result<()> {
206 debug!("Installing service: {install_ctx:?}");
207 let mut manager = <dyn ServiceManager>::native()
208 .inspect_err(|err| error!("Could not get native ServiceManage: {err:?}"))?;
209 if user_mode {
210 manager
211 .set_level(ServiceLevel::User)
212 .inspect_err(|err| error!("Could not set service to user mode: {err:?}"))?;
213 }
214 manager
215 .install(install_ctx)
216 .inspect_err(|err| error!("Error while installing service: {err:?}"))?;
217 Ok(())
218 }
219
220 fn start(&self, service_name: &str, user_mode: bool) -> Result<()> {
221 debug!("Starting service: {service_name}");
222 let label: ServiceLabel = service_name.parse()?;
223 let mut manager = <dyn ServiceManager>::native()
224 .inspect_err(|err| error!("Could not get native ServiceManage: {err:?}"))?;
225 if user_mode {
226 manager
227 .set_level(ServiceLevel::User)
228 .inspect_err(|err| error!("Could not set service to user mode: {err:?}"))?;
229 }
230 manager
231 .start(ServiceStartCtx { label })
232 .inspect_err(|err| error!("Error while starting service: {err:?}"))?;
233 Ok(())
234 }
235
236 fn stop(&self, service_name: &str, user_mode: bool) -> Result<()> {
237 debug!("Stopping service: {service_name}");
238 let label: ServiceLabel = service_name.parse()?;
239 let mut manager = <dyn ServiceManager>::native()
240 .inspect_err(|err| error!("Could not get native ServiceManage: {err:?}"))?;
241 if user_mode {
242 manager
243 .set_level(ServiceLevel::User)
244 .inspect_err(|err| error!("Could not set service to user mode: {err:?}"))?;
245 }
246 manager
247 .stop(ServiceStopCtx { label })
248 .inspect_err(|err| error!("Error while stopping service: {err:?}"))?;
249
250 Ok(())
251 }
252
253 fn uninstall(&self, service_name: &str, user_mode: bool) -> Result<()> {
254 debug!("Uninstalling service: {service_name}");
255 let label: ServiceLabel = service_name.parse()?;
256 let mut manager = <dyn ServiceManager>::native()
257 .inspect_err(|err| error!("Could not get native ServiceManage: {err:?}"))?;
258
259 if user_mode {
260 manager
261 .set_level(ServiceLevel::User)
262 .inspect_err(|err| error!("Could not set service to user mode: {err:?}"))?;
263 }
264 match manager.uninstall(ServiceUninstallCtx { label }) {
265 Ok(()) => Ok(()),
266 Err(err) => {
267 if std::io::ErrorKind::NotFound == err.kind() {
268 error!("Error while uninstall service, service file might have been removed manually: {service_name}");
269 Err(Error::ServiceRemovedManually(service_name.to_string()))
273 } else if err.raw_os_error() == Some(267) {
274 Err(Error::ServiceDoesNotExists(service_name.to_string()))
281 } else {
282 error!("Error while uninstalling service: {err:?}");
283 Err(err.into())
284 }
285 }
286 }
287 }
288
289 fn wait(&self, delay: u64) {
293 trace!("Waiting for {delay} milliseconds");
294 std::thread::sleep(std::time::Duration::from_millis(delay));
295 }
296}