server_manager/manager/impl.rs
1use crate::*;
2
3impl<F, Fut> ServerManager<F>
4where
5 F: Fn() -> Fut,
6 Fut: std::future::Future<Output = ()>,
7{
8 /// Title: Create a new ServerManager instance
9 ///
10 /// Parameters:
11 /// - `config`: The server configuration containing PID file path and log paths.
12 /// - `server_fn`: A closure representing the asynchronous server function.
13 ///
14 /// Returns:
15 /// - `ServerManager<F>`: A new instance of ServerManager.
16 pub fn new(config: ServerManagerConfig, server_fn: F) -> Self {
17 Self { config, server_fn }
18 }
19
20 /// Title: Start the server in foreground mode
21 ///
22 /// Parameters:
23 /// - None
24 ///
25 /// Returns:
26 /// - `()`: No return value.
27 ///
28 /// This function writes the current process ID to the PID file specified in the configuration
29 /// and then runs the server function asynchronously.
30 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 /// Title: Stop the server
39 ///
40 /// Parameters:
41 /// - None
42 ///
43 /// Returns:
44 /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
45 ///
46 /// This function reads the process ID from the PID file and attempts to kill the process using a SIGTERM signal.
47 pub fn stop(&self) -> Result<(), Box<dyn std::error::Error>> {
48 let pid: i32 = self.read_pid_file()?;
49 self.kill_process(pid)
50 }
51
52 /// Start the server in daemon (background) mode on Unix platforms.
53 /// Title: Start the server in daemon mode on non-Unix platforms
54 ///
55 /// Parameters:
56 /// - None
57 ///
58 /// Returns:
59 /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
60 ///
61 /// This function returns an error because daemon mode is not supported on non-Unix platforms.
62 #[cfg(not(windows))]
63 pub fn start_daemon(&self) -> Result<(), Box<dyn std::error::Error>> {
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 /// Start the server in daemon (background) mode on Windows platforms
85 /// Title: Start the server in daemon mode on Windows platforms
86 ///
87 /// Parameters:
88 /// - None
89 ///
90 /// Returns:
91 /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
92 ///
93 /// This function starts a detached process on Windows using Windows API.
94 pub fn start_daemon(&self) -> Result<(), Box<dyn std::error::Error>> {
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 /// Title: Read process ID from the PID file
117 ///
118 /// Parameters:
119 /// - None
120 ///
121 /// Returns:
122 /// - `Result<i32, Box<dyn std::error::Error>>`: The process ID if successful.
123 ///
124 /// This function reads the content of the PID file specified in the configuration and parses it as an integer.
125 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 /// Title: Write current process ID to the PID file
132 ///
133 /// Parameters:
134 /// - None
135 ///
136 /// Returns:
137 /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
138 ///
139 /// This function obtains the current process ID and writes it as a string to the PID file specified in the configuration.
140 fn write_pid_file(&self) -> Result<(), Box<dyn std::error::Error>> {
141 if let Some(parent) = std::path::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 /// Title: Kill process by PID on Unix platforms
150 ///
151 /// Parameters:
152 /// - `pid`: The process ID to kill.
153 ///
154 /// Returns:
155 /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
156 ///
157 /// This function sends a SIGTERM signal to the process with the given PID using libc::kill.
158 #[cfg(not(windows))]
159 fn kill_process(&self, pid: i32) -> Result<(), Box<dyn std::error::Error>> {
160 let result: Result<std::process::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 /// Kill process by PID on Windows platforms
178 /// Title: Kill process by PID on Windows platforms
179 ///
180 /// Parameters:
181 /// - ``pid``: The process ID to kill.
182 ///
183 /// Returns:
184 /// - ``Result<(), Box<dyn std::error::Error>>``: Operation result.
185 ///
186 /// This function attempts to kill the process with the given PID using Windows API.
187 /// If opening or terminating the process fails, the detailed error code is returned.
188 fn kill_process(&self, pid: i32) -> Result<(), Box<dyn std::error::Error>> {
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}