diff options
-rw-r--r-- | doc/einhyrningsinsctl.1.ronn | 31 | ||||
-rw-r--r-- | src/bin/einhyrningsinsctl.rs | 84 |
2 files changed, 94 insertions, 21 deletions
diff --git a/doc/einhyrningsinsctl.1.ronn b/doc/einhyrningsinsctl.1.ronn new file mode 100644 index 0000000..1c9056f --- /dev/null +++ b/doc/einhyrningsinsctl.1.ronn @@ -0,0 +1,31 @@ +einhyrningsinsctl(1) -- control of einhyrningsins +================================================= + +## SYNOPSIS + +`einhyrningsinsctl` [OPTIONS] [-e CMD] + +## DESCRIPTION + +Tiny shell client for einhyrningsins(1), which connects to a control socket +(UNIX domain socket) and either sends a single command or acts as an +interactive shell. + + * `-e`, `--execute`=CMD: + Instead of starting a shell, just execute the CMD. + * `-d`, `--socket-path`=PATH: + Where to look for the control socket (a UNIX domain socket). + Defaults to `/tmp/einhorn.sock`. + * `-h`, `--help`: + Summary of options (of course) + * `--version`: + Prints version number + +## COPYRIGHT +Copyright 2016 Bryan Newbold +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. +This is free software: you are free to change and redistribute it. There is NO +WARRANTY, to the extent permitted by law. + +## SEE ALSO +`einhorn (1)`, `einhyrningsins(1)` diff --git a/src/bin/einhyrningsinsctl.rs b/src/bin/einhyrningsinsctl.rs index 0e55073..7782be3 100644 --- a/src/bin/einhyrningsinsctl.rs +++ b/src/bin/einhyrningsinsctl.rs @@ -29,7 +29,7 @@ extern crate chan_signal; extern crate url; extern crate rustyline; - +use std::io; use std::io::prelude::*; use std::io::{BufReader, BufWriter}; use std::env; @@ -51,6 +51,10 @@ fn shell(ctrl_stream: UnixStream) { // `()` can be used when no completer is required let mut rl = Editor::<()>::new(); + println!(""); + println!("Welcome to the einhyrningsins shell!"); + println!("Try 'help if you need it"); + loop { let readline = rl.readline("> "); match readline { @@ -60,40 +64,55 @@ fn shell(ctrl_stream: UnixStream) { let mut chunks = line.split(' '); let cmd = chunks.nth(0).unwrap(); let args = chunks.collect(); - send_msg(&mut reader, &mut writer, cmd, args).unwrap(); + match send_msg(&mut reader, &mut writer, cmd, args) { + Ok(s) => { println!("{}", s); }, + Err(e) => { + println!("Error sending control message: {}", e); + exit(-1); + }, + } }, Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => { - println!("Quitting..."); + println!("Caught kill signal (shutting down)"); break }, Err(err) => { - println!("Shell Error: {:?}", err); + println!("Shell Error: {:?} (shutting down)", err); break } } } + //drop(ctrl_stream); } -fn send_msg(reader: &mut BufRead, writer: &mut Write, cmd: &str, args: Vec<&str>) -> Result<String, String> { +// This function sends a single request message down the writer, then waits for a reply on the +// reader and prints the result. +fn send_msg(reader: &mut BufRead, writer: &mut Write, cmd: &str, args: Vec<&str>) -> io::Result<String> { + let mut buffer = String::new(); let mut arg_list = json::JsonValue::new_array(); + for a in args { - arg_list.push(a).unwrap(); + arg_list.push(a).expect("function args"); } let req = object!{ "command" => cmd, "args" => arg_list }; //println!("Sending: {}", req.dump()); - writer.write_all(req.dump().as_bytes()).unwrap(); - writer.write_all("\n".as_bytes()).unwrap(); - writer.flush().unwrap(); + try!(writer.write_all(format!("{}\n", req.dump()).as_bytes())); + try!(writer.flush()); - reader.read_line(&mut buffer).unwrap(); + try!(reader.read_line(&mut buffer)); //println!("Got: {}", buffer); - let reply = json::parse(&buffer).unwrap(); - println!("{}", reply.as_str().unwrap()); - Ok(reply.as_str().unwrap().to_string()) + let reply = match json::parse(&buffer) { + Ok(obj) => obj, + Err(_) => { return Ok(buffer) }, + }; + Ok(match reply.as_str() { + Some(s) => s.to_string(), + None => buffer, + }) } fn print_usage(opts: Options) { @@ -109,6 +128,8 @@ fn main() { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("", "version", "print the version"); + opts.optopt("e", "execute", "submit this command instead (no shell)", "CMD"); + opts.optopt("d", "socket-path", "where to look for control socket (default: /tmp/einhorn.sock)", "PATH"); let matches = match opts.parse(&args[1..]) { Ok(m) => { m } @@ -126,17 +147,38 @@ fn main() { } // Bind to Control Socket - let ctrl_path = Path::new("/tmp/einhorn.sock"); - // XXX: handle this more gracefully (per-process) + let path_str = matches.opt_str("socket-path").unwrap_or("/tmp/einhorn.sock".to_string()); + let ctrl_path = Path::new(&path_str); if !ctrl_path.exists() { - println!("Couldn't find control socket: {:?}", ctrl_path); + println!("Couldn't find control socket ({:?})", ctrl_path); + println!("Is the master process running? Do you need to tell me the correct socket path?"); exit(-1); } - println!("Connecting to control socket: {:?}", ctrl_path); - let ctrl_stream = UnixStream::connect(ctrl_path).unwrap(); - - send_msg(&mut BufReader::new(&ctrl_stream), &mut BufWriter::new(&ctrl_stream), "ehlo", vec![]).unwrap(); + //println!("Connecting to control socket: {:?}", ctrl_path); + let ctrl_stream = match UnixStream::connect(ctrl_path) { + Ok(s) => s, + Err(e) => { + println!("Couldn't open socket [{}]: {}", path_str, e); + exit(-1); + }, + }; - shell(ctrl_stream); + // Send a test message before continuing + send_msg(&mut BufReader::new(&ctrl_stream), + &mut BufWriter::new(&ctrl_stream), + "ehlo", + vec![]).unwrap(); + + match matches.opt_str("execute") { + Some(cmd) => { + match send_msg(&mut BufReader::new(&ctrl_stream), + &mut BufWriter::new(&ctrl_stream), + &cmd, vec![]) { + Ok(reply) => println!("{}", reply), + Err(e) => println!("Communications error: {}", e), + } + }, + None => shell(ctrl_stream), + } exit(0); } |