From 2ba357c632f85c2218af1982f6a1858e525ba5b5 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 10 Sep 2020 12:20:13 +0200 Subject: [PATCH 1/8] Add fill command to overwrite SD card This patch adds the fill command that overwrites the SD card with random data. Similar to the reset command, we always require the user to enter the admin PIN even if is cached. --- CHANGELOG.md | 2 ++ doc/nitrocli.1 | 9 +++++++++ doc/nitrocli.1.pdf | Bin 42203 -> 43281 bytes src/args.rs | 2 ++ src/commands.rs | 35 +++++++++++++++++++++++++++++++++++ src/tests/fill.rs | 15 +++++++++++++++ src/tests/mod.rs | 1 + 7 files changed, 64 insertions(+) create mode 100644 src/tests/fill.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 83fc8d97..109e52a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Unreleased - Added `--usb-path` option that restricts the USB path of the device to connect to - Bumped `structopt` dependency to `0.3.17` +- Added the `fill` command that fills the SD card of a Nitrokey Storage device + with random data 0.3.4 diff --git a/doc/nitrocli.1 b/doc/nitrocli.1 index 6fb1fd9d..69dc91e0 100644 --- a/doc/nitrocli.1 +++ b/doc/nitrocli.1 @@ -131,6 +131,15 @@ open. .TP \fBnitrocli hidden close Close a hidden volume. +.TP +\fBnitrocli fill\fR +Fills the SD card with random data, overwriting all existing data. +This operation cannot be cancelled, even if the \fBnitrocli\fR process is +terminated before it finishes. + +This command requires the admin PIN. +To avoid accidental calls of this command, the user has to enter the PIN even +if it has been cached. .SS One-time passwords The Nitrokey Pro and the Nitrokey Storage support the generation of one-time diff --git a/doc/nitrocli.1.pdf b/doc/nitrocli.1.pdf index d98ab41f4f75d9807f6f6741974cc1775a839e38..4c3707d5b1492bec1fbe46e97fa6b8b60c8f197a 100644 GIT binary patch delta 34751 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 Ik|e_a0h~J6Qvd(} delta 33775 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 cct<{9 diff --git a/src/args.rs b/src/args.rs index 80abe17b..4947825c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -79,6 +79,8 @@ Command! { Config(ConfigArgs) => |ctx, args: ConfigArgs| args.subcmd.execute(ctx), /// Interacts with the device's encrypted volume Encrypted(EncryptedArgs) => |ctx, args: EncryptedArgs| args.subcmd.execute(ctx), + /// Fills the SD card with random data + Fill => crate::commands::fill, /// Interacts with the device's hidden volume Hidden(HiddenArgs) => |ctx, args: HiddenArgs| args.subcmd.execute(ctx), /// Lists the attached Nitrokey devices diff --git a/src/commands.rs b/src/commands.rs index ebb28ca1..f83c397c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -461,6 +461,41 @@ pub fn list(ctx: &mut Context<'_>, no_connect: bool) -> anyhow::Result<()> { Ok(()) } +/// Fill the SD card with random data +pub fn fill(ctx: &mut Context<'_>) -> anyhow::Result<()> { + with_storage_device(ctx, |ctx, mut device| { + let pin_entry = pinentry::PinEntry::from(args::PinType::Admin, &device)?; + + // Similar to reset, we want the user to re-enter the admin PIN even if is cached to avoid + // accidental data loss. + pinentry::clear(&pin_entry).context("Failed to clear cached secret")?; + + try_with_pin(ctx, &pin_entry, |pin| { + device.fill_sd_card(&pin).context("Failed to fill SD card") + })?; + + let mut last_progress = 0; + loop { + use nitrokey::OperationStatus; + + let status = device + .get_operation_status() + .context("Failed to query operation status")?; + match status { + OperationStatus::Ongoing(progress) => { + if last_progress != progress { + println!(ctx, "{}/100", progress)?; + } + last_progress = progress; + } + OperationStatus::Idle => break, + }; + } + + Ok(()) + }) +} + /// Perform a factory reset. pub fn reset(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |ctx, mut device| { diff --git a/src/tests/fill.rs b/src/tests/fill.rs new file mode 100644 index 00000000..70ea0812 --- /dev/null +++ b/src/tests/fill.rs @@ -0,0 +1,15 @@ +// fill.rs + +// Copyright (C) 2020 The Nitrocli Developers +// SPDX-License-Identifier: GPL-3.0-or-later + +use super::*; + +// Ignore this test as it takes about one hour to execute +#[ignore] +#[test_device(storage)] +fn fill(model: nitrokey::Model) -> anyhow::Result<()> { + let res = Nitrocli::new().model(model).handle(&["fill"]); + assert!(res.is_ok()); + Ok(()) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 631ffb0d..e0a5b9a6 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -9,6 +9,7 @@ use nitrokey_test::test as test_device; mod config; mod encrypted; +mod fill; mod hidden; mod list; mod lock; From b58bf85591c92cd58a9edc7b78c59403ba2d1bf2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 10 Sep 2020 13:34:46 +0200 Subject: [PATCH 2/8] Add is_tty field to Context This patch adds the is_tty field to the Context struct that indicates whether stdout is a TTY. This allows us to use TTY features like moving the cursor in our output. --- CHANGELOG.md | 1 + Cargo.lock | 161 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + src/main.rs | 15 ++++- src/tests/mod.rs | 1 + 5 files changed, 179 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 109e52a2..a342059f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Unreleased - Bumped `structopt` dependency to `0.3.17` - Added the `fill` command that fills the SD card of a Nitrokey Storage device with random data + - Added the `crossterm` dependency in version `0.17.8` 0.3.4 diff --git a/Cargo.lock b/Cargo.lock index 75528cc3..b98fb791 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + [[package]] name = "arrayref" version = "0.3.6" @@ -85,6 +91,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -102,6 +117,31 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossterm" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "858085e389f71d31a6909f2b55a56b87d1cb8b168c0f513dcbed5e66a3e1039c" +dependencies = [ + "bitflags", + "crossterm_winapi", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c" +dependencies = [ + "winapi", +] + [[package]] name = "directories" version = "3.0.1" @@ -163,6 +203,24 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.3.3" @@ -191,12 +249,37 @@ dependencies = [ "syn", ] +[[package]] +name = "mio" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9971bc8349a361217a8f2a41f5d011274686bd4436465ba51730921039d7fb" +dependencies = [ + "lazy_static", + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi", +] + [[package]] name = "nitrocli" version = "0.3.4" dependencies = [ "anyhow", "base32", + "crossterm", "directories", "envy", "libc", @@ -248,6 +331,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a59b732ed6d5212424ed31ec9649f05652bcbc38f45f2292b27a6044e7098803" +[[package]] +name = "ntapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" +dependencies = [ + "winapi", +] + [[package]] name = "num-traits" version = "0.2.12" @@ -257,6 +349,30 @@ dependencies = [ "autocfg", ] +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "proc-macro-error" version = "1.0.2" @@ -357,6 +473,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.114" @@ -377,6 +499,45 @@ dependencies = [ "syn", ] +[[package]] +name = "signal-hook" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" +dependencies = [ + "libc", + "mio", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "socket2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + [[package]] name = "structopt" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 43ff6cbf..a91985ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,9 @@ version = "1.0" [dependencies.base32] version = "0.4.0" +[dependencies.crossterm] +version = "0.17.8" + [dependencies.envy] version = "0.4.1" diff --git a/src/main.rs b/src/main.rs index baad15c2..cc895037 100644 --- a/src/main.rs +++ b/src/main.rs @@ -98,6 +98,8 @@ pub struct Context<'io> { pub stdout: &'io mut dyn io::Write, /// The `Write` object used as standard error throughout the program. pub stderr: &'io mut dyn io::Write, + /// Whether `stdout` is a TTY. + pub is_tty: bool, /// The admin PIN, if provided through an environment variable. pub admin_pin: Option, /// The user PIN, if provided through an environment variable. @@ -118,7 +120,12 @@ pub struct Context<'io> { } impl<'io> Context<'io> { - fn from_env(stdout: &'io mut O, stderr: &'io mut E, config: config::Config) -> Context<'io> + fn from_env( + stdout: &'io mut O, + stderr: &'io mut E, + is_tty: bool, + config: config::Config, + ) -> Context<'io> where O: io::Write, E: io::Write, @@ -126,6 +133,7 @@ impl<'io> Context<'io> { Context { stdout, stderr, + is_tty, admin_pin: env::var_os(NITROCLI_ADMIN_PIN), user_pin: env::var_os(NITROCLI_USER_PIN), new_admin_pin: env::var_os(NITROCLI_NEW_ADMIN_PIN), @@ -154,8 +162,11 @@ fn main() { let rc = match config::Config::load() { Ok(config) => { + use crossterm::tty::IsTty as _; + + let is_tty = stdout.is_tty(); let args = env::args().collect::>(); - let ctx = &mut Context::from_env(&mut stdout, &mut stderr, config); + let ctx = &mut Context::from_env(&mut stdout, &mut stderr, is_tty, config); run(ctx, args) } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index e0a5b9a6..1871f3c0 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -99,6 +99,7 @@ impl Nitrocli { let ctx = &mut crate::Context { stdout: &mut stdout, stderr: &mut stderr, + is_tty: false, admin_pin: self.admin_pin.clone(), user_pin: self.user_pin.clone(), new_admin_pin: self.new_admin_pin.clone(), From b853402432c637949f13383dfb70cc42f8a5f05a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 10 Sep 2020 12:39:07 +0200 Subject: [PATCH 3/8] Show progress bar in fill output This patch uses the progressing crate to display a progress bar for the fill command if the output is printed to a TTY. --- Cargo.lock | 10 ++++++ Cargo.toml | 3 ++ src/commands.rs | 17 +++++----- src/main.rs | 1 + src/output.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ src/redefine.rs | 6 ++++ 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/output.rs diff --git a/Cargo.lock b/Cargo.lock index b98fb791..0f82c965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,6 +287,7 @@ dependencies = [ "nitrokey", "nitrokey-test", "nitrokey-test-state", + "progressing", "regex", "serde", "structopt", @@ -408,6 +409,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "progressing" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b7db19a74ba7c34de36558abed080568491d2b8999a34de914b1793b0b4b1b" +dependencies = [ + "log", +] + [[package]] name = "quote" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index a91985ce..f188159c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,9 @@ version = "0.1" [dependencies.nitrokey] version = "0.7.1" +[dependencies.progressing] +version = "3.0.2" + [dependencies.serde] version = "1.0" features = ["derive"] diff --git a/src/commands.rs b/src/commands.rs index f83c397c..34b4a82f 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -22,6 +22,7 @@ use nitrokey::GetPasswordSafe; use crate::args; use crate::config; +use crate::output; use crate::pinentry; use crate::Context; @@ -474,22 +475,20 @@ pub fn fill(ctx: &mut Context<'_>) -> anyhow::Result<()> { device.fill_sd_card(&pin).context("Failed to fill SD card") })?; - let mut last_progress = 0; - loop { + let mut progress_bar = output::ProgressBar::new(); + progress_bar.draw(ctx)?; + while !progress_bar.is_finished() { use nitrokey::OperationStatus; + thread::sleep(time::Duration::from_secs(1)); let status = device .get_operation_status() .context("Failed to query operation status")?; match status { - OperationStatus::Ongoing(progress) => { - if last_progress != progress { - println!(ctx, "{}/100", progress)?; - } - last_progress = progress; - } - OperationStatus::Idle => break, + OperationStatus::Ongoing(progress) => progress_bar.update(progress)?, + OperationStatus::Idle => progress_bar.finish(), }; + progress_bar.draw(ctx)?; } Ok(()) diff --git a/src/main.rs b/src/main.rs index cc895037..d0fd4726 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,7 @@ mod arg_util; mod args; mod commands; mod config; +mod output; mod pinentry; #[cfg(test)] mod tests; diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 00000000..6e4d915b --- /dev/null +++ b/src/output.rs @@ -0,0 +1,82 @@ +// commands.rs + +// Copyright (C) 2020 The Nitrocli Developers +// SPDX-License-Identifier: GPL-3.0-or-later + +use crate::Context; + +/// A progress bar that can be printed to an interactive output. +pub struct ProgressBar { + redraw: bool, + progress: u8, + toggle: bool, + finished: bool, +} + +impl ProgressBar { + pub fn new() -> ProgressBar { + ProgressBar { + redraw: true, + progress: 0, + toggle: false, + finished: false, + } + } + + pub fn is_finished(&self) -> bool { + self.finished + } + + pub fn update(&mut self, progress: u8) -> anyhow::Result<()> { + anyhow::ensure!(!self.finished, "Tried to update finished progress bar"); + anyhow::ensure!( + progress <= 100, + "Progress bar value out of range: {}", + progress + ); + if progress != self.progress { + self.redraw = true; + self.progress = progress; + } + self.toggle = !self.toggle; + Ok(()) + } + + pub fn finish(&mut self) { + self.finished = true; + self.redraw = true; + self.progress = 100; + } + + pub fn draw(&self, ctx: &mut Context<'_>) -> anyhow::Result<()> { + use crossterm::{cursor, terminal}; + + if !ctx.is_tty { + return Ok(()); + } + + let progress_char = if self.toggle && !self.finished { + "." + } else { + " " + }; + if self.redraw { + use progressing::Baring; + + let mut progress_bar = progressing::mapping::Bar::with_range(0, 100); + progress_bar.set(self.progress); + + print!(ctx, "{}", terminal::Clear(terminal::ClearType::CurrentLine))?; + print!(ctx, "{}", cursor::MoveToColumn(0))?; + print!(ctx, " {} {}", progress_char, progress_bar)?; + if self.finished { + println!(ctx)?; + } + } else { + print!(ctx, "{}{}", cursor::MoveToColumn(1), progress_char)?; + } + + ctx.stdout.flush()?; + Ok(()) + } +} diff --git a/src/redefine.rs b/src/redefine.rs index dad45292..10fb631c 100644 --- a/src/redefine.rs +++ b/src/redefine.rs @@ -14,6 +14,12 @@ macro_rules! println { }; } +macro_rules! print { + ($ctx:expr, $($arg:tt)*) => { + write!($ctx.stdout, $($arg)*) + }; +} + macro_rules! eprintln { ($ctx:expr) => { writeln!($ctx.stderr, "") From 5f972f86fdc514fece0ebce6869feca855ef2688 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 12 Sep 2020 12:27:18 +0200 Subject: [PATCH 4/8] fixup! Add fill command to overwrite SD card mention operation time in man page --- doc/nitrocli.1 | 5 +++-- doc/nitrocli.1.pdf | Bin 43281 -> 43300 bytes 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/nitrocli.1 b/doc/nitrocli.1 index 69dc91e0..b9e4c857 100644 --- a/doc/nitrocli.1 +++ b/doc/nitrocli.1 @@ -134,8 +134,9 @@ Close a hidden volume. .TP \fBnitrocli fill\fR Fills the SD card with random data, overwriting all existing data. -This operation cannot be cancelled, even if the \fBnitrocli\fR process is -terminated before it finishes. +This operation takes about one hour to finish for a 16 GB SD card. +It cannot be cancelled, even if the \fBnitrocli\fR process is terminated before +it finishes. This command requires the admin PIN. To avoid accidental calls of this command, the user has to enter the PIN even diff --git a/doc/nitrocli.1.pdf b/doc/nitrocli.1.pdf index 4c3707d5b1492bec1fbe46e97fa6b8b60c8f197a..557dec794fa391ca012f41122ce09a0e93a68efb 100644 GIT binary patch delta 15062 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 YxVU+_SXiP$xIp}TsPy#Gsxqkm7YWL{%m4rY From 2f5e81e9df795617ae2af30a07356539365135e3 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 12 Sep 2020 12:30:59 +0200 Subject: [PATCH 5/8] fixup! Add is_tty field to Context use crossterm 0.17.7 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f82c965..51f9d73d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.17.8" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "858085e389f71d31a6909f2b55a56b87d1cb8b168c0f513dcbed5e66a3e1039c" +checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" dependencies = [ "bitflags", "crossterm_winapi", diff --git a/Cargo.toml b/Cargo.toml index f188159c..67c56f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ version = "1.0" version = "0.4.0" [dependencies.crossterm] -version = "0.17.8" +version = "0.17.7" [dependencies.envy] version = "0.4.1" From fa5701853b5e4986864d46b17cef90146d7a1be6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 12 Sep 2020 12:39:26 +0200 Subject: [PATCH 6/8] fixup! Show progress bar in fill output document ProgressBar --- src/output.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/output.rs b/src/output.rs index 6e4d915b..07326595 100644 --- a/src/output.rs +++ b/src/output.rs @@ -7,13 +7,18 @@ use crate::Context; /// A progress bar that can be printed to an interactive output. pub struct ProgressBar { + /// Whether to redraw the entire progress bar in the next call to `draw`. redraw: bool, + /// The current progress of the progress bar (0 <= progress <= 100). progress: u8, + /// Toogled on every call to `draw` to print a pulsing indicator. toggle: bool, + /// Whether this progress bar finished. finished: bool, } impl ProgressBar { + /// Creates a new empty progress bar. pub fn new() -> ProgressBar { ProgressBar { redraw: true, @@ -23,10 +28,12 @@ impl ProgressBar { } } + /// Whether this progress bar is finished. pub fn is_finished(&self) -> bool { self.finished } + /// Updates the progress bar with the given progress (0 <= progress <= 100). pub fn update(&mut self, progress: u8) -> anyhow::Result<()> { anyhow::ensure!(!self.finished, "Tried to update finished progress bar"); anyhow::ensure!( @@ -42,12 +49,21 @@ impl ProgressBar { Ok(()) } + /// Finish this progress bar. + /// + /// A finished progress bar may no longer be updated. pub fn finish(&mut self) { self.finished = true; self.redraw = true; self.progress = 100; } + /// Print the progress bar to the stdout set in the given context. + /// + /// On every call of this method (as long as the progress bar is not finished), a pulsing + /// indicator is printed to show that the process is still running. If there was progress since + /// the last call to `draw`, or if this is the first call, this function will also print the + /// progress bar itself. pub fn draw(&self, ctx: &mut Context<'_>) -> anyhow::Result<()> { use crossterm::{cursor, terminal}; From 9f226ecf54e4ef7e3ea23fb629ddb750b889e2d7 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 12 Sep 2020 21:59:00 +0200 Subject: [PATCH 7/8] fixup! Add is_tty field to Context replace crossterm with termion --- CHANGELOG.md | 2 +- Cargo.lock | 174 ++++++++------------------------------------------- Cargo.toml | 6 +- src/main.rs | 4 +- 4 files changed, 30 insertions(+), 156 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a342059f..c1de6d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ Unreleased - Bumped `structopt` dependency to `0.3.17` - Added the `fill` command that fills the SD card of a Nitrokey Storage device with random data - - Added the `crossterm` dependency in version `0.17.8` + - Added the `termion` dependency in version `1.5.5` 0.3.4 diff --git a/Cargo.lock b/Cargo.lock index 51f9d73d..94126a25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,12 +15,6 @@ version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" -[[package]] -name = "arc-swap" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" - [[package]] name = "arrayref" version = "0.3.6" @@ -91,15 +85,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -117,31 +102,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crossterm" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" -dependencies = [ - "bitflags", - "crossterm_winapi", - "lazy_static", - "libc", - "mio", - "parking_lot", - "signal-hook", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c" -dependencies = [ - "winapi", -] - [[package]] name = "directories" version = "3.0.1" @@ -203,15 +163,6 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.11" @@ -249,37 +200,12 @@ dependencies = [ "syn", ] -[[package]] -name = "mio" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9971bc8349a361217a8f2a41f5d011274686bd4436465ba51730921039d7fb" -dependencies = [ - "lazy_static", - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" -dependencies = [ - "socket2", - "winapi", -] - [[package]] name = "nitrocli" version = "0.3.4" dependencies = [ "anyhow", "base32", - "crossterm", "directories", "envy", "libc", @@ -291,6 +217,7 @@ dependencies = [ "regex", "serde", "structopt", + "termion", "toml", ] @@ -332,15 +259,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a59b732ed6d5212424ed31ec9649f05652bcbc38f45f2292b27a6044e7098803" -[[package]] -name = "ntapi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" -dependencies = [ - "winapi", -] - [[package]] name = "num-traits" version = "0.2.12" @@ -351,28 +269,10 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.2" +name = "numtoa" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "proc-macro-error" @@ -442,6 +342,15 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +dependencies = [ + "redox_syscall", +] + [[package]] name = "redox_users" version = "0.3.4" @@ -483,12 +392,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.114" @@ -509,45 +412,6 @@ dependencies = [ "syn", ] -[[package]] -name = "signal-hook" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" -dependencies = [ - "libc", - "mio", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" -dependencies = [ - "arc-swap", - "libc", -] - -[[package]] -name = "smallvec" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" - -[[package]] -name = "socket2" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - [[package]] name = "structopt" version = "0.3.17" @@ -594,6 +458,18 @@ dependencies = [ "syn", ] +[[package]] +name = "termion" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 67c56f9d..a24cc810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,9 +38,6 @@ version = "1.0" [dependencies.base32] version = "0.4.0" -[dependencies.crossterm] -version = "0.17.7" - [dependencies.envy] version = "0.4.1" @@ -67,6 +64,9 @@ features = ["derive"] version = "0.3.17" default-features = false +[dependencies.termion] +version = "1.5.5" + [dependencies.toml] version = "0.5.6" diff --git a/src/main.rs b/src/main.rs index d0fd4726..c0c7da55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,9 +163,7 @@ fn main() { let rc = match config::Config::load() { Ok(config) => { - use crossterm::tty::IsTty as _; - - let is_tty = stdout.is_tty(); + let is_tty = termion::is_tty(&stdout); let args = env::args().collect::>(); let ctx = &mut Context::from_env(&mut stdout, &mut stderr, is_tty, config); From 5c464a09b587c83cd4600aaecd3d6a98025496d1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 12 Sep 2020 22:05:34 +0200 Subject: [PATCH 8/8] fixup! Show progress bar in fill output replace crossterm with termion --- src/output.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/output.rs b/src/output.rs index 07326595..590e0c81 100644 --- a/src/output.rs +++ b/src/output.rs @@ -65,31 +65,39 @@ impl ProgressBar { /// the last call to `draw`, or if this is the first call, this function will also print the /// progress bar itself. pub fn draw(&self, ctx: &mut Context<'_>) -> anyhow::Result<()> { - use crossterm::{cursor, terminal}; + use anyhow::Context as _; + use termion::cursor::DetectCursorPos as _; + use termion::raw::IntoRawMode as _; if !ctx.is_tty { return Ok(()); } + let pos = ctx + .stdout + .into_raw_mode() + .context("Failed to activate raw mode")? + .cursor_pos() + .context("Failed to query cursor position")?; let progress_char = if self.toggle && !self.finished { "." } else { " " }; if self.redraw { - use progressing::Baring; + use progressing::Baring as _;; let mut progress_bar = progressing::mapping::Bar::with_range(0, 100); progress_bar.set(self.progress); - print!(ctx, "{}", terminal::Clear(terminal::ClearType::CurrentLine))?; - print!(ctx, "{}", cursor::MoveToColumn(0))?; + print!(ctx, "{}", termion::clear::CurrentLine)?; + print!(ctx, "{}", termion::cursor::Goto(1, pos.1))?; print!(ctx, " {} {}", progress_char, progress_bar)?; if self.finished { println!(ctx)?; } } else { - print!(ctx, "{}{}", cursor::MoveToColumn(1), progress_char)?; + print!(ctx, "{}{}", termion::cursor::Goto(2, pos.1), progress_char)?; } ctx.stdout.flush()?;