Skip to content

Commit 14cdaef

Browse files
authored
refactor(daemon): abstract daemon operations into Daemon struct (#26)
1 parent 7b2530f commit 14cdaef

File tree

4 files changed

+144
-128
lines changed

4 files changed

+144
-128
lines changed

src/daemon.rs

Lines changed: 131 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,168 +1,180 @@
11
use std::{
22
fs::{File, Permissions},
33
os::unix::fs::PermissionsExt,
4-
path::Path,
4+
path::{Path, PathBuf},
55
};
66

77
use daemonize::Daemonize;
88

99
use crate::{server, Args};
1010

11-
const PID_PATH: &str = "/var/run/pingly.pid";
11+
const DEFAULT_PID_PATH: &str = "/var/run/pingly.pid";
1212
const DEFAULT_STDOUT_PATH: &str = "/var/run/pingly.out";
1313
const DEFAULT_STDERR_PATH: &str = "/var/run/pingly.err";
1414

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,
2219
}
2320

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+
}
2928
}
3029
}
3130

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)
3739
}
3840

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)
6346
}
6447
}
6548

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+
}
7055

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))?;
7360

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))?;
7763

78-
root();
64+
let stderr = File::create(&self.stderr_file)?;
65+
stderr.set_permissions(Permissions::from_mode(0o755))?;
7966

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());
8580
}
86-
std::thread::sleep(std::time::Duration::from_secs(1))
8781
}
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)
8989
}
9090

91-
Ok(())
92-
}
91+
/// Stop the daemon
92+
pub fn stop(&self) -> crate::Result<()> {
93+
use nix::{sys::signal, unistd::Pid};
9394

94-
/// Restart the daemon
95-
pub fn restart(config: Args) -> crate::Result<()> {
96-
stop()?;
97-
start(config)
98-
}
95+
self.check_root();
9996

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;
120102
}
103+
std::thread::sleep(std::time::Duration::from_secs(1))
121104
}
122105
}
106+
107+
std::fs::remove_file(&self.pid_file)?;
108+
109+
Ok(())
123110
}
124-
Ok(())
125-
}
126111

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+
}
133117

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+
}
138138
}
139+
Ok(())
140+
}
139141

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+
}
143148

144-
use std::io::BufRead;
149+
let metadata = std::fs::metadata(file_path)?;
150+
if metadata.len() == 0 {
151+
return Ok(());
152+
}
145153

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}");
151169
}
152-
println!("{content}");
153-
} else if let Err(err) = line {
154-
eprintln!("Error reading line: {err}");
155170
}
171+
172+
Ok(())
156173
}
157174

175+
read_and_print_file(&self.stdout_file, "STDOUT>")?;
176+
read_and_print_file(&self.stderr_file, "STDERR>")?;
177+
158178
Ok(())
159179
}
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(())
168180
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ pub enum Error {
3232

3333
#[error(transparent)]
3434
Join(#[from] tokio::task::JoinError),
35+
36+
#[error(transparent)]
37+
Utf8(#[from] std::string::FromUtf8Error),
3538
}

src/main.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,18 @@ pub enum Commands {
7474

7575
fn main() -> Result<()> {
7676
let opt = Opt::parse();
77+
let daemon = daemon::Daemon::default();
7778
match opt.commands {
7879
Commands::Run(config) => server::run(config),
7980
#[cfg(target_family = "unix")]
80-
Commands::Start(config) => daemon::start(config),
81+
Commands::Start(config) => daemon.start(config),
8182
#[cfg(target_family = "unix")]
82-
Commands::Restart(config) => daemon::restart(config),
83+
Commands::Restart(config) => daemon.restart(config),
8384
#[cfg(target_family = "unix")]
84-
Commands::Stop => daemon::stop(),
85+
Commands::Stop => daemon.stop(),
8586
#[cfg(target_family = "unix")]
86-
Commands::PS => daemon::status(),
87+
Commands::PS => daemon.status(),
8788
#[cfg(target_family = "unix")]
88-
Commands::Log => daemon::log(),
89+
Commands::Log => daemon.log(),
8990
}
9091
}

src/server/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ pub async fn run(args: Args) -> Result<()> {
6464
let router = Router::new()
6565
.route("/api/all", any(track))
6666
.route("/api/tls", any(tls_track))
67-
.route("/api/http1", any(http1_headers))
68-
.route("/api/http2", any(http2_frames))
67+
.route("/api/http1", any(http1_track))
68+
.route("/api/http2", any(http2_track))
6969
.layer(global_layer);
7070

7171
// Signal the server to shutdown using Handle.
@@ -130,7 +130,7 @@ pub async fn tls_track(
130130
}
131131

132132
#[inline]
133-
pub async fn http1_headers(
133+
pub async fn http1_track(
134134
Extension(ConnectInfo(addr)): Extension<ConnectInfo<SocketAddr>>,
135135
Extension(track): Extension<ConnectionTrack>,
136136
req: Request<Body>,
@@ -142,7 +142,7 @@ pub async fn http1_headers(
142142
}
143143

144144
#[inline]
145-
pub async fn http2_frames(
145+
pub async fn http2_track(
146146
Extension(ConnectInfo(addr)): Extension<ConnectInfo<SocketAddr>>,
147147
Extension(track): Extension<ConnectionTrack>,
148148
req: Request<Body>,

0 commit comments

Comments
 (0)