|
1 | 1 | use std::{ |
2 | 2 | fs::{File, Permissions}, |
3 | 3 | os::unix::fs::PermissionsExt, |
4 | | - path::Path, |
| 4 | + path::{Path, PathBuf}, |
5 | 5 | }; |
6 | 6 |
|
7 | 7 | use daemonize::Daemonize; |
8 | 8 |
|
9 | 9 | use crate::{server, Args}; |
10 | 10 |
|
11 | | -const PID_PATH: &str = "/var/run/pingly.pid"; |
| 11 | +const DEFAULT_PID_PATH: &str = "/var/run/pingly.pid"; |
12 | 12 | const DEFAULT_STDOUT_PATH: &str = "/var/run/pingly.out"; |
13 | 13 | const DEFAULT_STDERR_PATH: &str = "/var/run/pingly.err"; |
14 | 14 |
|
15 | | -/// Get the pid of the daemon |
16 | | -fn get_pid() -> Option<String> { |
17 | | - if let Ok(data) = std::fs::read(PID_PATH) { |
18 | | - let binding = String::from_utf8(data).expect("pid file is not utf8"); |
19 | | - return Some(binding.trim().to_string()); |
20 | | - } |
21 | | - None |
| 15 | +pub struct Daemon { |
| 16 | + pid_file: PathBuf, |
| 17 | + stdout_file: PathBuf, |
| 18 | + stderr_file: PathBuf, |
22 | 19 | } |
23 | 20 |
|
24 | | -/// Check if the current user is root |
25 | | -pub fn root() { |
26 | | - if !nix::unistd::Uid::effective().is_root() { |
27 | | - println!("You must run this executable with root permissions"); |
28 | | - std::process::exit(-1) |
| 21 | +impl Default for Daemon { |
| 22 | + fn default() -> Self { |
| 23 | + Daemon { |
| 24 | + pid_file: PathBuf::from(DEFAULT_PID_PATH), |
| 25 | + stdout_file: PathBuf::from(DEFAULT_STDOUT_PATH), |
| 26 | + stderr_file: PathBuf::from(DEFAULT_STDERR_PATH), |
| 27 | + } |
29 | 28 | } |
30 | 29 | } |
31 | 30 |
|
32 | | -/// Start the daemon |
33 | | -pub fn start(config: Args) -> crate::Result<()> { |
34 | | - if let Some(pid) = get_pid() { |
35 | | - println!("pingly is already running with pid: {pid}"); |
36 | | - return Ok(()); |
| 31 | +impl Daemon { |
| 32 | + /// Get the pid of the daemon |
| 33 | + fn get_pid(&self) -> crate::Result<Option<String>> { |
| 34 | + if let Ok(data) = std::fs::read(&self.pid_file) { |
| 35 | + let binding = String::from_utf8(data)?; |
| 36 | + return Ok(Some(binding.trim().to_string())); |
| 37 | + } |
| 38 | + Ok(None) |
37 | 39 | } |
38 | 40 |
|
39 | | - root(); |
40 | | - |
41 | | - let pid_file = File::create(PID_PATH)?; |
42 | | - pid_file.set_permissions(Permissions::from_mode(0o755))?; |
43 | | - |
44 | | - let stdout = File::create(DEFAULT_STDOUT_PATH)?; |
45 | | - stdout.set_permissions(Permissions::from_mode(0o755))?; |
46 | | - |
47 | | - let stderr = File::create(DEFAULT_STDERR_PATH)?; |
48 | | - stdout.set_permissions(Permissions::from_mode(0o755))?; |
49 | | - |
50 | | - let mut daemonize = Daemonize::new() |
51 | | - .pid_file(PID_PATH) // Every method except `new` and `start` |
52 | | - .chown_pid_file(true) // is optional, see `Daemonize` documentation |
53 | | - .umask(0o777) // Set umask, `0o027` by default. |
54 | | - .stdout(stdout) // Redirect stdout to `/tmp/daemon.out`. |
55 | | - .stderr(stderr) // Redirect stderr to `/tmp/daemon.err`. |
56 | | - .privileged_action(|| "Executed before drop privileges"); |
57 | | - |
58 | | - if let Ok(user) = std::env::var("SUDO_USER") { |
59 | | - if let Ok(Some(real_user)) = nix::unistd::User::from_name(&user) { |
60 | | - daemonize = daemonize |
61 | | - .user(real_user.name.as_str()) |
62 | | - .group(real_user.gid.as_raw()); |
| 41 | + /// Check if the current user is root |
| 42 | + fn check_root(&self) { |
| 43 | + if !nix::unistd::Uid::effective().is_root() { |
| 44 | + println!("You must run this executable with root permissions"); |
| 45 | + std::process::exit(-1) |
63 | 46 | } |
64 | 47 | } |
65 | 48 |
|
66 | | - if let Some(err) = daemonize.start().err() { |
67 | | - eprintln!("Error: {err}"); |
68 | | - std::process::exit(-1) |
69 | | - } |
| 49 | + /// Start the daemon |
| 50 | + pub fn start(&self, config: Args) -> crate::Result<()> { |
| 51 | + if let Some(pid) = self.get_pid()? { |
| 52 | + println!("pingly is already running with pid: {pid}"); |
| 53 | + return Ok(()); |
| 54 | + } |
70 | 55 |
|
71 | | - server::run(config) |
72 | | -} |
| 56 | + self.check_root(); |
| 57 | + |
| 58 | + let pid_file = File::create(&self.pid_file)?; |
| 59 | + pid_file.set_permissions(Permissions::from_mode(0o755))?; |
73 | 60 |
|
74 | | -/// Stop the daemon |
75 | | -pub fn stop() -> crate::Result<()> { |
76 | | - use nix::{sys::signal, unistd::Pid}; |
| 61 | + let stdout = File::create(&self.stdout_file)?; |
| 62 | + stdout.set_permissions(Permissions::from_mode(0o755))?; |
77 | 63 |
|
78 | | - root(); |
| 64 | + let stderr = File::create(&self.stderr_file)?; |
| 65 | + stderr.set_permissions(Permissions::from_mode(0o755))?; |
79 | 66 |
|
80 | | - if let Some(pid) = get_pid() { |
81 | | - let pid = pid.parse::<i32>()?; |
82 | | - for _ in 0..360 { |
83 | | - if signal::kill(Pid::from_raw(pid), signal::SIGINT).is_err() { |
84 | | - break; |
| 67 | + let mut daemonize = Daemonize::new() |
| 68 | + .pid_file(&self.pid_file) |
| 69 | + .chown_pid_file(true) |
| 70 | + .umask(0o777) |
| 71 | + .stdout(stdout) |
| 72 | + .stderr(stderr) |
| 73 | + .privileged_action(|| "Executed before drop privileges"); |
| 74 | + |
| 75 | + if let Ok(user) = std::env::var("SUDO_USER") { |
| 76 | + if let Ok(Some(real_user)) = nix::unistd::User::from_name(&user) { |
| 77 | + daemonize = daemonize |
| 78 | + .user(real_user.name.as_str()) |
| 79 | + .group(real_user.gid.as_raw()); |
85 | 80 | } |
86 | | - std::thread::sleep(std::time::Duration::from_secs(1)) |
87 | 81 | } |
88 | | - let _ = std::fs::remove_file(PID_PATH); |
| 82 | + |
| 83 | + if let Some(err) = daemonize.start().err() { |
| 84 | + eprintln!("Error: {err}"); |
| 85 | + std::process::exit(-1) |
| 86 | + } |
| 87 | + |
| 88 | + server::run(config) |
89 | 89 | } |
90 | 90 |
|
91 | | - Ok(()) |
92 | | -} |
| 91 | + /// Stop the daemon |
| 92 | + pub fn stop(&self) -> crate::Result<()> { |
| 93 | + use nix::{sys::signal, unistd::Pid}; |
93 | 94 |
|
94 | | -/// Restart the daemon |
95 | | -pub fn restart(config: Args) -> crate::Result<()> { |
96 | | - stop()?; |
97 | | - start(config) |
98 | | -} |
| 95 | + self.check_root(); |
99 | 96 |
|
100 | | -/// Show the status of the daemon |
101 | | -pub fn status() -> crate::Result<()> { |
102 | | - match get_pid() { |
103 | | - None => println!("pingly is not running"), |
104 | | - Some(pid) => { |
105 | | - let mut sys = sysinfo::System::new(); |
106 | | - |
107 | | - // First, we update all information of our `System` struct. |
108 | | - sys.refresh_all(); |
109 | | - |
110 | | - // Display processes ID |
111 | | - for (raw_pid, process) in sys.processes().iter() { |
112 | | - if raw_pid.as_u32().eq(&(pid.parse::<u32>()?)) { |
113 | | - println!("{:<6} {:<6} {:<6}", "PID", "CPU(%)", "MEM(MB)"); |
114 | | - println!( |
115 | | - "{:<6} {:<6.1} {:<6.1}", |
116 | | - raw_pid, |
117 | | - process.cpu_usage(), |
118 | | - (process.memory() as f64) / 1024.0 / 1024.0 |
119 | | - ); |
| 97 | + if let Some(pid) = self.get_pid()? { |
| 98 | + let pid = pid.parse::<i32>()?; |
| 99 | + for _ in 0..360 { |
| 100 | + if signal::kill(Pid::from_raw(pid), signal::SIGINT).is_err() { |
| 101 | + break; |
120 | 102 | } |
| 103 | + std::thread::sleep(std::time::Duration::from_secs(1)) |
121 | 104 | } |
122 | 105 | } |
| 106 | + |
| 107 | + std::fs::remove_file(&self.pid_file)?; |
| 108 | + |
| 109 | + Ok(()) |
123 | 110 | } |
124 | | - Ok(()) |
125 | | -} |
126 | 111 |
|
127 | | -/// Show the log of the daemon |
128 | | -pub fn log() -> crate::Result<()> { |
129 | | - fn read_and_print_file(file_path: &Path, placeholder: &str) -> crate::Result<()> { |
130 | | - if !file_path.exists() { |
131 | | - return Ok(()); |
132 | | - } |
| 112 | + /// Restart the daemon |
| 113 | + pub fn restart(&self, config: Args) -> crate::Result<()> { |
| 114 | + self.stop()?; |
| 115 | + self.start(config) |
| 116 | + } |
133 | 117 |
|
134 | | - // Check if the file is empty before opening it |
135 | | - let metadata = std::fs::metadata(file_path)?; |
136 | | - if metadata.len() == 0 { |
137 | | - return Ok(()); |
| 118 | + /// Show the status of the daemon |
| 119 | + pub fn status(&self) -> crate::Result<()> { |
| 120 | + match self.get_pid()? { |
| 121 | + None => println!("pingly is not running"), |
| 122 | + Some(pid) => { |
| 123 | + let mut sys = sysinfo::System::new(); |
| 124 | + sys.refresh_all(); |
| 125 | + |
| 126 | + for (raw_pid, process) in sys.processes().iter() { |
| 127 | + if raw_pid.as_u32().eq(&(pid.parse::<u32>()?)) { |
| 128 | + println!("{:<6} {:<6} {:<6}", "PID", "CPU(%)", "MEM(MB)"); |
| 129 | + println!( |
| 130 | + "{:<6} {:<6.1} {:<6.1}", |
| 131 | + raw_pid, |
| 132 | + process.cpu_usage(), |
| 133 | + (process.memory() as f64) / 1024.0 / 1024.0 |
| 134 | + ); |
| 135 | + } |
| 136 | + } |
| 137 | + } |
138 | 138 | } |
| 139 | + Ok(()) |
| 140 | + } |
139 | 141 |
|
140 | | - let file = File::open(file_path)?; |
141 | | - let reader = std::io::BufReader::new(file); |
142 | | - let mut start = true; |
| 142 | + /// Show the log of the daemon |
| 143 | + pub fn log(&self) -> crate::Result<()> { |
| 144 | + fn read_and_print_file(file_path: &Path, placeholder: &str) -> crate::Result<()> { |
| 145 | + if !file_path.exists() { |
| 146 | + return Ok(()); |
| 147 | + } |
143 | 148 |
|
144 | | - use std::io::BufRead; |
| 149 | + let metadata = std::fs::metadata(file_path)?; |
| 150 | + if metadata.len() == 0 { |
| 151 | + return Ok(()); |
| 152 | + } |
145 | 153 |
|
146 | | - for line in reader.lines() { |
147 | | - if let Ok(content) = line { |
148 | | - if start { |
149 | | - start = false; |
150 | | - println!("{placeholder}"); |
| 154 | + let file = File::open(file_path)?; |
| 155 | + let reader = std::io::BufReader::new(file); |
| 156 | + let mut start = true; |
| 157 | + |
| 158 | + use std::io::BufRead; |
| 159 | + |
| 160 | + for line in reader.lines() { |
| 161 | + if let Ok(content) = line { |
| 162 | + if start { |
| 163 | + start = false; |
| 164 | + println!("{placeholder}"); |
| 165 | + } |
| 166 | + println!("{content}"); |
| 167 | + } else if let Err(err) = line { |
| 168 | + eprintln!("Error reading line: {err}"); |
151 | 169 | } |
152 | | - println!("{content}"); |
153 | | - } else if let Err(err) = line { |
154 | | - eprintln!("Error reading line: {err}"); |
155 | 170 | } |
| 171 | + |
| 172 | + Ok(()) |
156 | 173 | } |
157 | 174 |
|
| 175 | + read_and_print_file(&self.stdout_file, "STDOUT>")?; |
| 176 | + read_and_print_file(&self.stderr_file, "STDERR>")?; |
| 177 | + |
158 | 178 | Ok(()) |
159 | 179 | } |
160 | | - |
161 | | - let stdout_path = Path::new(DEFAULT_STDOUT_PATH); |
162 | | - read_and_print_file(stdout_path, "STDOUT>")?; |
163 | | - |
164 | | - let stderr_path = Path::new(DEFAULT_STDERR_PATH); |
165 | | - read_and_print_file(stderr_path, "STDERR>")?; |
166 | | - |
167 | | - Ok(()) |
168 | 180 | } |
0 commit comments