diff --git a/src/process1/Cargo.toml b/src/process1/Cargo.toml index dfcee00c04c8e7b999e840b30d591038552ee146..f24c6ddfc4da306c19db09c844316a1391b71772 100644 --- a/src/process1/Cargo.toml +++ b/src/process1/Cargo.toml @@ -20,4 +20,5 @@ log4rs = "1.0" #log4rs = { version="1.0", optional=true} siphasher = { version = "0.3", features = ["serde"] } utils = { path = "../utils" } -strum = { version = "0.23", features = ["derive"] } \ No newline at end of file +strum = { version = "0.23", features = ["derive"] } +nix = "0.23.0" diff --git a/src/process1/src/lib.rs b/src/process1/src/lib.rs index d3b4a65eb0f1879121e56edfb2ac3f11e2c39d9a..e868abd6ed1cb7137d2b170b03273f257a593af1 100644 --- a/src/process1/src/lib.rs +++ b/src/process1/src/lib.rs @@ -3,3 +3,4 @@ extern crate strum; pub mod manager; pub mod plugin; +pub mod watchdog; diff --git a/src/process1/src/manager/service.rs b/src/process1/src/manager/service.rs index ba90f9a6b97b05edd74150e46aae1aaf68fcf51e..3b203acad019c4dcc2f9482f6094540beebff881 100644 --- a/src/process1/src/manager/service.rs +++ b/src/process1/src/manager/service.rs @@ -7,6 +7,7 @@ use std::fmt; use std::io::{Error as IOError, ErrorKind}; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use crate::watchdog; #[derive(PartialEq)] struct ExitStatusSet { @@ -171,6 +172,23 @@ impl ServiceUnit { } } +impl ServiceUnit { + pub fn start_watchdog(self) { + let watchdog_usec = if self.watchdog_override_enable { + self.watchdog_override_usec + } else { self.watchdog_original_usec }; + if watchdog_usec == 0 || watchdog_usec == u64::MAX { + self.stop_watchdog() + } + watchdog::register_timer(); + watchdog::event_source_set_enabled(true); + } + + pub fn stop_watchdog(self) { + watchdog::event_source_set_enabled(false); + } +} + impl unit::UnitObj for ServiceUnit { fn init(&self) { todo!() diff --git a/src/process1/src/watchdog/hardware.rs b/src/process1/src/watchdog/hardware.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa1fdf505ec2cac3e0e6a1239796a2aa44e5cdad --- /dev/null +++ b/src/process1/src/watchdog/hardware.rs @@ -0,0 +1,187 @@ +use std::cmp::min; +use std::fs::{File, OpenOptions}; +use std::io::{self, Error, Result}; +use std::iter::FromIterator; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::time::{Duration, Instant}; + +use nix::{ioctl_read, ioctl_readwrite, libc}; +use nix::errno::{errno, Errno}; +use nix::fcntl::open; +use nix::libc::c_int; + +pub trait Watchdog { + fn config(self, timeout: Option) -> io::Result<()>; + fn close(self) -> io::Result; + fn feed(&mut self) -> io::Result<()>; +} + +#[derive(Debug)] +pub struct HardwareWatchdog { + device: String, + file: Option, + timeout: Option, + last_feed: Option, +} + +const WATCHDOG_IOCTL_BASE: u8 = b'W'; +const WATCHDOG_SETOPTIONS: u8 = 4; +const WATCHDOG_KEEPALIVE: u8 = 5; +const WATCHDOG_SETTIMEOUT: u8 = 6; +const WATCHDOG_GETTIMEOUT: u8 = 7; +const WDIOS_DISABLECARD: i32 = 0x0001; +const WDIOS_ENABLECARD: i32 = 0x0002; +ioctl_read!(watchdog_setoptions,WATCHDOG_IOCTL_BASE,WATCHDOG_SETOPTIONS,i32); +ioctl_read!(watchdog_keepalive,WATCHDOG_IOCTL_BASE,WATCHDOG_KEEPALIVE,i32); +ioctl_readwrite!(watchdog_settimeout,WATCHDOG_IOCTL_BASE,WATCHDOG_SETTIMEOUT,i32); +ioctl_read!(watchdog_gettimeout,WATCHDOG_IOCTL_BASE,WATCHDOG_GETTIMEOUT,i32); + +impl HardwareWatchdog { + fn set_options(&mut self, enable: bool) -> io::Result { + let mut flag = if enable { WDIOS_ENABLECARD } else { WDIOS_DISABLECARD }; + unsafe { watchdog_setoptions(self.fd()?, &mut flag as *mut i32).map_err(Error::from) } + } + + fn set_timeout(&mut self, mut secs: i32) -> Result { + unsafe { watchdog_settimeout(self.fd()?, &mut secs as *mut i32).map_err(Error::from) } + } + + fn obtain_timeout(&mut self) -> io::Result { + let mut sec = 0; + unsafe { watchdog_gettimeout(self.fd()?, &mut sec as *mut i32).map(|_| sec).map_err(Error::from) } + } + + fn keepalive(&mut self) -> io::Result { + let mut c = 0; + unsafe { watchdog_keepalive(self.fd()?, &mut c as *mut i32).map_err(Error::from) } + } + + fn fd(&mut self) -> io::Result { + if self.file.is_none() { + self.file = Some(OpenOptions::new().write(true).open(self.device.clone())?) + } + Ok(self.file.as_ref().unwrap().as_raw_fd()) + } + + fn set_device(&mut self, device: String) { + if self.device != device { + self.device = device; + self.file = None + } + } +} + +impl Default for HardwareWatchdog { + fn default() -> Self { + HardwareWatchdog { + device: "/dev/watchdog0".to_string(), + file: None, + timeout: Some(Duration::default()), + last_feed: None, + } + } +} + +impl Watchdog for HardwareWatchdog { + fn config(mut self, timeout: Option) -> io::Result<()> { + if self.file.is_some() && (self.timeout == timeout || timeout.is_none()) { + return Ok(()); + } + if let Some(time) = timeout { + if time.is_zero() { + self.timeout = timeout; + let _ = self.close(); + return Ok(()); + } + let secs = min(time.as_secs() as i32, i32::MAX); + match self.set_timeout(secs) { + Ok(_) => { + self.timeout = Some(Duration::from_secs(secs as u64)); + self.set_options(true)?; + self.feed()?; + } + Err(err) => { + if let Some(e) = err.raw_os_error() { + if !errno_is_not_supported(Errno::from_i32(e)) { + return Err(err); + } + } else { + return Err(err); + } + self.timeout = None; + } + } + } + if self.timeout.is_none() { + match self.obtain_timeout() { + Ok(secs) => { self.timeout = Some(Duration::from_secs(secs as u64)) } + Err(err) => { + self.timeout = Some(Duration::default()); + return Err(err); + } + } + } + Ok(()) + } + + fn close(mut self) -> io::Result { + self.timeout = Some(Duration::default()); + self.set_options(false).map_err(Error::from) + } + + fn feed(&mut self) -> io::Result<()> { + if let Some(time) = self.timeout { + if time.is_zero() { + return Ok(()); + } + } + self.keepalive()?; + self.last_feed = Some(Instant::now()); + Ok(()) + } +} + +fn errno_is_not_supported(errno: Errno) -> bool { + for e in vec![ + Errno::EOPNOTSUPP, + Errno::ENOTTY, + Errno::ENOSYS, + Errno::EAFNOSUPPORT, + Errno::EPFNOSUPPORT, + Errno::EPROTONOSUPPORT, + Errno::ESOCKTNOSUPPORT] { + if e == errno { + return true; + } + } + false +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_errno_is_not_supported() { + assert_eq!(errno_is_not_supported(Errno::ENOTTY), true); + } + + #[test] + fn test_feed() { + let mut watchdog = HardwareWatchdog::default(); + let _ = watchdog.feed(); + } + + #[test] + fn test_close() { + let mut watchdog = HardwareWatchdog::default(); + let _ = watchdog.close(); + } + + #[test] + fn test_config() { + let mut watchdog = HardwareWatchdog::default(); + let _ = watchdog.config(Some(Duration::from_secs(10))); + } +} + diff --git a/src/process1/src/watchdog/mod.rs b/src/process1/src/watchdog/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..c10daf085bd5bc238fd080c49f413557a564e300 --- /dev/null +++ b/src/process1/src/watchdog/mod.rs @@ -0,0 +1,5 @@ +pub mod hardware; + +pub fn register_timer() {} + +pub fn event_source_set_enabled(enable: bool) {} \ No newline at end of file