diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-01-30 12:48:44 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-01-30 12:48:44 +0100 |
| commit | b6c143f7ca6cce99c32a531de2e30a4f7d060502 (patch) | |
| tree | 977b907262030c3cdf620c18f1524bc48658d290 /talpid_cli/src | |
| parent | 8900089589d16807adee6202b48256b41734d9f9 (diff) | |
| download | mullvadvpn-b6c143f7ca6cce99c32a531de2e30a4f7d060502.tar.xz mullvadvpn-b6c143f7ca6cce99c32a531de2e30a4f7d060502.zip | |
Add cli crate
Diffstat (limited to 'talpid_cli/src')
| -rw-r--r-- | talpid_cli/src/cli.rs | 59 | ||||
| -rw-r--r-- | talpid_cli/src/main.rs | 72 |
2 files changed, 131 insertions, 0 deletions
diff --git a/talpid_cli/src/cli.rs b/talpid_cli/src/cli.rs new file mode 100644 index 0000000000..c439a923ec --- /dev/null +++ b/talpid_cli/src/cli.rs @@ -0,0 +1,59 @@ +use clap::{Arg, App, ArgMatches}; +use std::path::PathBuf; + +use talpid_core::net::RemoteAddr; + +static APP_AUTHOR: &'static str = "Mullvad"; +static APP_ABOUT: &'static str = "Run Talpid easily from the command line."; + +pub struct Args { + pub binary: String, + pub config: PathBuf, + pub remotes: Vec<RemoteAddr>, + pub verbosity: u64, +} + +pub fn parse_args_or_exit() -> Args { + let matches = get_matches(); + let remotes = values_t!(matches.values_of("remotes"), RemoteAddr).unwrap_or_else(|e| e.exit()); + Args { + binary: matches.value_of("openvpn").unwrap().to_owned(), + config: PathBuf::from(matches.value_of("config").unwrap()), + remotes: remotes, + verbosity: matches.occurrences_of("verbose"), + } +} + +fn get_matches() -> ArgMatches<'static> { + let app = create_app(); + app.clone().get_matches() +} + +fn create_app() -> App<'static, 'static> { + App::new(crate_name!()) + .version(crate_version!()) + .author(APP_AUTHOR) + .about(APP_ABOUT) + .arg(Arg::with_name("openvpn") + .long("openvpn") + .help("Specify what OpenVPN binary to run") + .default_value("/usr/sbin/openvpn")) + .arg(Arg::with_name("config") + .short("c") + .long("config") + .help("Specify what config file to start OpenVPN with") + .default_value("./openvpn.conf")) + .arg(Arg::with_name("remotes") + .short("r") + .long("remotes") + .help("Configure what remote(s) to connect to. Accepts anything OpenVPN can use. \ + Format: <address>:<port>") + .takes_value(true) + .multiple(true) + .required(true)) + .arg(Arg::with_name("verbose") + .short("v") + .long("verbose") + .multiple(true) + .help("Sets the level of verbosity")) +} diff --git a/talpid_cli/src/main.rs b/talpid_cli/src/main.rs new file mode 100644 index 0000000000..a174d7ed37 --- /dev/null +++ b/talpid_cli/src/main.rs @@ -0,0 +1,72 @@ +extern crate talpid_core; +#[macro_use] +extern crate clap; + +use std::io::{self, Read, Write}; +use std::sync::mpsc::{self, Receiver}; +use std::thread; + +use talpid_core::process::OpenVpnCommand; +use talpid_core::process::monitor::{ChildMonitor, TransitionResult, ChildSpawner}; + +mod cli; + +use cli::Args; + +/// Macro for printing to stderr. Will simply do nothing if the printing fails for some reason. +macro_rules! eprintln { + ($($arg:tt)*) => ( + use std::io::Write; + let _ = writeln!(&mut ::std::io::stderr(), $($arg)* ); + ) +} + +fn main() { + let args = cli::parse_args_or_exit(); + + let command = create_openvpn_command(&args); + let monitor = ChildMonitor::new(command); + if let Err(e) = main_loop(monitor) { + eprintln!("OpenVPN failed: {}", e); + } +} + +fn create_openvpn_command(args: &Args) -> OpenVpnCommand { + let mut command = OpenVpnCommand::new(&args.binary); + command.config(&args.config) + .remotes(&args.remotes[..]) + .unwrap() + .pipe_output(args.verbosity > 0); + + command +} + +fn main_loop<S>(mut monitor: ChildMonitor<S>) -> TransitionResult<()> + where S: ChildSpawner +{ + loop { + let rx = start_monitor(&mut monitor)?; + let clean_exit = rx.recv().unwrap(); + println!("Monitored process exited. clean: {}", clean_exit); + std::thread::sleep(std::time::Duration::from_millis(500)); + } +} + +fn start_monitor<S>(monitor: &mut ChildMonitor<S>) -> TransitionResult<Receiver<bool>> + where S: ChildSpawner +{ + let (tx, rx) = mpsc::channel(); + let callback = move |clean| tx.send(clean).unwrap(); + monitor.start(callback).map(|(stdout, stderr)| { + stdout.map(|stream| pass_io(stream, io::stdout())); + stderr.map(|stream| pass_io(stream, io::stderr())); + rx + }) +} + +fn pass_io<I, O>(mut input: I, mut output: O) + where I: Read + Send + 'static, + O: Write + Send + 'static +{ + thread::spawn(move || { io::copy(&mut input, &mut output).unwrap(); }); +} |
