/Core/comms/src/server_process/server.rs
Rust | 251 lines | 160 code | 43 blank | 48 comment | 20 complexity | 54ff156f0784969d8a70f9a2a75ccea5 MD5 | raw file
- // @Author: Lashermes Ronan <ronan>
- // @Date: 24-03-2017
- // @Email: ronan.lashermes@inria.fr
- // @Last modified by: ronan
- // @Last modified time: 24-03-2017
- // @License: MIT
- use hconfig::store::{StoreNode, StoreKey, Data, DataType, CompoundDataType};
- use config_keys::*;
- use std::process::Command;
- use std::sync::mpsc::*;
- use std::thread;
- use std::time::Duration;
- use std::process::Stdio;
- use std::io::Read;
- // use std::str::FromStr;
- // use dimval::{DimVal};
- use failure::Error;
- pub enum ServerMessage {
- Kill
- }
- pub fn pop_server(name: &str, conn_config: &StoreNode) -> Result<Sender<ServerMessage>, Error> {
- let config = try!(conn_config.get_node(&StoreKey::from(name)) .map_err(|e|format_err!("{}", e)));
- let pre_commands = extract_string_array(config, PRE_COMMANDS);
- execute_commands(&pre_commands);
- let args = extract_string_array(config, ARGS);
- let launch = try!(extract_string(config, LAUNCH_COMMAND));
- let wait_dimval = config.get_value(&StoreKey::from(WAIT))
- .and_then(|d|d.convert( &CompoundDataType::Singleton(DataType::Dim(0, format!("s"))) ))
- .unwrap_or(Data::create_dim_str("3s")?)
- .to_atomic_data()?
- .to_dim()?;
- let wait = wait_dimval.to_i64_prefix(-3)? as u64;//get wait time as milliseconds
- // let wait = extract_i64(config, WAIT) as u64;
- let end_commands = extract_string_array(config, END_COMMANDS);
- let res = launch_server(launch, args, wait, end_commands);
- let post_commands = extract_string_array(config, POST_COMMANDS);
- execute_commands(&post_commands);
- Ok(res)
- }
- /// Launch a server, i.e. a shell process that can be kept alive for the whole duration
- fn launch_server(prog: String, args: Vec<String>, wait: u64, end_commands: Vec<String>) -> Sender<ServerMessage> {
- let (tx,rx): (Sender<ServerMessage>, Receiver<ServerMessage>) = channel();
- info!("Launching server '{}' with args '{:?}'", prog, args);
- thread::spawn(move|| {
- let mut quit = false;
- //split on " " to separate process from args
- let cmd_words: Vec<_> = prog.split(" ").collect();
- if cmd_words.len() > 0 {
- //first word is process
- let mut server_cmd = Command::new(cmd_words[0]);
- //add other words as args
- for i in 1..cmd_words.len() {
- server_cmd.arg(cmd_words[i]);
- }
- //args can also be filled with the dedicated argument
- for a in args.iter() {
- let words = a.split(" ");
- for w in words {
- server_cmd.arg(w);
- }
- }
- let mut handle = server_cmd .stdin(Stdio::null())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn();
- if handle.is_err() {
- warn!("Failed to start server: {:?}", server_cmd);
- quit = true;
- }
- //while alive
- while !quit {
- // read stdout and stderr
- if let Ok(ref mut child) = handle {
- if let Some(ref mut stdo) = child.stdout {
- let mut stdo_str = String::new();
- while let Ok(_) = stdo.read_to_string(&mut stdo_str) {
- if stdo_str != "" {
- trace!("{}: STDOUT, {}", prog, stdo_str);
- }
- }
- }
- if let Some(ref mut stdo) = child.stderr {
- let mut stdo_str = String::new();
- while let Ok(_) = stdo.read_to_string(&mut stdo_str) {
- if stdo_str != "" {
- warn!("{}: STDERR, {}", prog, stdo_str);
- }
- }
- }
- }
- //watch for a kill messages
- match rx.try_recv() {
- Ok(message) => {
- match message {
- ServerMessage::Kill => {
- quit = true;
- }
- }
- },
- Err(_) => {
- // thread::sleep(Duration::from_millis(200));
- // warn!("Unexpected error in server ({:?}) thread: {}", server_cmd, e);
- // quit = true;
- }
- }
- }
- //kill order has been received, out of the event loop
- match handle {
- Ok(ref mut child) => {
- // let child_id = child.id();
- let res = child.kill();
- info!("Server killed: {:?} (={:?}).", server_cmd, res);
- // unsafe {
- // kill(child_id as i32, SIGTERM);
- // }
- }
- _ => {}
- }
- //exeuce after-kill commands
- execute_commands(&end_commands);
- }
- });
- thread::sleep(Duration::from_millis(wait));
- tx
- }
- /// Execute a list of shell commands (1 String = 1 shell command)
- fn execute_commands(cmds: &Vec<String>) {
- for cmd in cmds.iter() {
- let cmd_words: Vec<_> = cmd.split(" ").collect();
- if cmd_words.len() > 0 {
- let mut cmd_to_call = Command::new(cmd_words[0]);
- for i in 1..cmd_words.len() {
- cmd_to_call.arg(cmd_words[i]);
- }
- match cmd_to_call.output() {
- Ok(_) => info!("Command '{}' successfully executed", cmd),
- Err(e) => info!("Command '{}' failed to execute with error: {}", cmd, e.to_string())
- }
- }
- }
- }
- fn extract_string(config: &StoreNode, key: &str) -> Result<String, Error> {
- config.get_value(&StoreKey::from(key))?.to_string_result()
- // match config.get_value(&StoreKey::from(key)) {
- // Ok(Data::String(s)) => Ok(s),
- // Ok(d) => {return Err(format_err!("{} does not contain a string ({:?})", key, d));},
- // Err(e) => Err(e.into())
- // }
- }
- //default is 3000
- // fn extract_i64(config: &StoreNode, key: &str) -> i64 {
- // match config.get_value(&StoreKey::from(key)) {
- // Ok(Data::I64(i)) => i,
- // Ok(Data::String(s)) => {
- // match s.parse::<DimVal>()
- // .and_then(|x|x.to_f64_prefix(Prefix::Milli)) {
- // Ok(f) => f as i64,
- // Err(_) => 3000
- // }
- // },
- // Ok(Data::F64(f)) => (f*1000f64) as i64,
- // _ => 3000
- // }
- // }
- fn extract_string_array(config: &StoreNode, key: &str) -> Vec<String> {
- let mut args: Vec<String> = Vec::new();
- let node = match config.get_node(&StoreKey::from(key)) {
- Ok(n) => n,
- Err(_) => {return args;}
- };
- match node.get_node_value() {
- &Data::Singleton(ref ad) => {
- match ad.to_string_result() {
- Ok(ref s) => args.push(s.clone()),
- _ => {}
- }
- },
- &Data::Array(ref v) => {
- for d in v.iter() {
- // trace!("New data for {}: {:?}", key, d);
- match d.to_string_result() {
- Ok(ref s) => {
- args.push(s.to_string());
- },
- _ => {}
- }
- }
- },
- _ => {}
- }
- for c in node.get_node_value_array() {
- match c.to_string_result() {
- Ok(ref s) => {
- args.push(s.clone());
- },
- _ => {}
- }
- }
- args
- }
- #[test]
- fn test_convert_milli() {
- use dimval::DimVal;
- use std::str::FromStr;
- let wait_dimval = DimVal::from_str("3s").unwrap();
- let wait = wait_dimval.to_i64_prefix(-3).unwrap() as u64;
- assert_eq!(wait, 3000);
- }