From e569fec44940add141cc526ce77f67dfe976c8e0 Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Fri, 4 Jan 2019 16:30:20 -0800 Subject: Introduce command execution context support In the future we will need the ability to pass additional state that is deduced from arguments or elsewhere into the commands module. To enable such scenarios, this change introduces the concept of a command execution context. Such a context can store more or less arbitrary data, and the args module will take care of passing it through to the individual commands. --- nitrocli/src/args.rs | 161 ++++++++++++++++++++++++----------------------- nitrocli/src/commands.rs | 80 +++++++++++++---------- 2 files changed, 131 insertions(+), 110 deletions(-) diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs index ffcc0d1..cb66bb2 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -1,7 +1,7 @@ // args.rs // ************************************************************************* -// * Copyright (C) 2018 Daniel Mueller (deso@posteo.net) * +// * Copyright (C) 2018-2019 Daniel Mueller (deso@posteo.net) * // * * // * This program is free software: you can redistribute it and/or modify * // * it under the terms of the GNU General Public License as published by * @@ -28,6 +28,11 @@ use crate::pinentry; type Result = result::Result; +/// A command execution context that captures additional data pertaining +/// the command execution. +#[derive(Debug)] +pub struct ExecCtx {} + /// A top-level command for nitrocli. #[derive(Debug)] pub enum Command { @@ -42,15 +47,15 @@ pub enum Command { impl Command { /// Execute this command with the given arguments. - pub fn execute(&self, args: Vec) -> Result<()> { + pub fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { - Command::Config => config(args), - Command::Lock => lock(args), - Command::Otp => otp(args), - Command::Pin => pin(args), - Command::Pws => pws(args), - Command::Status => status(args), - Command::Storage => storage(args), + Command::Config => config(ctx, args), + Command::Lock => lock(ctx, args), + Command::Otp => otp(ctx, args), + Command::Pin => pin(ctx, args), + Command::Pws => pws(ctx, args), + Command::Status => status(ctx, args), + Command::Storage => storage(ctx, args), } } } @@ -97,10 +102,10 @@ enum ConfigCommand { } impl ConfigCommand { - fn execute(&self, args: Vec) -> Result<()> { + fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { - ConfigCommand::Get => config_get(args), - ConfigCommand::Set => config_set(args), + ConfigCommand::Get => config_get(ctx, args), + ConfigCommand::Set => config_set(ctx, args), } } } @@ -174,12 +179,12 @@ enum OtpCommand { } impl OtpCommand { - fn execute(&self, args: Vec) -> Result<()> { + fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { - OtpCommand::Clear => otp_clear(args), - OtpCommand::Get => otp_get(args), - OtpCommand::Set => otp_set(args), - OtpCommand::Status => otp_status(args), + OtpCommand::Clear => otp_clear(ctx, args), + OtpCommand::Get => otp_get(ctx, args), + OtpCommand::Set => otp_set(ctx, args), + OtpCommand::Status => otp_status(ctx, args), } } } @@ -292,11 +297,11 @@ enum PinCommand { } impl PinCommand { - fn execute(&self, args: Vec) -> Result<()> { + fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { PinCommand::Clear => pin_clear(args), - PinCommand::Set => pin_set(args), - PinCommand::Unblock => pin_unblock(args), + PinCommand::Set => pin_set(ctx, args), + PinCommand::Unblock => pin_unblock(ctx, args), } } } @@ -337,12 +342,12 @@ enum PwsCommand { } impl PwsCommand { - fn execute(&self, args: Vec) -> Result<()> { + fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { - PwsCommand::Clear => pws_clear(args), - PwsCommand::Get => pws_get(args), - PwsCommand::Set => pws_set(args), - PwsCommand::Status => pws_status(args), + PwsCommand::Clear => pws_clear(ctx, args), + PwsCommand::Get => pws_get(ctx, args), + PwsCommand::Set => pws_set(ctx, args), + PwsCommand::Status => pws_status(ctx, args), } } } @@ -385,12 +390,12 @@ fn parse(parser: &argparse::ArgumentParser<'_>, args: Vec) -> Result<()> } /// Inquire the status of the nitrokey. -fn status(args: Vec) -> Result<()> { +fn status(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Prints the status of the connected Nitrokey device"); parse(&parser, args)?; - commands::status() + commands::status(ctx) } #[derive(Debug)] @@ -401,11 +406,11 @@ enum StorageCommand { } impl StorageCommand { - fn execute(&self, args: Vec) -> Result<()> { + fn execute(&self, ctx: &ExecCtx, args: Vec) -> Result<()> { match *self { - StorageCommand::Close => storage_close(args), - StorageCommand::Open => storage_open(args), - StorageCommand::Status => storage_status(args), + StorageCommand::Close => storage_close(ctx, args), + StorageCommand::Open => storage_open(ctx, args), + StorageCommand::Status => storage_status(ctx, args), } } } @@ -438,7 +443,7 @@ impl str::FromStr for StorageCommand { } /// Execute a storage subcommand. -fn storage(args: Vec) -> Result<()> { +fn storage(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut subcommand = StorageCommand::Open; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -458,38 +463,38 @@ fn storage(args: Vec) -> Result<()> { drop(parser); subargs.insert(0, format!("nitrocli storage {}", subcommand)); - subcommand.execute(subargs) + subcommand.execute(ctx, subargs) } /// Open the encrypted volume on the nitrokey. -fn storage_open(args: Vec) -> Result<()> { +fn storage_open(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Opens the encrypted volume on a Nitrokey Storage"); parse(&parser, args)?; - commands::storage_open() + commands::storage_open(ctx) } /// Close the previously opened encrypted volume. -fn storage_close(args: Vec) -> Result<()> { +fn storage_close(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Closes the encrypted volume on a Nitrokey Storage"); parse(&parser, args)?; - commands::storage_close() + commands::storage_close(ctx) } /// Print the status of the nitrokey's storage. -fn storage_status(args: Vec) -> Result<()> { +fn storage_status(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Prints the status of the Nitrokey's storage"); parse(&parser, args)?; - commands::storage_status() + commands::storage_status(ctx) } /// Execute a config subcommand. -fn config(args: Vec) -> Result<()> { +fn config(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut subcommand = ConfigCommand::Get; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -509,20 +514,20 @@ fn config(args: Vec) -> Result<()> { drop(parser); subargs.insert(0, format!("nitrocli config {}", subcommand)); - subcommand.execute(subargs) + subcommand.execute(ctx, subargs) } /// Read the Nitrokey configuration. -fn config_get(args: Vec) -> Result<()> { +fn config_get(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Prints the Nitrokey configuration"); parse(&parser, args)?; - commands::config_get() + commands::config_get(ctx) } /// Write the Nitrokey configuration. -fn config_set(args: Vec) -> Result<()> { +fn config_set(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut numlock = None; let mut no_numlock = false; let mut capslock = None; @@ -586,20 +591,20 @@ fn config_set(args: Vec) -> Result<()> { } else { None }; - commands::config_set(numlock, capslock, scrollock, otp_pin) + commands::config_set(ctx, numlock, capslock, scrollock, otp_pin) } /// Lock the Nitrokey. -fn lock(args: Vec) -> Result<()> { +fn lock(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Locks the connected Nitrokey device"); parse(&parser, args)?; - commands::lock() + commands::lock(ctx) } /// Execute an OTP subcommand. -fn otp(args: Vec) -> Result<()> { +fn otp(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut subcommand = OtpCommand::Get; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -619,11 +624,11 @@ fn otp(args: Vec) -> Result<()> { drop(parser); subargs.insert(0, format!("nitrocli otp {}", subcommand)); - subcommand.execute(subargs) + subcommand.execute(ctx, subargs) } /// Generate a one-time password on the Nitrokey device. -fn otp_get(args: Vec) -> Result<()> { +fn otp_get(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut algorithm = OtpAlgorithm::Totp; let mut time: Option = None; @@ -647,11 +652,11 @@ fn otp_get(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::otp_get(slot, algorithm, time) + commands::otp_get(ctx, slot, algorithm, time) } /// Configure a one-time password slot on the Nitrokey device. -pub fn otp_set(args: Vec) -> Result<()> { +pub fn otp_set(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut algorithm = OtpAlgorithm::Totp; let mut name = "".to_owned(); @@ -713,11 +718,11 @@ pub fn otp_set(args: Vec) -> Result<()> { use_enter: false, token_id: None, }; - commands::otp_set(data, algorithm, counter, time_window, ascii) + commands::otp_set(ctx, data, algorithm, counter, time_window, ascii) } /// Clear an OTP slot. -fn otp_clear(args: Vec) -> Result<()> { +fn otp_clear(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut algorithm = OtpAlgorithm::Totp; let mut parser = argparse::ArgumentParser::new(); @@ -735,11 +740,11 @@ fn otp_clear(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::otp_clear(slot, algorithm) + commands::otp_clear(ctx, slot, algorithm) } /// Print the status of the OTP slots. -fn otp_status(args: Vec) -> Result<()> { +fn otp_status(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut all = false; let mut parser = argparse::ArgumentParser::new(); parser.set_description("Prints the status of the OTP slots"); @@ -751,11 +756,11 @@ fn otp_status(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::otp_status(all) + commands::otp_status(ctx, all) } /// Execute a PIN subcommand. -fn pin(args: Vec) -> Result<()> { +fn pin(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut subcommand = PinCommand::Clear; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -775,7 +780,7 @@ fn pin(args: Vec) -> Result<()> { drop(parser); subargs.insert(0, format!("nitrocli pin {}", subcommand)); - subcommand.execute(subargs) + subcommand.execute(ctx, subargs) } /// Clear the PIN as cached by various other commands. @@ -788,7 +793,7 @@ fn pin_clear(args: Vec) -> Result<()> { } /// Change a PIN. -fn pin_set(args: Vec) -> Result<()> { +fn pin_set(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut pintype = pinentry::PinType::User; let mut parser = argparse::ArgumentParser::new(); parser.set_description("Changes a PIN"); @@ -800,20 +805,20 @@ fn pin_set(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::pin_set(pintype) + commands::pin_set(ctx, pintype) } /// Unblock and reset the user PIN. -fn pin_unblock(args: Vec) -> Result<()> { +fn pin_unblock(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut parser = argparse::ArgumentParser::new(); parser.set_description("Unblocks and resets the user PIN"); parse(&parser, args)?; - commands::pin_unblock() + commands::pin_unblock(ctx) } /// Execute a PWS subcommand. -fn pws(args: Vec) -> Result<()> { +fn pws(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut subcommand = PwsCommand::Get; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -833,11 +838,11 @@ fn pws(args: Vec) -> Result<()> { drop(parser); subargs.insert(0, format!("nitrocli pws {}", subcommand)); - subcommand.execute(subargs) + subcommand.execute(ctx, subargs) } /// Access a slot of the password safe on the Nitrokey. -fn pws_get(args: Vec) -> Result<()> { +fn pws_get(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut name = false; let mut login = false; @@ -873,11 +878,11 @@ fn pws_get(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::pws_get(slot, name, login, password, quiet) + commands::pws_get(ctx, slot, name, login, password, quiet) } /// Set a slot of the password safe on the Nitrokey. -fn pws_set(args: Vec) -> Result<()> { +fn pws_set(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut name = String::new(); let mut login = String::new(); @@ -907,11 +912,11 @@ fn pws_set(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::pws_set(slot, &name, &login, &password) + commands::pws_set(ctx, slot, &name, &login, &password) } /// Clear a PWS slot. -fn pws_clear(args: Vec) -> Result<()> { +fn pws_clear(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut slot: u8 = 0; let mut parser = argparse::ArgumentParser::new(); parser.set_description("Clears a password safe slot"); @@ -923,11 +928,11 @@ fn pws_clear(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::pws_clear(slot) + commands::pws_clear(ctx, slot) } /// Print the status of the PWS slots. -fn pws_status(args: Vec) -> Result<()> { +fn pws_status(ctx: &ExecCtx, args: Vec) -> Result<()> { let mut all = false; let mut parser = argparse::ArgumentParser::new(); parser.set_description("Prints the status of the PWS slots"); @@ -939,12 +944,12 @@ fn pws_status(args: Vec) -> Result<()> { parse(&parser, args)?; drop(parser); - commands::pws_status(all) + commands::pws_status(ctx, all) } /// Parse the command-line arguments and return the selected command and /// the remaining arguments for the command. -fn parse_arguments(args: Vec) -> Result<(Command, Vec)> { +fn parse_arguments(args: Vec) -> Result<(Command, ExecCtx, Vec)> { let mut command = Command::Status; let mut subargs = vec![]; let mut parser = argparse::ArgumentParser::new(); @@ -964,11 +969,13 @@ fn parse_arguments(args: Vec) -> Result<(Command, Vec)> { drop(parser); subargs.insert(0, format!("nitrocli {}", command)); - Ok((command, subargs)) + + let ctx = ExecCtx { }; + Ok((command, ctx, subargs)) } /// Parse the command-line arguments and execute the selected command. pub fn handle_arguments(args: Vec) -> Result<()> { - let (command, args) = parse_arguments(args)?; - command.execute(args) + let (command, ctx, args) = parse_arguments(args)?; + command.execute(&ctx, args) } diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 77981db..aa99bd1 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -39,12 +39,12 @@ fn get_error(msg: &str, err: nitrokey::CommandError) -> Error { } /// Connect to any Nitrokey device and return it. -fn get_device() -> Result { +fn get_device(_ctx: &args::ExecCtx) -> Result { nitrokey::connect().map_err(|_| Error::Error("Nitrokey device not found".to_string())) } /// Connect to a Nitrokey Storage device and return it. -fn get_storage_device() -> Result { +fn get_storage_device(_ctx: &args::ExecCtx) -> Result { nitrokey::Storage::connect().or_else(|_| { Err(Error::Error( "Nitrokey Storage device not found".to_string(), @@ -217,8 +217,8 @@ fn print_status(model: &'static str, device: &nitrokey::DeviceWrapper) -> Result } /// Inquire the status of the nitrokey. -pub fn status() -> Result<()> { - let device = get_device()?; +pub fn status(ctx: &args::ExecCtx) -> Result<()> { + let device = get_device(ctx)?; let model = match device { nitrokey::DeviceWrapper::Pro(_) => "Pro", nitrokey::DeviceWrapper::Storage(_) => "Storage", @@ -227,8 +227,8 @@ pub fn status() -> Result<()> { } /// Open the encrypted volume on the nitrokey. -pub fn storage_open() -> Result<()> { - let device = get_storage_device()?; +pub fn storage_open(ctx: &args::ExecCtx) -> Result<()> { + let device = get_storage_device(ctx)?; try_with_passphrase( pinentry::PinType::User, "Opening encrypted volume failed", @@ -237,14 +237,14 @@ pub fn storage_open() -> Result<()> { } /// Close the previously opened encrypted volume. -pub fn storage_close() -> Result<()> { +pub fn storage_close(ctx: &args::ExecCtx) -> Result<()> { // Flush all filesystem caches to disk. We are mostly interested in // making sure that the encrypted volume on the nitrokey we are // about to close is not closed while not all data was written to // it. unsafe { sync() }; - get_storage_device()? + get_storage_device(ctx)? .disable_encrypted_volume() .map_err(|err| get_error("Closing encrypted volume failed", err)) } @@ -278,8 +278,8 @@ fn print_storage_status(status: &nitrokey::StorageStatus) { } /// Connect to and pretty print the status of a Nitrokey Storage. -pub fn storage_status() -> Result<()> { - let device = get_storage_device()?; +pub fn storage_status(ctx: &args::ExecCtx) -> Result<()> { + let device = get_storage_device(ctx)?; let status = device .get_status() .map_err(|err| get_error("Getting Storage status failed", err))?; @@ -297,8 +297,8 @@ fn format_option(option: Option) -> String { } /// Read the Nitrokey configuration. -pub fn config_get() -> Result<()> { - let config = get_device()? +pub fn config_get(ctx: &args::ExecCtx) -> Result<()> { + let config = get_device(ctx)? .get_config() .map_err(|err| get_error("Could not get configuration", err))?; println!( @@ -317,12 +317,13 @@ pub fn config_get() -> Result<()> { /// Write the Nitrokey configuration. pub fn config_set( + ctx: &args::ExecCtx, numlock: args::ConfigOption, capslock: args::ConfigOption, scrollock: args::ConfigOption, user_password: Option, ) -> Result<()> { - let device = authenticate_admin(get_device()?)?; + let device = authenticate_admin(get_device(ctx)?)?; let config = device .get_config() .map_err(|err| get_error("Could not get configuration", err))?; @@ -338,8 +339,8 @@ pub fn config_set( } /// Lock the Nitrokey device. -pub fn lock() -> Result<()> { - get_device()? +pub fn lock(ctx: &args::ExecCtx) -> Result<()> { + get_device(ctx)? .lock() .map_err(|err| get_error("Getting Storage status failed", err)) } @@ -364,8 +365,13 @@ fn get_unix_timestamp() -> Result { } /// Generate a one-time password on the Nitrokey device. -pub fn otp_get(slot: u8, algorithm: args::OtpAlgorithm, time: Option) -> Result<()> { - let device = get_device()?; +pub fn otp_get( + ctx: &args::ExecCtx, + slot: u8, + algorithm: args::OtpAlgorithm, + time: Option, +) -> Result<()> { + let device = get_device(ctx)?; if algorithm == args::OtpAlgorithm::Totp { device .set_time(match time { @@ -411,6 +417,7 @@ fn prepare_secret(secret: &str) -> Result { /// Configure a one-time password slot on the Nitrokey device. pub fn otp_set( + ctx: &args::ExecCtx, data: nitrokey::OtpSlotData, algorithm: args::OtpAlgorithm, counter: u64, @@ -423,7 +430,7 @@ pub fn otp_set( data.secret }; let data = nitrokey::OtpSlotData { secret, ..data }; - let device = authenticate_admin(get_device()?)?; + let device = authenticate_admin(get_device(ctx)?)?; match algorithm { args::OtpAlgorithm::Hotp => device.write_hotp_slot(data, counter), args::OtpAlgorithm::Totp => device.write_totp_slot(data, time_window), @@ -433,8 +440,8 @@ pub fn otp_set( } /// Clear an OTP slot. -pub fn otp_clear(slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> { - let device = authenticate_admin(get_device()?)?; +pub fn otp_clear(ctx: &args::ExecCtx, slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> { + let device = authenticate_admin(get_device(ctx)?)?; match algorithm { args::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot), args::OtpAlgorithm::Totp => device.erase_totp_slot(slot), @@ -479,8 +486,8 @@ fn print_otp_status( } /// Print the status of the OTP slots. -pub fn otp_status(all: bool) -> Result<()> { - let device = get_device()?; +pub fn otp_status(ctx: &args::ExecCtx, all: bool) -> Result<()> { + let device = get_device(ctx)?; println!("alg\tslot\tname"); print_otp_status(args::OtpAlgorithm::Hotp, &device, all)?; print_otp_status(args::OtpAlgorithm::Totp, &device, all)?; @@ -528,8 +535,8 @@ fn choose_pin(pintype: pinentry::PinType) -> Result { } /// Change a PIN. -pub fn pin_set(pintype: pinentry::PinType) -> Result<()> { - let device = get_device()?; +pub fn pin_set(ctx: &args::ExecCtx, pintype: pinentry::PinType) -> Result<()> { + let device = get_device(ctx)?; let new_pin = choose_pin(pintype)?; try_with_passphrase( pintype, @@ -542,8 +549,8 @@ pub fn pin_set(pintype: pinentry::PinType) -> Result<()> { } /// Unblock and reset the user PIN. -pub fn pin_unblock() -> Result<()> { - let device = get_device()?; +pub fn pin_unblock(ctx: &args::ExecCtx) -> Result<()> { + let device = get_device(ctx)?; let user_pin = choose_pin(pinentry::PinType::User)?; try_with_passphrase( pinentry::PinType::Admin, @@ -568,13 +575,14 @@ fn print_pws_data( /// Read a PWS slot. pub fn pws_get( + ctx: &args::ExecCtx, slot: u8, show_name: bool, show_login: bool, show_password: bool, quiet: bool, ) -> Result<()> { - let device = get_device()?; + let device = get_device(ctx)?; let pws = get_password_safe(&device)?; let show_all = !show_name && !show_login && !show_password; if show_all || show_name { @@ -590,8 +598,14 @@ pub fn pws_get( } /// Write a PWS slot. -pub fn pws_set(slot: u8, name: &str, login: &str, password: &str) -> Result<()> { - let device = get_device()?; +pub fn pws_set( + ctx: &args::ExecCtx, + slot: u8, + name: &str, + login: &str, + password: &str, +) -> Result<()> { + let device = get_device(ctx)?; let pws = get_password_safe(&device)?; pws .write_slot(slot, name, login, password) @@ -599,8 +613,8 @@ pub fn pws_set(slot: u8, name: &str, login: &str, password: &str) -> Result<()> } /// Clear a PWS slot. -pub fn pws_clear(slot: u8) -> Result<()> { - let device = get_device()?; +pub fn pws_clear(ctx: &args::ExecCtx, slot: u8) -> Result<()> { + let device = get_device(ctx)?; let pws = get_password_safe(&device)?; pws .erase_slot(slot) @@ -624,8 +638,8 @@ fn print_pws_slot(pws: &nitrokey::PasswordSafe<'_>, slot: usize, programmed: boo } /// Print the status of all PWS slots. -pub fn pws_status(all: bool) -> Result<()> { - let device = get_device()?; +pub fn pws_status(ctx: &args::ExecCtx, all: bool) -> Result<()> { + let device = get_device(ctx)?; let pws = get_password_safe(&device)?; let slots = pws .get_slot_status() -- cgit v1.2.3