From 67b3be4c4a2ded95dd604bc9cbe67286712abd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Sat, 29 Jul 2023 18:11:32 +0200 Subject: [PATCH 1/2] feat: use /run/user to store socket and pid file --- Cargo.lock | 5 +++-- Cargo.toml | 1 + src/cli.rs | 3 ++- src/helper.rs | 7 +++++++ src/main.rs | 8 +++++++- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/helper.rs diff --git a/Cargo.lock b/Cargo.lock index f817b60..2a2cb5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,9 +621,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" @@ -1134,6 +1134,7 @@ dependencies = [ "clap", "daemonize", "humantime", + "libc", "notify-rust", "serde", "serde_cbor", diff --git a/Cargo.toml b/Cargo.toml index 6626413..52842b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ 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" diff --git a/src/cli.rs b/src/cli.rs index 51fe842..025d30e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,5 @@ use crate::daemon::{Answer, AnswerErr, Command as OtherCommand}; +use crate::run_path; use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use std::net::Shutdown; @@ -12,7 +13,7 @@ pub struct Cli { #[command(subcommand)] pub command: Command, #[arg(short, long)] - #[clap(default_value = "/tmp/timers.socket")] + #[clap(default_value_t = format!("{}/timers.socket", run_path()))] pub socket: String, } diff --git a/src/helper.rs b/src/helper.rs new file mode 100644 index 0000000..164b5e9 --- /dev/null +++ b/src/helper.rs @@ -0,0 +1,7 @@ +pub fn getuid() -> u32 { + unsafe { libc::getuid() } +} + +pub fn run_path() -> String { + format!("/run/user/{}", getuid()) +} diff --git a/src/main.rs b/src/main.rs index 3143354..48222fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ mod cli; mod daemon; +mod helper; 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; use clap::Parser; use cli::PomodoroCommand; @@ -13,7 +16,10 @@ fn main() -> Result<(), anyhow::Error> { let args = Cli::parse(); let daemon_command = match args.command { CliCommand::Daemon { no_notify } => { - daemonize::Daemonize::new().start()?; + daemonize::Daemonize::new() + .pid_file(format!("{}/timers.pid", run_path())) + .user(getuid()) + .start()?; return Daemon::new(args.socket, no_notify)?.run(); } CliCommand::Add { name, duration } => { 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 2/2] 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())