aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/einhyrningsinsctl.1.ronn31
-rw-r--r--src/bin/einhyrningsinsctl.rs84
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);
}