feat!: implement own pid file + signal handling
This commit is contained in:
parent
67b3be4c4a
commit
8bbb6c9113
5 changed files with 58 additions and 27 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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<Path>,
|
||||
pid_file_path: Box<Path>,
|
||||
listener: UnixListener,
|
||||
timers: Vec<Timer>,
|
||||
pomodoro: Option<Pomodoro>,
|
||||
notify: bool,
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
pub fn new(socket_path: String, no_notify: bool) -> anyhow::Result<Self> {
|
||||
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))?;
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DaemonErr {
|
||||
#[error("Daemon already running!")]
|
||||
AlreadyRunning,
|
||||
}
|
||||
let listener = UnixListener::bind(&socket_path)
|
||||
.context(format!("Cannot bind to socket {}!", socket_path))?;
|
||||
|
||||
impl Daemon {
|
||||
pub fn new(socket: String, pid_file: String, no_notify: bool) -> anyhow::Result<Self> {
|
||||
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).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!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue