diff --git a/src/storage.rs b/src/storage.rs index 9987136..f102d8a 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -9,6 +9,7 @@ pub trait Storage { fn recent_date(&self) -> Result>; } +#[derive(Clone)] pub struct SimpleStorage(String, String); impl SimpleStorage { diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs index 7cb0313..aa57e2e 100644 --- a/src/symbols/mod.rs +++ b/src/symbols/mod.rs @@ -25,6 +25,7 @@ pub mod nginx; pub mod not_a_symlink; pub mod npm; pub mod owner; +pub mod stored_directory; pub mod systemd; pub mod tls; pub mod user; diff --git a/src/symbols/stored_directory.rs b/src/symbols/stored_directory.rs new file mode 100644 index 0000000..18141d1 --- /dev/null +++ b/src/symbols/stored_directory.rs @@ -0,0 +1,94 @@ +use std::borrow::{Borrow, Cow}; +use std::error::Error; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; +use std::str::FromStr; + +use command_runner::CommandRunner; +use resources::Resource; +use symbols::Symbol; +use storage::Storage; + +#[derive(Debug, PartialEq)] +pub enum StorageDirection { load, save } + +pub struct StoredDirectory<'a, S> where S: Storage { + path: Cow<'a, str>, + storage: S, + dir: StorageDirection, + command_runner: &'a CommandRunner +} + +impl<'a, S> StoredDirectory<'a, S> where S: Storage { + pub fn new(path: Cow<'a, str>, storage: S, dir: StorageDirection, command_runner: &'a CommandRunner) -> Self { + StoredDirectory { + path: path, + storage: storage, + dir: dir, + command_runner: command_runner + } + } +} + +impl<'a, S> fmt::Display for StoredDirectory<'a, S> where S: Storage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Stored directory {} ({:?})", self.path, self.dir) + } +} + +impl<'a, S> Symbol for StoredDirectory<'a, S> where S: Storage { + fn target_reached(&self) -> Result> { + let metadata = fs::metadata(self.path.as_ref()); + // Check if dir exists + if let Err(e) = metadata { + return if e.kind() == io::ErrorKind::NotFound { + Ok(self.dir == StorageDirection::save) + } else { + Err(Box::new(e)) + }; + } + if !metadata.unwrap().is_dir() { + return Err(Box::new(io::Error::new(io::ErrorKind::AlreadyExists, "Could not create a directory, non-directory file exists"))); + } + + let dump_date = try!(self.storage.recent_date()); + let output = try!(self.command_runner.run_with_args("sh", &["-c", &format!("find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o", self.path)])); + if output.status.code() != Some(0) { + return Err(try!(String::from_utf8(output.stderr)).into()); + } + let modified_date = try!(u64::from_str(try!(String::from_utf8(output.stdout)).trim_right())); + Ok(if self.dir == StorageDirection::save { modified_date <= dump_date } else { dump_date <= modified_date }) + } + + fn execute(&self) -> Result<(), Box> { + if self.dir == StorageDirection::load { + try!(self.command_runner.run_with_args("cp", &["-a", &try!(self.storage.read_filename()), self.path.borrow()])); + } else { + try!(self.command_runner.run_with_args("cp", &["-a", self.path.borrow(), &self.storage.write_filename()])); + } + Ok(()) + } + + fn get_prerequisites(&self) -> Vec { + if self.dir == StorageDirection::save { return vec![]; } + if let Some(parent) = Path::new(self.path.as_ref()).parent() { + vec![ Resource::new("dir", parent.to_string_lossy()) ] + } else { + vec![] + } + } + + fn provides(&self) -> Option> { + if self.dir == StorageDirection::load { + Some(vec![ Resource::new("dir", self.path.to_string()) ]) + } else { + None + } + } +} + +#[cfg(test)] +mod test { +} diff --git a/src/symbols/systemd/node_js_user_service.rs b/src/symbols/systemd/node_js_user_service.rs index ce6d060..a0b7526 100644 --- a/src/symbols/systemd/node_js_user_service.rs +++ b/src/symbols/systemd/node_js_user_service.rs @@ -88,15 +88,32 @@ WantedBy=default.target } impl<'a, C, R> NodeJsSystemdUserService<'a, C, R> where C: Deref, R: CommandRunner { - fn check_if_service(&self) -> Result> { + + fn systemctl_wait_for_dbus(&self, args: &[&str]) -> Result> { + let mut tries = 5; loop { - // Check if service is registered - let active_state = try!(self.command_runner.run_with_args("systemctl", &["--user", "show", "--property", "ActiveState", self.service_name])); - if !active_state.status.success() { - return Err(String::from_utf8(active_state.stderr).unwrap().trim_right().into()); + let result = try!(self.command_runner.run_with_args("systemctl", args)); + if !result.status.success() { + let raw_stderr = try!(String::from_utf8(result.stderr)); + let stderr = raw_stderr.trim_right(); + if stderr != "Failed to connect to bus: No such file or directory" { + return Err(stderr.into()); + } + } else { + return Ok(try!(String::from_utf8(result.stdout)).trim_right().to_string()); } - // Check if service is running - match String::from_utf8(active_state.stdout).unwrap().trim_right() { + tries -= 1; + if tries == 0 { + return Err("Gave up waiting for dbus to appear".to_string().into()); + } + sleep(Duration::from_millis(500)); + } + } + + fn check_if_service(&self) -> Result> { + loop { + let active_state = try!(self.systemctl_wait_for_dbus(&["--user", "show", "--property", "ActiveState", self.service_name])); + match active_state.as_ref() { "ActiveState=activating" => sleep(Duration::from_millis(500)), "ActiveState=active" => return Ok(true), "ActiveState=failed" => return Err(Box::new(NodeJsSystemdUserServiceError::ActivationFailed(self.command_runner.run_with_args("journalctl", &["--user", &format!("--user-unit={}", self.service_name)])) as NodeJsSystemdUserServiceError)), @@ -141,8 +158,8 @@ impl<'a, C, R> Symbol for NodeJsSystemdUserService<'a, C, R> where C: Deref Result<(), Box> { try!(self.file.execute()); - try!(self.command_runner.run_with_args("systemctl", &["--user", "enable", self.service_name])); - try!(self.command_runner.run_with_args("systemctl", &["--user", "start", self.service_name])); + try!(self.systemctl_wait_for_dbus(&["--user", "enable", self.service_name])); + try!(self.systemctl_wait_for_dbus(&["--user", "start", self.service_name])); if !(try!(self.check_if_service())) { return Err(Box::new(NodeJsSystemdUserServiceError::GenericError as NodeJsSystemdUserServiceError));