diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e791bf..dc0c256c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Added +* Add `--snapshot-to` CLI option for saving the current state snapshot to a file when pressing `d` + ### Fixed * Fix Ctrl+C handling to use SIGINT signal instead of keypress #491 - @chiranjeevi-max diff --git a/src/cli.rs b/src/cli.rs index af21c6a9..45b78bd7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -32,6 +32,10 @@ pub struct Opt { /// Enable debug logging to a file pub log_to: Option, + #[arg(long, value_hint = ValueHint::FilePath)] + /// Write a current-state snapshot to this file when the user presses 'd' + pub snapshot_to: Option, + #[command(flatten)] pub verbosity: Verbosity, diff --git a/src/main.rs b/src/main.rs index 400f5480..dc31c627 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,7 +54,12 @@ fn main() -> eyre::Result<()> { )?; } - let os_input = os::get_input(opts.interface.as_deref(), !opts.no_resolve, opts.dns_server)?; + let os_input = os::get_input( + opts.interface.as_deref(), + !opts.no_resolve, + opts.dns_server, + opts.snapshot_to.as_deref(), + )?; if opts.raw { let terminal_backend = RawTerminalBackend {}; start(terminal_backend, os_input, opts); @@ -89,6 +94,7 @@ pub struct OsInputOutput { pub terminal_events: Box + Send>, pub dns_client: Option, pub write_to_stdout: Box, + pub write_to_file: Option>, } pub fn start(terminal_backend: B, os_input: OsInputOutput, opts: Opt) @@ -124,6 +130,8 @@ where let network_utilization = Arc::new(Mutex::new(Utilization::new())); let ui = Arc::new(Mutex::new(Ui::new(terminal_backend, &opts))); + let mut write_to_file = os_input.write_to_file; + let display_handler = thread::Builder::new() .name("display_handler".to_string()) .spawn({ @@ -267,6 +275,16 @@ where table_cycle_offset.store(new, Ordering::SeqCst); ui.draw(paused, elapsed_time, new); } + Event::Key(KeyEvent { + modifiers: KeyModifiers::NONE, + code: KeyCode::Char('d'), + kind: KeyEventKind::Press, + .. + }) => { + if let Some(writer) = write_to_file.as_mut() { + ui.output_text(writer); + } + } _ => (), }; } diff --git a/src/os/shared.rs b/src/os/shared.rs index 65141722..f9a53ef4 100644 --- a/src/os/shared.rs +++ b/src/os/shared.rs @@ -1,6 +1,8 @@ use std::{ + fs::OpenOptions, io::{self, ErrorKind, Write}, net::Ipv4Addr, + path::Path, time::{self, Duration}, }; @@ -105,10 +107,26 @@ fn create_write_to_stdout() -> Box { }) } +pub fn create_write_to_file(path: &Path) -> eyre::Result> { + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .map_err(|err| eyre!("Failed to open file in {path:?} due to {err}"))?; + let path = path.to_path_buf(); + + Ok(Box::new(move |output| { + if let Err(e) = writeln!(&mut file, "{output}") { + warn!("Failed to write dump to file {path:?}: {e}"); + } + })) +} + pub fn get_input( interface_name: Option<&str>, resolve: bool, dns_server: Option, + snapshot_path: Option<&Path>, ) -> eyre::Result { // get the user's requested interface, if any // IDEA: allow requesting multiple interfaces @@ -219,6 +237,9 @@ pub fn get_input( }; let write_to_stdout = create_write_to_stdout(); + let write_to_file = snapshot_path + .map(|path| create_write_to_file(path)) + .transpose()?; Ok(OsInputOutput { interfaces_with_frames, @@ -226,6 +247,7 @@ pub fn get_input( terminal_events: Box::new(TerminalEvents), dns_client, write_to_stdout, + write_to_file, }) } diff --git a/src/tests/cases/test_utils.rs b/src/tests/cases/test_utils.rs index 36f87836..95c255fe 100644 --- a/src/tests/cases/test_utils.rs +++ b/src/tests/cases/test_utils.rs @@ -271,6 +271,7 @@ pub fn os_input_output_factory( terminal_events: keyboard_events, dns_client, write_to_stdout, + write_to_file: None, } }