diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-07-17 10:36:52 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-07-17 10:48:09 +0200 |
| commit | f07f1a0262c908ff83490850b67a167f963efc2d (patch) | |
| tree | 5a5209baefc0b10ba528f254e7ce2456c23f6ac9 /mullvad-cli | |
| parent | 6a4202fc8c55752f0fef86c04b628a3f9ada5279 (diff) | |
| download | mullvadvpn-f07f1a0262c908ff83490850b67a167f963efc2d.tar.xz mullvadvpn-f07f1a0262c908ff83490850b67a167f963efc2d.zip | |
Rename all crates from snake_case to kebab-case
Diffstat (limited to 'mullvad-cli')
| -rw-r--r-- | mullvad-cli/Cargo.toml | 19 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/account.rs | 55 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/connect.rs | 22 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/disconnect.rs | 22 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/mod.rs | 31 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/status.rs | 29 | ||||
| -rw-r--r-- | mullvad-cli/src/main.rs | 51 | ||||
| -rw-r--r-- | mullvad-cli/src/rpc.rs | 36 |
8 files changed, 265 insertions, 0 deletions
diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml new file mode 100644 index 0000000000..83bcb95b5e --- /dev/null +++ b/mullvad-cli/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "mullvad-cli" +version = "0.1.0" +authors = ["Linus Färnstrand <linus@mullvad.net>", "Erik Larkö <erik@mullvad.net>"] +description = "Run Talpid easily from the command line" + +[[bin]] +name = "mullvad" + +[dependencies] +clap = "2.20" +error-chain = "0.10" +log = "0.3" +env_logger = "0.4" +serde = "1.0" +serde_json = "1.0" + +mullvad-types = { path = "../mullvad-types" } +talpid-ipc = { path = "../talpid-ipc" } diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs new file mode 100644 index 0000000000..fcfe9f9901 --- /dev/null +++ b/mullvad-cli/src/cmds/account.rs @@ -0,0 +1,55 @@ +use Command; +use Result; +use clap; +use rpc; + +pub struct Account; + +impl Command for Account { + fn name(&self) -> &'static str { + "account" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Control and display information about your Mullvad account") + .setting(clap::AppSettings::SubcommandRequired) + .subcommand(clap::SubCommand::with_name("set") + .about("Change account") + .arg(clap::Arg::with_name("token") + .help("The Mullvad account token to configure the client with") + .required(true))) + .subcommand(clap::SubCommand::with_name("get") + .about("Display information about the currently configured account")) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<()> { + if let Some(set_matches) = matches.subcommand_matches("set") { + let token = value_t_or_exit!(set_matches.value_of("token"), String); + self.set(&token) + } else if let Some(_matches) = matches.subcommand_matches("get") { + self.get() + } else { + unreachable!("No account command given"); + } + } +} + +impl Account { + fn set(&self, token: &str) -> Result<()> { + rpc::call("set_account", &[token]).map( + |_: Option<()>| { + println!("Mullvad account \"{}\" set", token); + }, + ) + } + + fn get(&self) -> Result<()> { + let token: Option<String> = rpc::call("get_account", &[] as &[u8; 0])?; + match token { + Some(token) => println!("Mullvad account: {:?}", token), + None => println!("No account configured"), + } + Ok(()) + } +} diff --git a/mullvad-cli/src/cmds/connect.rs b/mullvad-cli/src/cmds/connect.rs new file mode 100644 index 0000000000..4847a0805a --- /dev/null +++ b/mullvad-cli/src/cmds/connect.rs @@ -0,0 +1,22 @@ +use Command; +use Result; +use clap; +use rpc; + +pub struct Connect; + +impl Command for Connect { + fn name(&self) -> &'static str { + "connect" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Command the client to start establishing a VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let _response: Option<()> = rpc::call("connect", &[] as &[u8; 0])?; + Ok(()) + } +} diff --git a/mullvad-cli/src/cmds/disconnect.rs b/mullvad-cli/src/cmds/disconnect.rs new file mode 100644 index 0000000000..543aa9d0cc --- /dev/null +++ b/mullvad-cli/src/cmds/disconnect.rs @@ -0,0 +1,22 @@ +use Command; +use Result; +use clap; +use rpc; + +pub struct Disconnect; + +impl Command for Disconnect { + fn name(&self) -> &'static str { + "disconnect" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Command the client to disconnect the VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let _response: Option<()> = rpc::call("disconnect", &[] as &[u8; 0])?; + Ok(()) + } +} diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs new file mode 100644 index 0000000000..c621c0840a --- /dev/null +++ b/mullvad-cli/src/cmds/mod.rs @@ -0,0 +1,31 @@ +use Command; +use std::collections::HashMap; + +mod account; +pub use self::account::Account; + +mod status; +pub use self::status::Status; + +mod connect; +pub use self::connect::Connect; + +mod disconnect; +pub use self::disconnect::Disconnect; + +/// Returns a map of all available subcommands with their name as key. +pub fn get_commands() -> HashMap<&'static str, Box<Command>> { + let commands: Vec<Box<Command>> = vec![ + Box::new(Account), + Box::new(Status), + Box::new(Connect), + Box::new(Disconnect), + ]; + let mut map = HashMap::new(); + for cmd in commands { + if let Some(_) = map.insert(cmd.name(), cmd) { + panic!("Multiple commands with the same name"); + } + } + map +} diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs new file mode 100644 index 0000000000..e02773285f --- /dev/null +++ b/mullvad-cli/src/cmds/status.rs @@ -0,0 +1,29 @@ +use Command; +use Result; +use clap; + +use mullvad_types::states::{DaemonState, SecurityState, TargetState}; +use rpc; + +pub struct Status; + +impl Command for Status { + fn name(&self) -> &'static str { + "status" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()).about("View the state of the VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let state: DaemonState = rpc::call("get_state", &[] as &[u8; 0])?; + match (state.state, state.target_state) { + (SecurityState::Unsecured, TargetState::Unsecured) => println!("Disconnected"), + (SecurityState::Unsecured, TargetState::Secured) => println!("Connecting..."), + (SecurityState::Secured, TargetState::Unsecured) => println!("Disconnecting..."), + (SecurityState::Secured, TargetState::Secured) => println!("Connected"), + } + Ok(()) + } +} diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs new file mode 100644 index 0000000000..08d5459dd7 --- /dev/null +++ b/mullvad-cli/src/main.rs @@ -0,0 +1,51 @@ +// `error_chain!` can recurse deeply +#![recursion_limit = "1024"] + +extern crate mullvad_types; +extern crate talpid_ipc; +#[macro_use] +extern crate clap; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +extern crate env_logger; +extern crate serde; +extern crate serde_json; + +mod rpc; +mod cmds; + + +error_chain!{} + +quick_main!(run); + +fn run() -> Result<()> { + env_logger::init().chain_err(|| "Failed to bootstrap logging system")?; + + let commands = cmds::get_commands(); + + let app = clap::App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .setting(clap::AppSettings::SubcommandRequired) + .subcommands(commands.values().map(|cmd| cmd.clap_subcommand())); + + let app_matches = app.get_matches(); + let (subcommand_name, subcommand_matches) = app_matches.subcommand(); + if let Some(cmd) = commands.get(subcommand_name) { + cmd.run(subcommand_matches.expect("No command matched")) + } else { + unreachable!("No command matched"); + } +} + +pub trait Command { + fn name(&self) -> &'static str; + + fn clap_subcommand(&self) -> clap::App<'static, 'static>; + + fn run(&self, matches: &clap::ArgMatches) -> Result<()>; +} diff --git a/mullvad-cli/src/rpc.rs b/mullvad-cli/src/rpc.rs new file mode 100644 index 0000000000..7397e822c4 --- /dev/null +++ b/mullvad-cli/src/rpc.rs @@ -0,0 +1,36 @@ + + +use {Result, ResultExt}; +use serde; +use std::fs::File; +use std::io::Read; +use talpid_ipc::WsIpcClient; + +pub fn call<T, O>(method: &str, args: &T) -> Result<O> + where T: serde::Serialize, + O: for<'de> serde::Deserialize<'de> +{ + call_internal(method, args).chain_err(|| "Unable to call backend over RPC") +} + +pub fn call_internal<T, O>(method: &str, args: &T) -> Result<O> + where T: serde::Serialize, + O: for<'de> serde::Deserialize<'de> +{ + let address = read_rpc_address()?; + info!("Using RPC address {}", address); + let mut rpc_client = WsIpcClient::new(address) + .chain_err(|| "Unable to create RPC client")?; + rpc_client.call(method, args).chain_err(|| format!("Unable to call RPC method {}", method)) +} + +fn read_rpc_address() -> Result<String> { + let path = ::std::env::temp_dir().join(".mullvad_rpc_address"); + + debug!("Trying to read RPC address at {}", path.to_string_lossy()); + let mut address = String::new(); + if let Ok(_) = File::open(path).and_then(|mut file| file.read_to_string(&mut address)) { + return Ok(address); + } + bail!("Unable to read RPC address"); +} |
