server_manager/manager/
impl.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use crate::*;


impl<F, Fut> ServerManager<F>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = ()>,
{
    /// Title: Create a new ServerManager instance
    ///
    /// Parameters:
    /// - `config`: The server configuration containing PID file path and log paths.
    /// - `server_fn`: A closure representing the asynchronous server function.
    ///
    /// Returns:
    /// - `ServerManager<F>`: A new instance of ServerManager.
    pub fn new(config: ServerManagerConfig, server_fn: F) -> Self {
        Self { config, server_fn }
    }

    /// Title: Start the server in foreground mode
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `()`: No return value.
    ///
    /// This function writes the current process ID to the PID file specified in the configuration
    /// and then runs the server function asynchronously.
    pub async fn start(&self) {
        if let Err(e) = self.write_pid_file() {
            eprintln!("Failed to write pid file: {}", e);
            return;
        }
        (self.server_fn)().await;
    }

    /// Title: Stop the server
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function reads the process ID from the PID file and attempts to kill the process using a SIGTERM signal.
    pub fn stop(&self) -> Result<(), Box<dyn Error>> {
        let pid: i32 = self.read_pid_file()?;
        self.kill_process(pid)
    }

    /// Title: Start the server in daemon (background) mode on Unix platforms
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function uses the daemonize crate to run the server process in the background. It configures
    /// the PID file, stdout log, and stderr log paths from the configuration.
    #[cfg(unix)]
    pub fn start_daemon(&self) -> Result<(), Box<dyn Error>> {
        let stdout_file = fs::File::create(&self.config.stdout_log)?;
        let stderr_file = fs::File::create(&self.config.stderr_log)?;
        let daemonize_obj = Daemonize::new()
            .pid_file(&self.config.pid_file)
            .chown_pid_file(true)
            .working_directory(".")
            .umask(0o027)
            .stdout(stdout_file)
            .stderr(stderr_file);
        daemonize_obj.start()?;
        let rt = tokio::runtime::Runtime::new()?;
        rt.block_on(async {
            (self.server_fn)().await;
        });
        Ok(())
    }

    /// Title: Start the server in daemon mode on non-Unix platforms
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function returns an error because daemon mode is not supported on non-Unix platforms.
    #[cfg(not(unix))]
    pub fn start_daemon(&self) -> Result<(), Box<dyn Error>> {
        Err("Daemon mode is not supported on non-Unix platforms".into())
    }

    /// Title: Read process ID from the PID file
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `Result<i32, Box<dyn std::error::Error>>`: The process ID if successful.
    ///
    /// This function reads the content of the PID file specified in the configuration and parses it as an integer.
    fn read_pid_file(&self) -> Result<i32, Box<dyn Error>> {
        let pid_str: String = fs::read_to_string(&self.config.pid_file)?;
        let pid: i32 = pid_str.trim().parse::<i32>()?;
        Ok(pid)
    }

    /// Title: Write current process ID to the PID file
    ///
    /// Parameters:
    /// - None
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function obtains the current process ID and writes it as a string to the PID file specified in the configuration.
    fn write_pid_file(&self) -> Result<(), Box<dyn Error>> {
        let pid: u32 = process::id();
        fs::write(&self.config.pid_file, pid.to_string())?;
        Ok(())
    }

    /// Title: Kill process by PID on Unix platforms
    ///
    /// Parameters:
    /// - `pid`: The process ID to kill.
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function sends a SIGTERM signal to the process with the given PID using libc::kill.
    #[cfg(unix)]
    fn kill_process(&self, pid: i32) -> Result<(), Box<dyn Error>> {
        let result = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
        if result == 0 {
            Ok(())
        } else {
            Err(format!("Failed to kill process with pid: {}", pid).into())
        }
    }

    /// Title: Kill process by PID on non-Unix platforms
    ///
    /// Parameters:
    /// - `pid`: The process ID to kill.
    ///
    /// Returns:
    /// - `Result<(), Box<dyn std::error::Error>>`: Operation result.
    ///
    /// This function returns an error because killing a process is not supported on non-Unix platforms.
    #[cfg(not(unix))]
    fn kill_process(&self, _pid: i32) -> Result<(), Box<dyn Error>> {
        Err("kill_process is not supported on non-Unix platforms".into())
    }
}