use crate::errors::SdError;
use libc::pid_t;
use nix::unistd;
use std::os::unix::net::UnixDatagram;
use std::{env, fmt, fs, path, time};
pub fn booted() -> bool {
fs::symlink_metadata("/run/systemd/system")
.map(|p| p.is_dir())
.unwrap_or(false)
}
pub fn watchdog_enabled(unset_env: bool) -> Option<time::Duration> {
let env_usec = env::var("WATCHDOG_USEC").ok();
let env_pid = env::var("WATCHDOG_PID").ok();
if unset_env {
env::remove_var("WATCHDOG_USEC");
env::remove_var("WATCHDOG_PID");
};
let timeout = {
if let Some(usec) = env_usec.and_then(|usec_str| usec_str.parse::<u64>().ok()) {
time::Duration::from_millis(usec / 1_000)
} else {
return None;
}
};
let pid = {
if let Some(pid_str) = env_pid {
if let Ok(p) = pid_str.parse::<pid_t>() {
unistd::Pid::from_raw(p)
} else {
return None;
}
} else {
return Some(timeout);
}
};
if unistd::getpid() == pid {
Some(timeout)
} else {
None
}
}
pub fn notify(unset_env: bool, state: &[NotifyState]) -> Result<bool, SdError> {
let env_sock = env::var("NOTIFY_SOCKET").ok();
if unset_env {
env::remove_var("NOTIFY_SOCKET");
};
let path = {
if let Some(p) = env_sock.map(path::PathBuf::from) {
p
} else {
return Ok(false);
}
};
let sock =
UnixDatagram::unbound().map_err(|e| format!("failed to open datagram socket: {}", e))?;
let msg = state
.iter()
.fold(String::new(), |res, s| res + &format!("{}\n", s));
let msg_len = msg.len();
let sent_len = sock
.send_to(msg.as_bytes(), path)
.map_err(|e| format!("failed to send datagram: {}", e))?;
if sent_len != msg_len {
return Err(format!("incomplete write, sent {} out of {}", sent_len, msg_len).into());
}
Ok(true)
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NotifyState {
Buserror(String),
Errno(u8),
Fdname(String),
Fdstore,
Mainpid(unistd::Pid),
Other(String),
Ready,
Reloading,
Status(String),
Stopping,
Watchdog,
WatchdogUsec(u64),
}
impl fmt::Display for NotifyState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
NotifyState::Buserror(ref s) => write!(f, "BUSERROR={}", s),
NotifyState::Errno(e) => write!(f, "ERRNO={}", e),
NotifyState::Fdname(ref s) => write!(f, "FDNAME={}", s),
NotifyState::Fdstore => write!(f, "FDSTORE=1"),
NotifyState::Mainpid(ref p) => write!(f, "MAINPID={}", p),
NotifyState::Other(ref s) => write!(f, "{}", s),
NotifyState::Ready => write!(f, "READY=1"),
NotifyState::Reloading => write!(f, "RELOADING=1"),
NotifyState::Status(ref s) => write!(f, "STATUS={}", s),
NotifyState::Stopping => write!(f, "STOPPING=1"),
NotifyState::Watchdog => write!(f, "WATCHDOG=1"),
NotifyState::WatchdogUsec(u) => write!(f, "WATCHDOG_USEC={}", u),
}
}
}