diff --git a/examples/actor.sh b/examples/actor.sh new file mode 100755 index 0000000..78cad96 --- /dev/null +++ b/examples/actor.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "Bash actor called with $@" diff --git a/examples/bffh.dhall b/examples/bffh.dhall index b5d892b..742ff69 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -1,19 +1,29 @@ -- { actor_connections = [] : List { _1 : Text, _2 : Text } { actor_connections = [ { _1 = "Testmachine", _2 = "Actor" } - , { _1 = "Another", _2 = "Actor2" } - , { _1 = "Yetmore", _2 = "Actor3" } + , { _1 = "Another", _2 = "Bash" } + , { _1 = "Yetmore", _2 = "Bash2" } ] , actors = { Actor = { module = "Dummy", params = {=} } , Actor2 = { module = "Dummy", params = {=} } , Actor3 = { module = "Dummy", params = {=} } + , Bash = { module = "Process", params = + { cmd = "./examples/actor.sh" + , args = "your ad could be here" + }} + , Bash2 = { module = "Process", params = + { cmd = "./examples/actor.sh" + , args = "this is a different one" + }} + , Bash3 = { module = "Process", params = + { cmd = "./examples/actor.sh" + }} } , init_connections = [] : List { _1 : Text, _2 : Text } --, init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }] -, initiators = - { Initiator = { module = "Dummy", params = {=} } - } +, initiators = --{=} + { Initiator = { module = "Dummy", params = {=} } } , listens = [ { address = "127.0.0.1", port = Some 59661 } , { address = "::1", port = Some 59661 } diff --git a/src/actor.rs b/src/actor.rs index 8802615..e4a3ae7 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -161,6 +161,10 @@ fn load_single( "Dummy" => { Some(Box::new(Dummy::new(log))) } + "Process" => { + Process::new(log.new(o!("name" => name.clone())), name.clone(), params) + .map(|a| a.into_boxed_actuator()) + } _ => { error!(log, "No actor found with name \"{}\", configured as \"{}\".", module_name, name); None diff --git a/src/machine.rs b/src/machine.rs index 270056b..e585e4e 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -163,9 +163,7 @@ impl Inner { } pub fn do_state_change(&mut self, new_state: MachineState) { - print!("State {:?}", &new_state); let old_state = self.state.replace(new_state); - print!("<- {:?}", &old_state); self.reset.replace(old_state); } diff --git a/src/modules.rs b/src/modules.rs index 7f99a56..46b6427 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -7,3 +7,6 @@ mod shelly; pub use shelly::Shelly; + +mod process; +pub use process::Process; diff --git a/src/modules/process.rs b/src/modules/process.rs new file mode 100644 index 0000000..6828a7c --- /dev/null +++ b/src/modules/process.rs @@ -0,0 +1,92 @@ +use std::collections::HashMap; +use std::process::Stdio; +use smol::process::Command; + +use futures::future::FutureExt; + +use crate::actor::Actuator; +use crate::db::machine::{MachineState, Status}; +use futures::future::BoxFuture; + +use slog::Logger; + +pub struct Process { + log: Logger, + name: String, + cmd: String, + args: Vec, +} + +impl Process { + pub fn new(log: Logger, 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 { log, name, cmd, args }) + } + + pub fn into_boxed_actuator(self) -> Box { + Box::new(self) + } +} + +impl Actuator for Process { + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { + debug!(self.log, "Running {} ({}) for {:?}", &self.name, &self.cmd, &state); + let mut command = Command::new(&self.cmd); + command + .stdin(Stdio::null()) + .args(self.args.iter()) + .arg(&self.name); + + let fstate = state.state.clone(); + match state.state { + Status::Free => { + command.arg("free"); + } + Status::InUse(by) => { + command.arg("inuse"); + by.map(|user| command.arg(format!("{}", user))); + } + Status::ToCheck(by) => { + command.arg("tocheck") + .arg(format!("{}", by)); + } + Status::Blocked(by) => { + command.arg("blocked") + .arg(format!("{}", by)); + } + Status::Disabled => { command.arg("disabled"); }, + Status::Reserved(by) => { + command.arg("reserved") + .arg(format!("{}", by)); + } + } + + let flog = self.log.new(o!()); + let name = self.name.clone(); + Box::pin(command.output().map(move |res| match res { + Ok(retv) if retv.status.success() => { + trace!(flog, "Actor was successful"); + let outstr = String::from_utf8_lossy(&retv.stdout); + for line in outstr.lines() { + debug!(flog, "{}", line); + } + } + Ok(retv) => { + warn!(flog, "Actor {} returned nonzero output {} for {:?}", name, retv.status, fstate); + if !retv.stderr.is_empty() { + let errstr = String::from_utf8_lossy(&retv.stderr); + for line in errstr.lines() { + warn!(flog, "{}", line); + } + } + } + Err(err) => { warn!(flog, "Actor {} failed to run cmd: {}", name, err); } + })) + } +}