server_manager/manager/
impl.rs1use crate::*;
2
3impl<F, Fut> ServerManager<F>
7where
8 F: Fn() -> Fut,
9 Fut: std::future::Future<Output = ()>,
10{
11 pub fn new(config: ServerManagerConfig, server_fn: F) -> Self {
22 Self { config, server_fn }
23 }
24
25 pub async fn start(&self) {
29 if let Err(e) = self.write_pid_file() {
30 eprintln!("Failed to write pid file: {}", e);
31 return;
32 }
33 (self.server_fn)().await;
34 }
35
36 pub fn stop(&self) -> ServerManagerResult {
44 let pid: i32 = self.read_pid_file()?;
45 self.kill_process(pid)
46 }
47
48 #[cfg(not(windows))]
50 pub fn start_daemon(&self) -> ServerManagerResult {
51 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
52 self.write_pid_file()?;
53 let rt: Runtime = Runtime::new()?;
54 rt.block_on(async {
55 (self.server_fn)().await;
56 });
57 return Ok(());
58 }
59 let exe_path: PathBuf = std::env::current_exe()?;
60 let mut cmd: Command = Command::new(exe_path);
61 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
62 .stdout(Stdio::null())
63 .stderr(Stdio::null())
64 .stdin(Stdio::null());
65 cmd.spawn()
66 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
67 Ok(())
68 }
69
70 #[cfg(windows)]
72 pub fn start_daemon(&self) -> ServerManagerResult {
73 use std::os::windows::process::CommandExt;
74 if std::env::var(RUNNING_AS_DAEMON).is_ok() {
75 self.write_pid_file()?;
76 let rt: Runtime = Runtime::new()?;
77 rt.block_on(async {
78 (self.server_fn)().await;
79 });
80 return Ok(());
81 }
82 let exe_path: PathBuf = std::env::current_exe()?;
83 let mut cmd: Command = Command::new(exe_path);
84 cmd.env(RUNNING_AS_DAEMON, RUNNING_AS_DAEMON_VALUE)
85 .stdout(Stdio::null())
86 .stderr(Stdio::null())
87 .stdin(Stdio::null())
88 .creation_flags(0x00000008);
89 cmd.spawn()
90 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
91 Ok(())
92 }
93
94 fn read_pid_file(&self) -> Result<i32, Box<dyn std::error::Error>> {
100 let pid_str: String = fs::read_to_string(&self.config.pid_file)?;
101 let pid: i32 = pid_str.trim().parse::<i32>()?;
102 Ok(pid)
103 }
104
105 fn write_pid_file(&self) -> ServerManagerResult {
111 if let Some(parent) = Path::new(&self.config.pid_file).parent() {
112 fs::create_dir_all(parent)?;
113 }
114 let pid: u32 = id();
115 fs::write(&self.config.pid_file, pid.to_string())?;
116 Ok(())
117 }
118
119 #[cfg(not(windows))]
129 fn kill_process(&self, pid: i32) -> ServerManagerResult {
130 let result: Result<Output, std::io::Error> = Command::new("kill")
131 .arg("-TERM")
132 .arg(pid.to_string())
133 .output();
134 match result {
135 Ok(output) if output.status.success() => Ok(()),
136 Ok(output) => Err(format!(
137 "Failed to kill process with pid: {}, error: {}",
138 pid,
139 String::from_utf8_lossy(&output.stderr)
140 )
141 .into()),
142 Err(e) => Err(format!("Failed to execute kill command: {}", e).into()),
143 }
144 }
145
146 #[cfg(windows)]
156 fn kill_process(&self, pid: i32) -> ServerManagerResult {
157 use std::ffi::c_void;
158 type DWORD = u32;
159 type BOOL = i32;
160 type HANDLE = *mut c_void;
161 type UINT = u32;
162 const PROCESS_TERMINATE: DWORD = 0x0001;
163 const PROCESS_ALL_ACCESS: DWORD = 0x1F0FFF;
164 unsafe extern "system" {
165 fn OpenProcess(
166 dwDesiredAccess: DWORD,
167 bInheritHandle: BOOL,
168 dwProcessId: DWORD,
169 ) -> HANDLE;
170 fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL;
171 fn CloseHandle(hObject: HANDLE) -> BOOL;
172 fn GetLastError() -> DWORD;
173 }
174 let process_id: DWORD = pid as DWORD;
175 let mut process_handle: HANDLE = unsafe { OpenProcess(PROCESS_TERMINATE, 0, process_id) };
176 if process_handle.is_null() {
177 process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, process_id) };
178 }
179 if process_handle.is_null() {
180 let error_code = unsafe { GetLastError() };
181 return Err(format!(
182 "Failed to open process with pid: {}. Error code: {}",
183 pid, error_code
184 )
185 .into());
186 }
187 let terminate_result: BOOL = unsafe { TerminateProcess(process_handle, 1) };
188 if terminate_result == 0 {
189 let error_code = unsafe { GetLastError() };
190 unsafe {
191 CloseHandle(process_handle);
192 }
193 return Err(format!(
194 "Failed to terminate process with pid: {}. Error code: {}",
195 pid, error_code
196 )
197 .into());
198 }
199 unsafe {
200 CloseHandle(process_handle);
201 }
202 Ok(())
203 }
204
205 fn run_with_cargo_watch(&self, run_args: &[&str], wait: bool) -> ServerManagerResult {
216 let cargo_watch_installed: Output = Command::new("cargo")
217 .arg("install")
218 .arg("--list")
219 .output()?;
220 if !String::from_utf8_lossy(&cargo_watch_installed.stdout).contains("cargo-watch") {
221 eprintln!("Cargo-watch not found. Attempting to install...");
222 let install_status: ExitStatus = Command::new("cargo")
223 .arg("install")
224 .arg("cargo-watch")
225 .stdout(Stdio::inherit())
226 .stderr(Stdio::inherit())
227 .spawn()?
228 .wait()?;
229 if !install_status.success() {
230 return Err("Failed to install cargo-watch. Please install it manually: `cargo install cargo-watch`".into());
231 }
232 eprintln!("Cargo-watch installed successfully.");
233 }
234 let mut command: Command = Command::new("cargo-watch");
235 command
236 .args(run_args)
237 .stdout(Stdio::inherit())
238 .stderr(Stdio::inherit())
239 .stdin(Stdio::inherit());
240 let mut child: Child = command
241 .spawn()
242 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
243 if wait {
244 child
245 .wait()
246 .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
247 }
248 exit(0);
249 }
250
251 pub fn hot_restart(&self, run_args: &[&str]) -> ServerManagerResult {
261 self.run_with_cargo_watch(run_args, false)
262 }
263
264 pub fn hot_restart_wait(&self, run_args: &[&str]) -> ServerManagerResult {
274 self.run_with_cargo_watch(run_args, true)
275 }
276}