1use super::*;
2
3impl Default for ServerManager {
5 #[inline(always)]
7 fn default() -> Self {
8 let empty_hook: ServerManagerHook = Arc::new(|| Box::pin(async {}));
9 Self {
10 pid_file: Default::default(),
11 stop_hook: empty_hook.clone(),
12 server_hook: empty_hook.clone(),
13 start_hook: empty_hook,
14 }
15 }
16}
17
18impl ServerManager {
22 #[inline(always)]
26 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub async fn start(&self) {
34 (self.get_start_hook())().await;
35 if let Err(e) = self.write_pid_file() {
36 eprintln!("Failed to write pid file: {e}");
37 return;
38 }
39 (self.get_server_hook())().await;
40 }
41
42 pub async fn stop(&self) -> ServerManagerResult {
50 (self.get_stop_hook())().await;
51 let pid: i32 = self.read_pid_file()?;
52 self.kill_process(pid)
53 }
54
55 #[cfg(not(windows))]
57 pub async fn start_daemon(&self) -> ServerManagerResult {
58 (self.get_start_hook())().await;
59 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
60 self.write_pid_file()?;
61 let rt: Runtime = Runtime::new()?;
62 rt.block_on(async {
63 (self.get_server_hook())().await;
64 });
65 return Ok(());
66 }
67 let exe_path: PathBuf = std::env::current_exe()?;
68 let mut cmd: Command = Command::new(exe_path);
69 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
70 .stdout(Stdio::null())
71 .stderr(Stdio::null())
72 .stdin(Stdio::null());
73 cmd.spawn()
74 .map_err(|error: Error| Box::new(error) as Box<dyn std::error::Error>)?;
75 Ok(())
76 }
77
78 #[cfg(windows)]
80 pub async fn start_daemon(&self) -> ServerManagerResult {
81 (self.get_start_hook())().await;
82 use std::os::windows::process::CommandExt;
83 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
84 self.write_pid_file()?;
85 let rt: Runtime = Runtime::new()?;
86 rt.block_on(async {
87 (self.get_server_hook())().await;
88 });
89 return Ok(());
90 }
91 let exe_path: PathBuf = std::env::current_exe()?;
92 let mut cmd: Command = Command::new(exe_path);
93 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
94 .stdout(Stdio::null())
95 .stderr(Stdio::null())
96 .stdin(Stdio::null())
97 .creation_flags(0x00000008);
98 cmd.spawn()
99 .map_err(|error: Error| Box::new(error) as Box<dyn std::error::Error>)?;
100 Ok(())
101 }
102
103 fn read_pid_file(&self) -> Result<i32, Box<dyn std::error::Error>> {
109 let pid_str: String = fs::read_to_string(self.get_pid_file())?;
110 let pid: i32 = pid_str.trim().parse::<i32>()?;
111 Ok(pid)
112 }
113
114 fn write_pid_file(&self) -> ServerManagerResult {
120 if let Some(parent) = Path::new(self.get_pid_file()).parent() {
121 fs::create_dir_all(parent)?;
122 }
123 let pid: u32 = id();
124 fs::write(self.get_pid_file(), pid.to_string())?;
125 Ok(())
126 }
127
128 #[cfg(not(windows))]
138 fn kill_process(&self, pid: i32) -> ServerManagerResult {
139 match Command::new("kill")
140 .arg("-TERM")
141 .arg(pid.to_string())
142 .output()
143 {
144 Ok(output) if output.status.success() => Ok(()),
145 Ok(output) => Err(format!(
146 "Failed to kill process with pid: {}, error: {}",
147 pid,
148 String::from_utf8_lossy(&output.stderr)
149 )
150 .into()),
151 Err(e) => Err(format!("Failed to execute kill command: {}", e).into()),
152 }
153 }
154
155 #[cfg(windows)]
165 fn kill_process(&self, pid: i32) -> ServerManagerResult {
166 unsafe extern "system" {
167 fn OpenProcess(
168 dwDesiredAccess: u32,
169 bInheritHandle: i32,
170 dwProcessId: u32,
171 ) -> *mut c_void;
172 fn TerminateProcess(hProcess: *mut c_void, uExitCode: u32) -> i32;
173 fn CloseHandle(hObject: *mut c_void) -> i32;
174 fn GetLastError() -> u32;
175 }
176 let process_id: u32 = pid as u32;
177 let mut process_handle: *mut c_void = unsafe { OpenProcess(0x0001, 0, process_id) };
178 if process_handle.is_null() {
179 process_handle = unsafe { OpenProcess(0x1F0FFF, 0, process_id) };
180 }
181 if process_handle.is_null() {
182 let error_code = unsafe { GetLastError() };
183 return Err(format!(
184 "Failed to open process with pid: {pid}. Error code: {error_code}"
185 )
186 .into());
187 }
188 let terminate_result: i32 = unsafe { TerminateProcess(process_handle, 1) };
189 if terminate_result == 0 {
190 let error_code = unsafe { GetLastError() };
191 unsafe {
192 CloseHandle(process_handle);
193 }
194 return Err(format!(
195 "Failed to terminate process with pid: {pid}. Error code: {error_code}"
196 )
197 .into());
198 }
199 unsafe {
200 CloseHandle(process_handle);
201 }
202 Ok(())
203 }
204
205 async fn run_with_cargo_watch(&self, run_args: &[&str], wait: bool) -> ServerManagerResult {
216 (self.get_start_hook())().await;
217 let cargo_watch_installed: Output = Command::new("cargo")
218 .arg("install")
219 .arg("--list")
220 .output()?;
221 if !String::from_utf8_lossy(&cargo_watch_installed.stdout).contains("cargo-watch") {
222 eprintln!("Cargo-watch not found. Attempting to install...");
223 let install_status: ExitStatus = Command::new("cargo")
224 .arg("install")
225 .arg("cargo-watch")
226 .stdout(Stdio::inherit())
227 .stderr(Stdio::inherit())
228 .spawn()?
229 .wait()?;
230 if !install_status.success() {
231 return Err("Failed to install cargo-watch. Please install it manually: `cargo install cargo-watch`".into());
232 }
233 eprintln!("Cargo-watch installed successfully.");
234 }
235 let mut command: Command = Command::new("cargo-watch");
236 command
237 .args(run_args)
238 .stdout(Stdio::inherit())
239 .stderr(Stdio::inherit())
240 .stdin(Stdio::inherit());
241 let mut child: Child = command
242 .spawn()
243 .map_err(|error: Error| Box::new(error) as Box<dyn std::error::Error>)?;
244 if wait {
245 child
246 .wait()
247 .map_err(|error: Error| Box::new(error) as Box<dyn std::error::Error>)?;
248 }
249 exit(0);
250 }
251
252 pub async fn watch_detached(&self, run_args: &[&str]) -> ServerManagerResult {
264 self.run_with_cargo_watch(run_args, false).await
265 }
266
267 pub async fn watch(&self, run_args: &[&str]) -> ServerManagerResult {
279 self.run_with_cargo_watch(run_args, true).await
280 }
281}