server_manager/manager/
impl.rs1use crate::*;
2
3impl<F, Fut> ServerManager<F>
4where
5 F: Fn() -> Fut,
6 Fut: std::future::Future<Output = ()>,
7{
8 pub fn new(config: ServerManagerConfig, server_fn: F) -> Self {
17 Self { config, server_fn }
18 }
19
20 pub async fn start(&self) {
31 if let Err(e) = self.write_pid_file() {
32 eprintln!("Failed to write pid file: {}", e);
33 return;
34 }
35 (self.server_fn)().await;
36 }
37
38 pub fn stop(&self) -> ServerManagerResult {
48 let pid: i32 = self.read_pid_file()?;
49 self.kill_process(pid)
50 }
51
52 #[cfg(not(windows))]
63 pub fn start_daemon(&self) -> ServerManagerResult {
64 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
65 self.write_pid_file()?;
66 let rt: Runtime = Runtime::new()?;
67 rt.block_on(async {
68 (self.server_fn)().await;
69 });
70 return Ok(());
71 }
72 let exe_path: PathBuf = std::env::current_exe()?;
73 let mut cmd: Command = Command::new(exe_path);
74 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
75 .stdout(Stdio::null())
76 .stderr(Stdio::null())
77 .stdin(Stdio::null());
78 cmd.spawn()
79 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
80 Ok(())
81 }
82
83 #[cfg(windows)]
84 pub fn start_daemon(&self) -> ServerManagerResult {
95 use std::os::windows::process::CommandExt;
96 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
97 self.write_pid_file()?;
98 let rt: Runtime = Runtime::new()?;
99 rt.block_on(async {
100 (self.server_fn)().await;
101 });
102 return Ok(());
103 }
104 let exe_path: PathBuf = std::env::current_exe()?;
105 let mut cmd: Command = Command::new(exe_path);
106 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
107 .stdout(Stdio::null())
108 .stderr(Stdio::null())
109 .stdin(Stdio::null())
110 .creation_flags(0x00000008);
111 cmd.spawn()
112 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
113 Ok(())
114 }
115
116 fn read_pid_file(&self) -> Result<i32, Box<dyn std::error::Error>> {
126 let pid_str: String = fs::read_to_string(&self.config.pid_file)?;
127 let pid: i32 = pid_str.trim().parse::<i32>()?;
128 Ok(pid)
129 }
130
131 fn write_pid_file(&self) -> ServerManagerResult {
141 if let Some(parent) = Path::new(&self.config.pid_file).parent() {
142 fs::create_dir_all(parent)?;
143 }
144 let pid: u32 = id();
145 fs::write(&self.config.pid_file, pid.to_string())?;
146 Ok(())
147 }
148
149 #[cfg(not(windows))]
159 fn kill_process(&self, pid: i32) -> ServerManagerResult {
160 let result: Result<Output, std::io::Error> = Command::new("kill")
161 .arg("-TERM")
162 .arg(pid.to_string())
163 .output();
164 match result {
165 Ok(output) if output.status.success() => Ok(()),
166 Ok(output) => Err(format!(
167 "Failed to kill process with pid: {}, error: {}",
168 pid,
169 String::from_utf8_lossy(&output.stderr)
170 )
171 .into()),
172 Err(e) => Err(format!("Failed to execute kill command: {}", e).into()),
173 }
174 }
175
176 #[cfg(windows)]
177 fn kill_process(&self, pid: i32) -> ServerManagerResult {
189 use std::ffi::c_void;
190 type DWORD = u32;
191 type BOOL = i32;
192 type HANDLE = *mut c_void;
193 type UINT = u32;
194 const PROCESS_TERMINATE: DWORD = 0x0001;
195 const PROCESS_ALL_ACCESS: DWORD = 0x1F0FFF;
196 unsafe extern "system" {
197 fn OpenProcess(
198 dwDesiredAccess: DWORD,
199 bInheritHandle: BOOL,
200 dwProcessId: DWORD,
201 ) -> HANDLE;
202 fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL;
203 fn CloseHandle(hObject: HANDLE) -> BOOL;
204 fn GetLastError() -> DWORD;
205 }
206 let process_id: DWORD = pid as DWORD;
207 let mut process_handle: HANDLE = unsafe { OpenProcess(PROCESS_TERMINATE, 0, process_id) };
208 if process_handle.is_null() {
209 process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, process_id) };
210 }
211 if process_handle.is_null() {
212 let error_code = unsafe { GetLastError() };
213 return Err(format!(
214 "Failed to open process with pid: {}. Error code: {}",
215 pid, error_code
216 )
217 .into());
218 }
219 let terminate_result: BOOL = unsafe { TerminateProcess(process_handle, 1) };
220 if terminate_result == 0 {
221 let error_code = unsafe { GetLastError() };
222 unsafe {
223 CloseHandle(process_handle);
224 }
225 return Err(format!(
226 "Failed to terminate process with pid: {}. Error code: {}",
227 pid, error_code
228 )
229 .into());
230 }
231 unsafe {
232 CloseHandle(process_handle);
233 }
234 Ok(())
235 }
236
237 fn run_with_cargo_watch(&self, run_args: &[&str], wait: bool) -> ServerManagerResult {
246 let cargo_watch_installed: Output = Command::new("cargo")
247 .arg("install")
248 .arg("--list")
249 .output()?;
250 if !String::from_utf8_lossy(&cargo_watch_installed.stdout).contains("cargo-watch") {
251 eprintln!("Cargo-watch not found. Attempting to install...");
252 let install_status: ExitStatus = Command::new("cargo")
253 .arg("install")
254 .arg("cargo-watch")
255 .stdout(Stdio::inherit())
256 .stderr(Stdio::inherit())
257 .spawn()?
258 .wait()?;
259 if !install_status.success() {
260 return Err("Failed to install cargo-watch. Please install it manually: `cargo install cargo-watch`".into());
261 }
262 eprintln!("Cargo-watch installed successfully.");
263 }
264 let mut command: Command = Command::new("cargo-watch");
265 command
266 .args(run_args)
267 .stdout(Stdio::inherit())
268 .stderr(Stdio::inherit())
269 .stdin(Stdio::inherit());
270 let mut child: Child = command
271 .spawn()
272 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
273 if wait {
274 child
275 .wait()
276 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
277 }
278 exit(0);
279 }
280
281 pub fn hot_restart(&self, run_args: &[&str]) -> ServerManagerResult {
292 self.run_with_cargo_watch(run_args, false)
293 }
294
295 pub fn hot_restart_wait(&self, run_args: &[&str]) -> ServerManagerResult {
305 self.run_with_cargo_watch(run_args, true)
306 }
307}