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",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "daemonize"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -1132,12 +1123,12 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"daemonize",
|
|
||||||
"humantime",
|
"humantime",
|
||||||
"libc",
|
"libc",
|
||||||
"notify-rust",
|
"notify-rust",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_cbor",
|
"serde_cbor",
|
||||||
|
"signal-hook",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
clap = { version = "4.3.4", features = ["derive"] }
|
clap = { version = "4.3.4", features = ["derive"] }
|
||||||
daemonize = "0.5.0"
|
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
libc = "0.2.147"
|
libc = "0.2.147"
|
||||||
notify-rust = "4.8.0"
|
notify-rust = "4.8.0"
|
||||||
serde = { version = "1.0.164", features = ["derive"] }
|
serde = { version = "1.0.164", features = ["derive"] }
|
||||||
serde_cbor = "0.11.2"
|
serde_cbor = "0.11.2"
|
||||||
|
signal-hook = "0.3.17"
|
||||||
thiserror = "1.0.44"
|
thiserror = "1.0.44"
|
||||||
|
|
|
@ -25,6 +25,9 @@ pub enum Command {
|
||||||
/// do not send notifications
|
/// do not send notifications
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
no_notify: bool,
|
no_notify: bool,
|
||||||
|
#[arg(short, long)]
|
||||||
|
#[clap(default_value_t = format!("{}/timers.pid", run_path()))]
|
||||||
|
pid_file: String,
|
||||||
},
|
},
|
||||||
/// Add a timer
|
/// Add a timer
|
||||||
#[clap(visible_alias = "a")]
|
#[clap(visible_alias = "a")]
|
||||||
|
|
|
@ -4,6 +4,10 @@ pub use crate::timer::Timer;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
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::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
os::unix::net::{UnixListener, UnixStream},
|
os::unix::net::{UnixListener, UnixStream},
|
||||||
|
@ -63,22 +67,41 @@ pub enum AnswerErr {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Daemon {
|
pub struct Daemon {
|
||||||
|
socket_path: Box<Path>,
|
||||||
|
pid_file_path: Box<Path>,
|
||||||
listener: UnixListener,
|
listener: UnixListener,
|
||||||
timers: Vec<Timer>,
|
timers: Vec<Timer>,
|
||||||
pomodoro: Option<Pomodoro>,
|
pomodoro: Option<Pomodoro>,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Daemon {
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub fn new(socket_path: String, no_notify: bool) -> anyhow::Result<Self> {
|
pub enum DaemonErr {
|
||||||
let path = std::path::Path::new(&socket_path);
|
#[error("Daemon already running!")]
|
||||||
if path.exists() {
|
AlreadyRunning,
|
||||||
std::fs::remove_file(path)
|
|
||||||
.with_context(|| format!("Could not remove previous socket {}!", socket_path))?;
|
|
||||||
}
|
}
|
||||||
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 {
|
Ok(Self {
|
||||||
|
socket_path: socket_path.into(),
|
||||||
|
pid_file_path: pid_file_path.into(),
|
||||||
listener,
|
listener,
|
||||||
timers: Vec::new(),
|
timers: Vec::new(),
|
||||||
pomodoro: None,
|
pomodoro: None,
|
||||||
|
@ -168,7 +191,12 @@ impl Daemon {
|
||||||
self.listener
|
self.listener
|
||||||
.set_nonblocking(true)
|
.set_nonblocking(true)
|
||||||
.context("Could not set listener to non blocking!")?;
|
.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() {
|
while let Ok((stream, _)) = self.listener.accept() {
|
||||||
if let Err(e) = self.handle_stream(&stream) {
|
if let Err(e) = self.handle_stream(&stream) {
|
||||||
println!("Error while handling stream: {}", e)
|
println!("Error while handling stream: {}", e)
|
||||||
|
@ -176,6 +204,20 @@ impl Daemon {
|
||||||
}
|
}
|
||||||
self.check_timers();
|
self.check_timers();
|
||||||
sleep(Duration::from_millis(100));
|
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 pomodoro;
|
||||||
mod timer;
|
mod timer;
|
||||||
|
|
||||||
use crate::helper::getuid;
|
|
||||||
use crate::cli::{send_command, Cli, Command as CliCommand};
|
use crate::cli::{send_command, Cli, Command as CliCommand};
|
||||||
use crate::daemon::{Command as DaemonCommand, Daemon};
|
use crate::daemon::{Command as DaemonCommand, Daemon};
|
||||||
use crate::helper::run_path;
|
use crate::helper::run_path;
|
||||||
|
@ -15,12 +14,8 @@ use cli::PomodoroCommand;
|
||||||
fn main() -> Result<(), anyhow::Error> {
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
let daemon_command = match args.command {
|
let daemon_command = match args.command {
|
||||||
CliCommand::Daemon { no_notify } => {
|
CliCommand::Daemon { no_notify, pid_file } => {
|
||||||
daemonize::Daemonize::new()
|
return Daemon::new(args.socket, pid_file, no_notify)?.run();
|
||||||
.pid_file(format!("{}/timers.pid", run_path()))
|
|
||||||
.user(getuid())
|
|
||||||
.start()?;
|
|
||||||
return Daemon::new(args.socket, no_notify)?.run();
|
|
||||||
}
|
}
|
||||||
CliCommand::Add { name, duration } => {
|
CliCommand::Add { name, duration } => {
|
||||||
DaemonCommand::Add(name.into_boxed_str(), duration.into())
|
DaemonCommand::Add(name.into_boxed_str(), duration.into())
|
||||||
|
|
Loading…
Reference in a new issue