cli: add status command
Provides equivalent functionality to `yubico-piv-tool`
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
)]
|
||||
|
||||
use gumdrop::Options;
|
||||
use yubikey_cli::commands::YubikeyCli;
|
||||
use yubikey_cli::commands::YubiKeyCli;
|
||||
|
||||
fn main() {
|
||||
YubikeyCli::parse_args_default_or_exit().run();
|
||||
YubiKeyCli::parse_args_default_or_exit().run();
|
||||
}
|
||||
|
||||
+50
-21
@@ -1,9 +1,10 @@
|
||||
//! Commands of the CLI application
|
||||
|
||||
pub mod readers;
|
||||
pub mod status;
|
||||
|
||||
use self::readers::ReadersCmd;
|
||||
use crate::status::{self, STDOUT};
|
||||
use self::{readers::ReadersCmd, status::StatusCmd};
|
||||
use crate::terminal::{self, STDOUT};
|
||||
use gumdrop::Options;
|
||||
use std::{
|
||||
env,
|
||||
@@ -11,36 +12,29 @@ use std::{
|
||||
process::exit,
|
||||
};
|
||||
use termcolor::{ColorChoice, ColorSpec, WriteColor};
|
||||
use yubikey_piv::{Serial, YubiKey};
|
||||
|
||||
/// The `yubikey` CLI utility
|
||||
#[derive(Debug, Options)]
|
||||
pub struct YubikeyCli {
|
||||
pub struct YubiKeyCli {
|
||||
/// Obtain help about the current command
|
||||
#[options(short = "h", help = "print help message")]
|
||||
pub help: bool,
|
||||
|
||||
/// Specify the serial number of the YubiKey to connect to
|
||||
#[options(
|
||||
short = "s",
|
||||
long = "serial",
|
||||
help = "serial number of the YubiKey to connect to"
|
||||
)]
|
||||
pub serial: Option<Serial>,
|
||||
|
||||
/// Subcommand to execute.
|
||||
#[options(command)]
|
||||
pub command: Option<Commands>,
|
||||
}
|
||||
|
||||
impl YubikeyCli {
|
||||
/// Run the underlying command type or print usage info and exit
|
||||
pub fn run(&self) {
|
||||
// TODO(tarcieri): make this more configurable
|
||||
status::set_color_choice(ColorChoice::Auto);
|
||||
|
||||
// Only show logs if `RUST_LOG` is set
|
||||
if env::var("RUST_LOG").is_ok() {
|
||||
env_logger::builder().format_timestamp(None).init();
|
||||
}
|
||||
|
||||
match &self.command {
|
||||
Some(cmd) => cmd.run(),
|
||||
None => Self::print_usage().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
impl YubiKeyCli {
|
||||
/// Print usage information
|
||||
pub fn print_usage() -> Result<(), io::Error> {
|
||||
let mut stdout = STDOUT.lock();
|
||||
@@ -65,6 +59,36 @@ impl YubikeyCli {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run the underlying command type or print usage info and exit
|
||||
pub fn run(&self) {
|
||||
// TODO(tarcieri): make this more configurable
|
||||
terminal::set_color_choice(ColorChoice::Auto);
|
||||
|
||||
// Only show logs if `RUST_LOG` is set
|
||||
if env::var("RUST_LOG").is_ok() {
|
||||
env_logger::builder().format_timestamp(None).init();
|
||||
}
|
||||
|
||||
match &self.command {
|
||||
Some(cmd) => cmd.run(self.yubikey_init()),
|
||||
None => Self::print_usage().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the YubiKey client driver
|
||||
fn yubikey_init(&self) -> YubiKey {
|
||||
match self.serial {
|
||||
Some(serial) => YubiKey::open_by_serial(serial).unwrap_or_else(|e| {
|
||||
status_err!("couldn't open YubiKey (serial #{}): {}", serial, e);
|
||||
exit(1);
|
||||
}),
|
||||
None => YubiKey::open().unwrap_or_else(|e| {
|
||||
status_err!("couldn't open default YubiKey: {}", e);
|
||||
exit(1);
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subcommands of this application
|
||||
@@ -81,15 +105,20 @@ pub enum Commands {
|
||||
/// `readers` subcommand
|
||||
#[options(help = "list detected readers")]
|
||||
Readers(ReadersCmd),
|
||||
|
||||
/// `status` subcommand
|
||||
#[options(help = "show yubikey status")]
|
||||
Status(StatusCmd),
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
/// Run the given command
|
||||
pub fn run(&self) {
|
||||
pub fn run(&self, yubikey: YubiKey) {
|
||||
match self {
|
||||
Commands::Help(help) => help.run(),
|
||||
Commands::Version(version) => version.run(),
|
||||
Commands::Readers(list) => list.run(),
|
||||
Commands::Status(status) => status.run(yubikey),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
//! List detected readers
|
||||
|
||||
use crate::terminal::STDOUT;
|
||||
use gumdrop::Options;
|
||||
use std::process::exit;
|
||||
use yubikey_piv::readers::Readers;
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
process::exit,
|
||||
};
|
||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||
use yubikey_piv::{Readers, Serial};
|
||||
|
||||
/// The `readers` subcommand
|
||||
#[derive(Debug, Options)]
|
||||
@@ -26,6 +31,9 @@ impl ReadersCmd {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let mut s = STDOUT.lock();
|
||||
s.reset().unwrap();
|
||||
|
||||
for (i, reader) in readers_iter.enumerate() {
|
||||
let name = reader.name();
|
||||
let mut yubikey = match reader.open() {
|
||||
@@ -34,7 +42,23 @@ impl ReadersCmd {
|
||||
};
|
||||
|
||||
let serial = yubikey.serial();
|
||||
println!("{}: {} (serial: {})", i + 1, name, serial);
|
||||
self.print_reader(&mut s, i + 1, &name, serial).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a reader
|
||||
fn print_reader(
|
||||
&self,
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
index: usize,
|
||||
name: &str,
|
||||
serial: Serial,
|
||||
) -> Result<(), io::Error> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>3}:", index)?;
|
||||
stream.reset()?;
|
||||
writeln!(stream, " {} (serial: {})", name, serial)?;
|
||||
stream.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//! Print device status
|
||||
|
||||
use crate::terminal::STDOUT;
|
||||
use gumdrop::Options;
|
||||
use std::io::{self, Write};
|
||||
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||
use yubikey_piv::YubiKey;
|
||||
|
||||
// String to use for `None`
|
||||
const NONE_STR: &str = "<none>";
|
||||
|
||||
/// The `status` subcommand
|
||||
#[derive(Debug, Options)]
|
||||
pub struct StatusCmd {}
|
||||
|
||||
impl StatusCmd {
|
||||
/// Run the `status` subcommand
|
||||
pub fn run(&self, mut yk: YubiKey) {
|
||||
let mut s = STDOUT.lock();
|
||||
s.reset().unwrap();
|
||||
|
||||
self.attr(&mut s, "version", yk.version()).unwrap();
|
||||
self.attr(&mut s, "serial", yk.serial()).unwrap();
|
||||
|
||||
if let Ok(chuid) = yk.chuid() {
|
||||
self.attr(&mut s, "CHUID", chuid).unwrap();
|
||||
} else {
|
||||
self.attr(&mut s, "CHUID", NONE_STR).unwrap();
|
||||
}
|
||||
|
||||
if let Ok(chuid) = yk.cccid() {
|
||||
self.attr(&mut s, "CCC", chuid).unwrap();
|
||||
} else {
|
||||
self.attr(&mut s, "CCC", NONE_STR).unwrap();
|
||||
}
|
||||
|
||||
self.attr(&mut s, "PIN retries", yk.get_pin_retries().unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Print a status attribute
|
||||
fn attr(
|
||||
&self,
|
||||
stream: &mut StandardStreamLock<'_>,
|
||||
name: &str,
|
||||
value: impl ToString,
|
||||
) -> Result<(), io::Error> {
|
||||
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||
write!(stream, "{:>12}:", name)?;
|
||||
stream.reset()?;
|
||||
writeln!(stream, " {}", value.to_string())?;
|
||||
stream.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,6 +9,6 @@
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod status;
|
||||
pub mod terminal;
|
||||
|
||||
pub mod commands;
|
||||
|
||||
@@ -9,7 +9,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
#[macro_export]
|
||||
macro_rules! status_ok {
|
||||
($status:expr, $msg:expr) => {
|
||||
$crate::status::Status::new()
|
||||
$crate::terminal::Status::new()
|
||||
.justified()
|
||||
.bold()
|
||||
.color(termcolor::Color::Green)
|
||||
@@ -25,7 +25,7 @@ macro_rules! status_ok {
|
||||
#[macro_export]
|
||||
macro_rules! status_warn {
|
||||
($msg:expr) => {
|
||||
$crate::status::Status::new()
|
||||
$crate::terminal::Status::new()
|
||||
.bold()
|
||||
.color(termcolor::Color::Yellow)
|
||||
.status("warning:")
|
||||
@@ -40,7 +40,7 @@ macro_rules! status_warn {
|
||||
#[macro_export]
|
||||
macro_rules! status_err {
|
||||
($msg:expr) => {
|
||||
$crate::status::Status::new()
|
||||
$crate::terminal::Status::new()
|
||||
.bold()
|
||||
.color(termcolor::Color::Red)
|
||||
.status("error:")
|
||||
Reference in New Issue
Block a user