Skip to content

Commit

Permalink
Add initial parsec-tool implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Ellis <[email protected]>
  • Loading branch information
Joe Ellis committed Jul 28, 2020
1 parent a3f8a55 commit d8ea69f
Show file tree
Hide file tree
Showing 9 changed files with 1,351 additions and 1 deletion.
1,002 changes: 1,002 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ edition = "2018"
documentation = "https://docs.rs/crate/parsec-tool"

[dependencies]
ansi_term = "0.12"
anyhow = "1.0.31"
atty = "0.2"
clap = "3.0.0-beta.1"
parsec-client = "0.7.1"
thiserror = "1.0"

[[bin]]
name = "parsec-tool"
5 changes: 5 additions & 0 deletions MAINTAINERS.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"ionut-arm",
"justincormack",
"paulhowardarm",
"joechrisellis",
]

[people]
Expand All @@ -43,3 +44,7 @@
Email = "[email protected]"
GitHub = "paulhowardarm"

[people.joechrisellis]
Name = "Joe Ellis"
Email = "[email protected]"
GitHub = "joechrisellis"
65 changes: 65 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! CLI implementation.
use crate::common::{PROJECT_AUTHOR, PROJECT_DESC, PROJECT_NAME, PROJECT_VERSION};
use clap::{App, AppSettings, Arg};

#[derive(Debug)]
pub enum Action {
ListOpcodes(String),
ListProviders,
Ping,
}

#[derive(Debug)]
pub struct CommandLineContext {
pub action: Action,
pub app_name: Option<String>,
pub verbosity: u64,
}

pub fn parse_cli() -> CommandLineContext {
let matches = App::new(PROJECT_NAME)
.about(PROJECT_DESC)
.author(PROJECT_AUTHOR)
.version(PROJECT_VERSION)
.setting(AppSettings::SubcommandRequiredElseHelp)
.arg(
Arg::with_name("v")
.short('v')
.multiple(true)
.about("Sets the level of verbosity"),
)
.subcommand(
App::new("list_opcodes").about("List the opcodes").arg(
Arg::with_name("provider")
.short('p')
.takes_value(true)
.required(true)
.possible_values(&["core", "mbedcrypto", "pkcs11", "tpm"])
.about("The provider to get the opcodes for"),
),
)
.subcommand(App::new("list_providers").about("List the providers"))
.subcommand(App::new("ping").about("Ping the client"))
.get_matches();

CommandLineContext {
action: if let Some(matches) = matches.subcommand_matches("list_opcodes") {
Action::ListOpcodes(String::from(matches.value_of("provider").unwrap()))
} else if let Some(_matches) = matches.subcommand_matches("list_providers") {
Action::ListProviders
} else if let Some(_matches) = matches.subcommand_matches("ping") {
Action::Ping
} else {
unreachable!()
},
app_name: match matches.value_of("appname") {
Some(app_name) => Some(String::from(app_name)),
_ => None,
},
verbosity: matches.occurrences_of("verbosity"),
}
}
105 changes: 105 additions & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! Command implementations. Interacts with parsec-client-rust.
use crate::cli::{Action, CommandLineContext};
use crate::error::ParsecCliError;
use anyhow::{Context, Result};
use parsec_client::auth::AuthenticationData;
use parsec_client::core::interface::requests::ProviderID;
use parsec_client::BasicClient;

fn get_unauthenticated_client() -> BasicClient {
BasicClient::new(AuthenticationData::None)
}

/// Gets the list of availble opcodes for a given provider and outputs them.
fn list_opcodes(
_command_line_context: &CommandLineContext,
provider: &str,
) -> Result<(), ParsecCliError> {
let client = get_unauthenticated_client();

let desired_provider = match provider {
"core" => ProviderID::Core,
"mbedcrypto" => ProviderID::MbedCrypto,
"pkcs11" => ProviderID::Pkcs11,
"tpm" => ProviderID::Tpm,
_ => unreachable!(),
};

let provider_opcodes = client
.list_opcodes(desired_provider)
.context("Failed to list opcodes.")?;

e_info!("Available opcodes for provider {:?}:", desired_provider);
for provider_opcode in provider_opcodes {
print_colored!(Blue, "*");
println!(" {:?}", provider_opcode);
}
Ok(())
}

/// Gets the list of available providers and outputs them.
fn list_providers(_command_line_context: &CommandLineContext) -> Result<(), ParsecCliError> {
let client = get_unauthenticated_client();
let available_providers = client
.list_providers()
.context("Failed to list providers.")?;

e_info!("Available providers:");
for provider in available_providers {
print!("[");
print_colored!(Cyan, "0x{:02x} ({})", provider.id as u32, provider.id);
println!("]");

print_colored!(Yellow, "Description");
println!(": {}", provider.description);

print_colored!(Yellow, "Version");
println!(
": {}.{}.{}",
provider.version_maj, provider.version_min, provider.version_rev,
);

print_colored!(Yellow, "Vendor");
println!(
": {}",
if !provider.vendor.is_empty() {
provider.vendor
} else {
"Unspecified".to_string()
},
);

print_colored!(Yellow, "UUID");
println!(": {}", provider.uuid);
println!();
}
Ok(())
}

/// Attempts to ping the Parsec service.
fn ping(_command_line_context: &CommandLineContext) -> Result<(), ParsecCliError> {
e_info!("Pinging Parsec service...");

let client = get_unauthenticated_client();
let (wire_prot_v_maj, wire_prot_v_min) =
client.ping().context("Failed to ping Parsec service.")?;

success!(
"Service wire protocol version is {}.{}.",
wire_prot_v_maj,
wire_prot_v_min
);
Ok(())
}

pub fn dispatch_command(command_line_context: &CommandLineContext) -> Result<(), ParsecCliError> {
match &command_line_context.action {
Action::ListOpcodes(provider) => list_opcodes(command_line_context, provider),
Action::ListProviders => list_providers(command_line_context),
Action::Ping => ping(command_line_context),
}
}
9 changes: 9 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! Common variables.
pub const PROJECT_NAME: &str = env!("CARGO_PKG_NAME");
pub const PROJECT_DESC: &str = env!("CARGO_PKG_DESCRIPTION");
pub const PROJECT_AUTHOR: &str = env!("CARGO_PKG_AUTHORS");
pub const PROJECT_VERSION: &str = env!("CARGO_PKG_VERSION");
12 changes: 12 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! Error definitions/handling.
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ParsecCliError {
#[error(transparent)]
ClientError(#[from] anyhow::Error),
}
22 changes: 21 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@

//! Parsec Tool: a tool to communicate with Parsec from the command-line
#[macro_use]
mod output;

mod cli;
mod command;
mod common;
mod error;

use crate::command::dispatch_command;
use anyhow::{Context, Result};

fn run() -> Result<bool> {
let command_line_context = cli::parse_cli();
dispatch_command(&command_line_context).context("Command exited with errors.")?;
Ok(true)
}

fn main() {
println!("Hello, Parsec!");
if let Err(err) = run() {
e_err!("{:?}", err);
std::process::exit(1);
}
}
123 changes: 123 additions & 0 deletions src/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! Output helpers.
// COLORS:
// The output colour of a particular object of text should convey some information about what
// exactly it is. These are not hard and fast rules, but generally speaking:
// - Blue for general information.
// - Green for success.
// - Red for errors and warnings.
// - Cyan should be used for section titles.
// - Yellow should represent field names -- i.e. UUIDs, versions, etc.
//
// By sticking to this guideline we ensure that the CLI stays consistent.

/// Gets a colorized message for outputting to a particular stream. Do not use colors if the stream
/// is not a tty.
#[macro_export]
macro_rules! colorize {
($color:ident, $stream:ident, $arg:tt) => {{
let style = if atty::is(atty::Stream::$stream) {
ansi_term::Color::$color.normal()
} else {
ansi_term::Style::default()
};
style.paint($arg)
}};
($color:ident, $stream:ident, $($arg:tt)*) => {{
let style = if atty::is(atty::Stream::$stream) {
ansi_term::Color::$color.normal()
} else {
ansi_term::Style::default()
};
style.paint(format!($($arg)*))
}};
}

/// Wrapper around `print!` that accepts a color as its first argument.
#[macro_export]
macro_rules! print_colored {
($color:ident, $($arg:tt)*) => {
print!("{}", colorize!($color, Stdout, $($arg)*))
};
}

/// Wrapper around `println!` that accepts a color as its first argument.
#[macro_export]
macro_rules! println_colored {
($color:ident, $($arg:tt)*) => {
println!("{}", colorize!($color, Stdout, $($arg)*))
};
}

/// Wrapper around `eprint!` that accepts a color as its first argument.
#[macro_export]
macro_rules! eprint_colored {
($color:ident, $($arg:tt)*) => {
eprint!("{}", colorize!($color, Stderr, $($arg)*))
};
}

/// Wrapper around `eprintln!` that accepts a color as its first argument.
#[macro_export]
macro_rules! eprintln_colored {
($color:ident, $($arg:tt)*) => {
eprintln!("{}", colorize!($color, Stderr, $($arg)*))
};
}

/// Write an info message to stdout.
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
print!("{}", colorize!(Blue, Stdout, "[INFO] "));
println!($($arg)*);
};
}

/// Write an error message to stdout.
#[macro_export]
macro_rules! err {
($($arg:tt)*) => {
print!("{}", colorize!(Red, Stdout, "[ERR] "));
println!($($arg)*);
};
}

/// Write a success message to stdout.
#[macro_export]
macro_rules! success {
($($arg:tt)*) => {
print!("{}", colorize!(Green, Stdout, "[SUCCESS] "));
println!($($arg)*);
};
}

/// Write an info message to stderr.
#[macro_export]
macro_rules! e_info {
($($arg:tt)*) => {
eprint!("{}", colorize!(Blue, Stderr, "[INFO] "));
eprintln!($($arg)*);
};
}

/// Write an error message to stderr.
#[macro_export]
macro_rules! e_err {
($($arg:tt)*) => {
eprint!("{}", colorize!(Red, Stderr, "[ERR] "));
eprintln!($($arg)*);
};
}

/// Write a success message to stderr.
#[macro_export]
macro_rules! e_success {
($($arg:tt)*) => {
eprint!("{}", colorize!(Green, Stderr, "[SUCCESS] "));
eprintln!($($arg)*);
};
}

0 comments on commit d8ea69f

Please sign in to comment.