diff --git a/bffhd/actors/dummy.rs b/bffhd/actors/dummy.rs new file mode 100644 index 0000000..20e100d --- /dev/null +++ b/bffhd/actors/dummy.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; +use futures_util::future; +use futures_util::future::BoxFuture; +use crate::actors::Actor; +use crate::resources::state::State; + +pub struct Dummy { + name: String, + params: HashMap, +} + +impl Dummy { + pub fn new(name: String, params: HashMap) -> Self { + Self { name, params } + } +} + +impl Actor for Dummy { + fn apply(&mut self, state: State) -> BoxFuture<'static, ()> { + tracing::info!(name=%self.name, params=?self.params, ?state, "dummy actor updating state"); + Box::pin(future::ready(())) + } +} diff --git a/bffhd/actors/mod.rs b/bffhd/actors/mod.rs index 00fccee..64e3607 100644 --- a/bffhd/actors/mod.rs +++ b/bffhd/actors/mod.rs @@ -18,8 +18,12 @@ use anyhow::Context as _; use once_cell::sync::Lazy; use rustls::{Certificate, RootCertStore}; use url::Url; +use crate::actors::dummy::Dummy; +use crate::actors::process::Process; mod shelly; +mod process; +mod dummy; pub trait Actor { fn apply(&mut self, state: State) -> BoxFuture<'static, ()>; @@ -245,8 +249,8 @@ fn load_single( ) -> Option> { tracing::info!(%name, %module_name, ?params, "Loading actor"); match module_name.as_ref() { - //"Dummy" => Some(Box::new(Dummy::new())), - //"Process" => Process::new(name.clone(), params).map(|a| a.into_boxed_actuator()), + "Dummy" => Some(Box::new(Dummy::new(name.clone(), params.clone()))), + "Process" => Process::new(name.clone(), params).map(|a| a.into_boxed_actuator()), "Shelly" => Some(Box::new(Shelly::new(name.clone(), client, params))), _ => { None diff --git a/bffhd/actors/process.rs b/bffhd/actors/process.rs new file mode 100644 index 0000000..08da96a --- /dev/null +++ b/bffhd/actors/process.rs @@ -0,0 +1,86 @@ +use std::collections::HashMap; +use std::process::{Command, Stdio}; +use futures_util::future::BoxFuture; +use crate::actors::Actor; +use crate::resources::modules::fabaccess::Status; +use crate::resources::state::State; + +pub struct Process { + name: String, + cmd: String, + args: Vec, +} + +impl Process { + pub fn new(name: String, params: &HashMap) -> Option { + let cmd = params.get("cmd").map(|s| s.to_string())?; + let args = params.get("args").map(|argv| + argv.split_whitespace() + .map(|s| s.to_string()) + .collect()) + .unwrap_or_else(Vec::new); + + Some(Self { name, cmd, args }) + } + + pub fn into_boxed_actuator(self) -> Box { + Box::new(self) + } +} + +impl Actor for Process { + fn apply(&mut self, state: State) -> BoxFuture<'static, ()> { + tracing::debug!(name=%self.name, cmd=%self.cmd, ?state, + "Process actor updating state"); + let mut command = Command::new(&self.cmd); + command + .stdin(Stdio::null()) + .args(self.args.iter()) + .arg(&self.name); + + match state.inner.state { + Status::Free => { + command.arg("free"); + } + Status::InUse(by) => { + command.arg("inuse").arg(format!("{}", by.get_username())); + } + Status::ToCheck(by) => { + command.arg("tocheck") + .arg(format!("{}", by.get_username())); + } + Status::Blocked(by) => { + command.arg("blocked") + .arg(format!("{}", by.get_username())); + } + Status::Disabled => { command.arg("disabled"); }, + Status::Reserved(by) => { + command.arg("reserved") + .arg(format!("{}", by.get_username())); + } + } + + let name = self.name.clone(); + Box::pin(async move { match command.output() { + Ok(retv) if retv.status.success() => { + tracing::trace!("Actor was successful"); + let outstr = String::from_utf8_lossy(&retv.stdout); + for line in outstr.lines() { + tracing::debug!(%name, %line, "actor stdout"); + } + } + Ok(retv) => { + tracing::warn!(%name, ?state, code=?retv.status, + "Actor returned nonzero exitcode" + ); + if !retv.stderr.is_empty() { + let errstr = String::from_utf8_lossy(&retv.stderr); + for line in errstr.lines() { + tracing::warn!(%name, %line, "actor stderr"); + } + } + } + Err(error) => tracing::warn!(%name, ?error, "process actor failed to run cmd"), + }}) + } +} diff --git a/bffhd/actors/shelly.rs b/bffhd/actors/shelly.rs index b80f530..218fcd6 100644 --- a/bffhd/actors/shelly.rs +++ b/bffhd/actors/shelly.rs @@ -39,7 +39,9 @@ impl Shelly { impl Actor for Shelly { fn apply(&mut self, state: State) -> BoxFuture<'static, ()> { - tracing::debug!(?state, "Shelly changing state"); + tracing::debug!(?state, name=%self.name, + "Shelly changing state" + ); let pl = match state.inner.state { Status::InUse(_) => "on", _ => "off", diff --git a/bffhd/lib.rs b/bffhd/lib.rs index 501ec6d..832fd88 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -101,7 +101,10 @@ impl Diflouroborane { let resources = ResourcesHandle::new(config.machines.iter().map(|(id, desc)| { Resource::new(Arc::new(resources::Inner::new(id.to_string(), statedb.clone(), desc.clone()))) })); - RESOURCES.set(resources); + RESOURCES.set(resources.clone()); + + actors::load(self.executor.clone(), &config, resources.clone())?; + let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?;