summaryrefslogtreecommitdiff
path: root/src/daemon.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon.rs')
-rw-r--r--src/daemon.rs66
1 files changed, 56 insertions, 10 deletions
diff --git a/src/daemon.rs b/src/daemon.rs
index fa8f287..8bf8adb 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -1,7 +1,11 @@
+use log::{debug, error, info};
use nix::sys::signal::kill;
use nix::sys::signal::Signal;
use nix::unistd::Pid;
+use std::ffi::OsStr;
use std::fs::read;
+use std::os::unix::ffi::OsStrExt;
+use std::path::{Path, PathBuf};
use std::process::Command;
use std::thread::sleep;
use std::time::{Duration, Instant};
@@ -19,6 +23,10 @@ pub enum DaemonError {
#[error(transparent)]
TempFile(#[from] std::io::Error),
+ /// Something went wrong read standard output of daemon.
+ #[error("failed to read daemon stdout: {0}")]
+ Stdout(std::io::Error),
+
/// Something went wrong read error output of daemon.
#[error("failed to read daemon stderr: {0}")]
Stderr(std::io::Error),
@@ -67,18 +75,22 @@ impl DaemonManager {
/// it reads a configuration file and that has errors. This
/// function won't wait for that to happen: it only cares about
/// the PID.
- pub fn start(&self, argv: &[&str]) -> Result<Daemon, DaemonError> {
- let stdout = NamedTempFile::new()?;
- let stderr = NamedTempFile::new()?;
+ pub fn start(
+ &self,
+ argv: &[&OsStr],
+ stdout: &Path,
+ stderr: &Path,
+ ) -> Result<Daemon, DaemonError> {
+ info!("start daemon: {:?}", argv);
let pid = NamedTempFile::new()?;
let output = Command::new("daemonize")
.args(&[
"-c",
"/",
"-e",
- &stderr.path().display().to_string(),
+ &stderr.display().to_string(),
"-o",
- &stdout.path().display().to_string(),
+ &stdout.display().to_string(),
"-p",
&pid.path().display().to_string(),
])
@@ -91,6 +103,7 @@ impl DaemonManager {
std::process::exit(1);
}
+ debug!("waiting for daemon to write PID file");
let time = Instant::now();
while time.elapsed() < self.timeout {
// Do we have the pid file?
@@ -102,7 +115,8 @@ impl DaemonManager {
// Parse as an integer, if possible.
if let Ok(pid) = pid.parse() {
// We have a pid, stop waiting.
- return Ok(Daemon::new(pid));
+ info!("got pid for daemon: pid");
+ return Ok(Daemon::new(pid, stdout, stderr));
}
}
sleep_briefly();
@@ -111,8 +125,22 @@ impl DaemonManager {
}
}
- let cmd = argv.join(" ");
- let err = read(stderr.path()).map_err(DaemonError::Stderr)?;
+ error!(
+ "no PID file within {} ms, giving up",
+ self.timeout.as_millis()
+ );
+ let mut cmd = String::new();
+ for arg in argv {
+ if !cmd.is_empty() {
+ cmd.push(' ');
+ }
+ cmd.push_str(
+ &String::from_utf8_lossy(arg.as_bytes())
+ .to_owned()
+ .to_string(),
+ );
+ }
+ let err = read(&stderr).map_err(DaemonError::Stderr)?;
let err = String::from_utf8_lossy(&err).into_owned();
Err(DaemonError::Timeout(self.timeout.as_millis(), cmd, err))
}
@@ -124,11 +152,18 @@ impl DaemonManager {
#[derive(Debug)]
pub struct Daemon {
pid: Option<i32>,
+ stdout: PathBuf,
+ stderr: PathBuf,
}
impl Daemon {
- fn new(pid: i32) -> Self {
- Self { pid: Some(pid) }
+ fn new(pid: i32, stdout: &Path, stderr: &Path) -> Self {
+ info!("started daemon with PID {}", pid);
+ Self {
+ pid: Some(pid),
+ stdout: stdout.to_path_buf(),
+ stderr: stderr.to_path_buf(),
+ }
}
/// Explicitly stop a daemon.
@@ -137,11 +172,22 @@ impl Daemon {
/// errors. It can only be called once.
pub fn stop(&mut self) -> Result<(), DaemonError> {
if let Some(raw_pid) = self.pid.take() {
+ info!("stopping daemon with PID {}", raw_pid);
let pid = Pid::from_raw(raw_pid);
kill(pid, Some(Signal::SIGKILL)).map_err(|e| DaemonError::Kill(raw_pid, e))?;
}
Ok(())
}
+
+ /// Return what the daemon has written to its stderr so far.
+ pub fn stdout(&self) -> Result<Vec<u8>, DaemonError> {
+ std::fs::read(&self.stdout).map_err(DaemonError::Stdout)
+ }
+
+ /// Return what the daemon has written to its stderr so far.
+ pub fn stderr(&self) -> Result<Vec<u8>, DaemonError> {
+ std::fs::read(&self.stderr).map_err(DaemonError::Stderr)
+ }
}
impl Drop for Daemon {