From 8bbb6c911387acf71db6f41c4ce47af3018e50ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Sun, 30 Jul 2023 17:53:28 +0200 Subject: [PATCH] feat!: implement own pid file + signal handling --- Cargo.lock | 11 +--------- Cargo.toml | 2 +- src/cli.rs | 3 +++ src/daemon.rs | 60 +++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 9 ++------ 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a2cb5d..0a46a4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,15 +345,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "daemonize" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e" -dependencies = [ - "libc", -] - [[package]] name = "derivative" version = "2.2.0" @@ -1132,12 +1123,12 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "daemonize", "humantime", "libc", "notify-rust", "serde", "serde_cbor", + "signal-hook", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 52842b4..1209e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ edition = "2021" [dependencies] anyhow = "1.0.71" clap = { version = "4.3.4", features = ["derive"] } -daemonize = "0.5.0" humantime = "2.1.0" libc = "0.2.147" notify-rust = "4.8.0" serde = { version = "1.0.164", features = ["derive"] } serde_cbor = "0.11.2" +signal-hook = "0.3.17" thiserror = "1.0.44" diff --git a/src/cli.rs b/src/cli.rs index 025d30e..3926105 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -25,6 +25,9 @@ pub enum Command { /// do not send notifications #[arg(short, long)] no_notify: bool, + #[arg(short, long)] + #[clap(default_value_t = format!("{}/timers.pid", run_path()))] + pid_file: String, }, /// Add a timer #[clap(visible_alias = "a")] diff --git a/src/daemon.rs b/src/daemon.rs index 7b15c35..e9cecbb 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -4,6 +4,10 @@ pub use crate::timer::Timer; use anyhow::Context; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; +use std::fs::File; +use std::path::Path; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::{ io::Write, os::unix::net::{UnixListener, UnixStream}, @@ -63,22 +67,41 @@ pub enum AnswerErr { } pub struct Daemon { + socket_path: Box, + pid_file_path: Box, listener: UnixListener, timers: Vec, pomodoro: Option, notify: bool, } +#[derive(Debug, thiserror::Error)] +pub enum DaemonErr { + #[error("Daemon already running!")] + AlreadyRunning, +} + impl Daemon { - pub fn new(socket_path: String, no_notify: bool) -> anyhow::Result { - let path = std::path::Path::new(&socket_path); - if path.exists() { - std::fs::remove_file(path) - .with_context(|| format!("Could not remove previous socket {}!", socket_path))?; + pub fn new(socket: String, pid_file: String, no_notify: bool) -> anyhow::Result { + let pid_file_path = std::path::Path::new(&pid_file); + if pid_file_path.exists() { + return Err(DaemonErr::AlreadyRunning)?; + }; + let mut pid_file = File::create(pid_file_path) + .with_context(|| format!("Could not create pid file at {}!", pid_file))?; + write!(pid_file, "{}", std::process::id()).context("Could not write pid to file!")?; + + let socket_path = std::path::Path::new(&socket); + if socket_path.exists() { + std::fs::remove_file(socket_path) + .with_context(|| format!("Could not remove previous socket {}!", socket))?; } - let listener = UnixListener::bind(&socket_path) - .context(format!("Cannot bind to socket {}!", socket_path))?; + let listener = + UnixListener::bind(&socket).context(format!("Cannot bind to socket {}!", socket))?; + Ok(Self { + socket_path: socket_path.into(), + pid_file_path: pid_file_path.into(), listener, timers: Vec::new(), pomodoro: None, @@ -168,7 +191,12 @@ impl Daemon { self.listener .set_nonblocking(true) .context("Could not set listener to non blocking!")?; - loop { + + let term = Arc::new(AtomicBool::new(false)); + for sig in signal_hook::consts::TERM_SIGNALS { + signal_hook::flag::register(*sig, Arc::clone(&term))?; + } + while !term.load(Ordering::Relaxed) { while let Ok((stream, _)) = self.listener.accept() { if let Err(e) = self.handle_stream(&stream) { println!("Error while handling stream: {}", e) @@ -176,6 +204,20 @@ impl Daemon { } self.check_timers(); sleep(Duration::from_millis(100)); - } + }; + Ok(()) + } +} + +impl Drop for Daemon { + fn drop(&mut self) { + println!("Stopping..."); + if let Err(err) = std::fs::remove_file(self.socket_path.as_ref()) { + eprintln!("Error removing socket: {}", err) + }; + if let Err(err) = std::fs::remove_file(self.pid_file_path.as_ref()) { + eprintln!("Error removing pid file: {}", err) + }; + println!("Stopped successfully!"); } } diff --git a/src/main.rs b/src/main.rs index 48222fe..5b118eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ mod notification; mod pomodoro; mod timer; -use crate::helper::getuid; use crate::cli::{send_command, Cli, Command as CliCommand}; use crate::daemon::{Command as DaemonCommand, Daemon}; use crate::helper::run_path; @@ -15,12 +14,8 @@ use cli::PomodoroCommand; fn main() -> Result<(), anyhow::Error> { let args = Cli::parse(); let daemon_command = match args.command { - CliCommand::Daemon { no_notify } => { - daemonize::Daemonize::new() - .pid_file(format!("{}/timers.pid", run_path())) - .user(getuid()) - .start()?; - return Daemon::new(args.socket, no_notify)?.run(); + CliCommand::Daemon { no_notify, pid_file } => { + return Daemon::new(args.socket, pid_file, no_notify)?.run(); } CliCommand::Add { name, duration } => { DaemonCommand::Add(name.into_boxed_str(), duration.into())