Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 131 additions & 119 deletions src/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,168 +1,180 @@
use std::{
fs::{File, Permissions},
os::unix::fs::PermissionsExt,
path::Path,
path::{Path, PathBuf},
};

use daemonize::Daemonize;

use crate::{server, Args};

const PID_PATH: &str = "/var/run/pingly.pid";
const DEFAULT_PID_PATH: &str = "/var/run/pingly.pid";
const DEFAULT_STDOUT_PATH: &str = "/var/run/pingly.out";
const DEFAULT_STDERR_PATH: &str = "/var/run/pingly.err";

/// Get the pid of the daemon
fn get_pid() -> Option<String> {
if let Ok(data) = std::fs::read(PID_PATH) {
let binding = String::from_utf8(data).expect("pid file is not utf8");
return Some(binding.trim().to_string());
}
None
pub struct Daemon {
pid_file: PathBuf,
stdout_file: PathBuf,
stderr_file: PathBuf,
}

/// Check if the current user is root
pub fn root() {
if !nix::unistd::Uid::effective().is_root() {
println!("You must run this executable with root permissions");
std::process::exit(-1)
impl Default for Daemon {
fn default() -> Self {
Daemon {
pid_file: PathBuf::from(DEFAULT_PID_PATH),
stdout_file: PathBuf::from(DEFAULT_STDOUT_PATH),
stderr_file: PathBuf::from(DEFAULT_STDERR_PATH),
}
}
}

/// Start the daemon
pub fn start(config: Args) -> crate::Result<()> {
if let Some(pid) = get_pid() {
println!("pingly is already running with pid: {pid}");
return Ok(());
impl Daemon {
/// Get the pid of the daemon
fn get_pid(&self) -> crate::Result<Option<String>> {
if let Ok(data) = std::fs::read(&self.pid_file) {
let binding = String::from_utf8(data)?;
return Ok(Some(binding.trim().to_string()));
}
Ok(None)
}

root();

let pid_file = File::create(PID_PATH)?;
pid_file.set_permissions(Permissions::from_mode(0o755))?;

let stdout = File::create(DEFAULT_STDOUT_PATH)?;
stdout.set_permissions(Permissions::from_mode(0o755))?;

let stderr = File::create(DEFAULT_STDERR_PATH)?;
stdout.set_permissions(Permissions::from_mode(0o755))?;

let mut daemonize = Daemonize::new()
.pid_file(PID_PATH) // Every method except `new` and `start`
.chown_pid_file(true) // is optional, see `Daemonize` documentation
.umask(0o777) // Set umask, `0o027` by default.
.stdout(stdout) // Redirect stdout to `/tmp/daemon.out`.
.stderr(stderr) // Redirect stderr to `/tmp/daemon.err`.
.privileged_action(|| "Executed before drop privileges");

if let Ok(user) = std::env::var("SUDO_USER") {
if let Ok(Some(real_user)) = nix::unistd::User::from_name(&user) {
daemonize = daemonize
.user(real_user.name.as_str())
.group(real_user.gid.as_raw());
/// Check if the current user is root
fn check_root(&self) {
if !nix::unistd::Uid::effective().is_root() {
println!("You must run this executable with root permissions");
std::process::exit(-1)
}
}

if let Some(err) = daemonize.start().err() {
eprintln!("Error: {err}");
std::process::exit(-1)
}
/// Start the daemon
pub fn start(&self, config: Args) -> crate::Result<()> {
if let Some(pid) = self.get_pid()? {
println!("pingly is already running with pid: {pid}");
return Ok(());
}

server::run(config)
}
self.check_root();

let pid_file = File::create(&self.pid_file)?;
pid_file.set_permissions(Permissions::from_mode(0o755))?;

/// Stop the daemon
pub fn stop() -> crate::Result<()> {
use nix::{sys::signal, unistd::Pid};
let stdout = File::create(&self.stdout_file)?;
stdout.set_permissions(Permissions::from_mode(0o755))?;

root();
let stderr = File::create(&self.stderr_file)?;
stderr.set_permissions(Permissions::from_mode(0o755))?;

if let Some(pid) = get_pid() {
let pid = pid.parse::<i32>()?;
for _ in 0..360 {
if signal::kill(Pid::from_raw(pid), signal::SIGINT).is_err() {
break;
let mut daemonize = Daemonize::new()
.pid_file(&self.pid_file)
.chown_pid_file(true)
.umask(0o777)
.stdout(stdout)
.stderr(stderr)
.privileged_action(|| "Executed before drop privileges");

if let Ok(user) = std::env::var("SUDO_USER") {
if let Ok(Some(real_user)) = nix::unistd::User::from_name(&user) {
daemonize = daemonize
.user(real_user.name.as_str())
.group(real_user.gid.as_raw());
}
std::thread::sleep(std::time::Duration::from_secs(1))
}
let _ = std::fs::remove_file(PID_PATH);

if let Some(err) = daemonize.start().err() {
eprintln!("Error: {err}");
std::process::exit(-1)
}

server::run(config)
}

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

/// Restart the daemon
pub fn restart(config: Args) -> crate::Result<()> {
stop()?;
start(config)
}
self.check_root();

/// Show the status of the daemon
pub fn status() -> crate::Result<()> {
match get_pid() {
None => println!("pingly is not running"),
Some(pid) => {
let mut sys = sysinfo::System::new();

// First, we update all information of our `System` struct.
sys.refresh_all();

// Display processes ID
for (raw_pid, process) in sys.processes().iter() {
if raw_pid.as_u32().eq(&(pid.parse::<u32>()?)) {
println!("{:<6} {:<6} {:<6}", "PID", "CPU(%)", "MEM(MB)");
println!(
"{:<6} {:<6.1} {:<6.1}",
raw_pid,
process.cpu_usage(),
(process.memory() as f64) / 1024.0 / 1024.0
);
if let Some(pid) = self.get_pid()? {
let pid = pid.parse::<i32>()?;
for _ in 0..360 {
if signal::kill(Pid::from_raw(pid), signal::SIGINT).is_err() {
break;
}
std::thread::sleep(std::time::Duration::from_secs(1))
}
}

std::fs::remove_file(&self.pid_file)?;

Ok(())
}
Ok(())
}

/// Show the log of the daemon
pub fn log() -> crate::Result<()> {
fn read_and_print_file(file_path: &Path, placeholder: &str) -> crate::Result<()> {
if !file_path.exists() {
return Ok(());
}
/// Restart the daemon
pub fn restart(&self, config: Args) -> crate::Result<()> {
self.stop()?;
self.start(config)
}

// Check if the file is empty before opening it
let metadata = std::fs::metadata(file_path)?;
if metadata.len() == 0 {
return Ok(());
/// Show the status of the daemon
pub fn status(&self) -> crate::Result<()> {
match self.get_pid()? {
None => println!("pingly is not running"),
Some(pid) => {
let mut sys = sysinfo::System::new();
sys.refresh_all();

for (raw_pid, process) in sys.processes().iter() {
if raw_pid.as_u32().eq(&(pid.parse::<u32>()?)) {
println!("{:<6} {:<6} {:<6}", "PID", "CPU(%)", "MEM(MB)");
println!(
"{:<6} {:<6.1} {:<6.1}",
raw_pid,
process.cpu_usage(),
(process.memory() as f64) / 1024.0 / 1024.0
);
}
}
}
}
Ok(())
}

let file = File::open(file_path)?;
let reader = std::io::BufReader::new(file);
let mut start = true;
/// Show the log of the daemon
pub fn log(&self) -> crate::Result<()> {
fn read_and_print_file(file_path: &Path, placeholder: &str) -> crate::Result<()> {
if !file_path.exists() {
return Ok(());
}

use std::io::BufRead;
let metadata = std::fs::metadata(file_path)?;
if metadata.len() == 0 {
return Ok(());
}

for line in reader.lines() {
if let Ok(content) = line {
if start {
start = false;
println!("{placeholder}");
let file = File::open(file_path)?;
let reader = std::io::BufReader::new(file);
let mut start = true;

use std::io::BufRead;

for line in reader.lines() {
if let Ok(content) = line {
if start {
start = false;
println!("{placeholder}");
}
println!("{content}");
} else if let Err(err) = line {
eprintln!("Error reading line: {err}");
}
println!("{content}");
} else if let Err(err) = line {
eprintln!("Error reading line: {err}");
}

Ok(())
}

read_and_print_file(&self.stdout_file, "STDOUT>")?;
read_and_print_file(&self.stderr_file, "STDERR>")?;

Ok(())
}

let stdout_path = Path::new(DEFAULT_STDOUT_PATH);
read_and_print_file(stdout_path, "STDOUT>")?;

let stderr_path = Path::new(DEFAULT_STDERR_PATH);
read_and_print_file(stderr_path, "STDERR>")?;

Ok(())
}
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ pub enum Error {

#[error(transparent)]
Join(#[from] tokio::task::JoinError),

#[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error),
}
11 changes: 6 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,18 @@ pub enum Commands {

fn main() -> Result<()> {
let opt = Opt::parse();
let daemon = daemon::Daemon::default();
match opt.commands {
Commands::Run(config) => server::run(config),
#[cfg(target_family = "unix")]
Commands::Start(config) => daemon::start(config),
Commands::Start(config) => daemon.start(config),
#[cfg(target_family = "unix")]
Commands::Restart(config) => daemon::restart(config),
Commands::Restart(config) => daemon.restart(config),
#[cfg(target_family = "unix")]
Commands::Stop => daemon::stop(),
Commands::Stop => daemon.stop(),
#[cfg(target_family = "unix")]
Commands::PS => daemon::status(),
Commands::PS => daemon.status(),
#[cfg(target_family = "unix")]
Commands::Log => daemon::log(),
Commands::Log => daemon.log(),
}
}
8 changes: 4 additions & 4 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ pub async fn run(args: Args) -> Result<()> {
let router = Router::new()
.route("/api/all", any(track))
.route("/api/tls", any(tls_track))
.route("/api/http1", any(http1_headers))
.route("/api/http2", any(http2_frames))
.route("/api/http1", any(http1_track))
.route("/api/http2", any(http2_track))
.layer(global_layer);

// Signal the server to shutdown using Handle.
Expand Down Expand Up @@ -130,7 +130,7 @@ pub async fn tls_track(
}

#[inline]
pub async fn http1_headers(
pub async fn http1_track(
Extension(ConnectInfo(addr)): Extension<ConnectInfo<SocketAddr>>,
Extension(track): Extension<ConnectionTrack>,
req: Request<Body>,
Expand All @@ -142,7 +142,7 @@ pub async fn http1_headers(
}

#[inline]
pub async fn http2_frames(
pub async fn http2_track(
Extension(ConnectInfo(addr)): Extension<ConnectInfo<SocketAddr>>,
Extension(track): Extension<ConnectionTrack>,
req: Request<Body>,
Expand Down
Loading