diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 77db9658..7c539abe 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -72,6 +72,9 @@ jobs: if: matrix.os == 'ubuntu' run: cargo install cross --git https://github.com/cross-rs/cross + - name: Clippy (release mode) + run: cargo clippy --release -- -D warnings + - name: Build binary if: matrix.os == 'ubuntu' run: cross build --release --target ${{ matrix.target }} diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b957d9..79dcb432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,9 @@ All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] -- Added Vietnamese translation 🇻🇳 ([#577](https://github.com/GyulyVGC/sniffnet/pull/577)) - Added CLI argument `--adapter []` to allow immediately starting the capture from a given network interface ([#643](https://github.com/GyulyVGC/sniffnet/pull/643) — fixes [#636](https://github.com/GyulyVGC/sniffnet/issues/636)) +- Added Vietnamese translation 🇻🇳 ([#577](https://github.com/GyulyVGC/sniffnet/pull/577)) +- Redirect `stderr` and `stdout` to file on Windows release builds ([#645](https://github.com/GyulyVGC/sniffnet/pull/645) — fixes [#578](https://github.com/GyulyVGC/sniffnet/issues/578)) - Updated some of the existing translations to v1.3: - Chinese ([#575](https://github.com/GyulyVGC/sniffnet/pull/575)) - Korean ([#604](https://github.com/GyulyVGC/sniffnet/pull/604)) diff --git a/Cargo.lock b/Cargo.lock index 9602f65a..13944921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -1397,6 +1397,17 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filedescriptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + [[package]] name = "finl_unicode" version = "1.3.0" @@ -1637,6 +1648,16 @@ dependencies = [ "slab", ] +[[package]] +name = "gag" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a713bee13966e9fbffdf7193af71d54a6b35a0bb34997cd6c9519ebeb5005972" +dependencies = [ + "filedescriptor", + "tempfile", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4476,6 +4497,7 @@ dependencies = [ "ctrlc", "dns-lookup", "etherparse", + "gag", "iced", "maxminddb", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 072b86c5..74615ab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,9 @@ phf_shared = "0.11.2" splines = "4.4.0" clap = { version = "4.5.23", features = ["derive"] } +[target.'cfg(windows)'.dependencies] +gag = "1.0.0" + [target.'cfg(not(target_arch = "powerpc64"))'.dependencies] reqwest = { version = "0.12.9", default-features = false, features = ["json", "blocking", "rustls-tls"] } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 6e74a36d..f8e617a2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -17,20 +17,43 @@ struct Args { /// Start sniffing packets from the supplied network adapter #[arg(short, long, value_name = "NAME", default_missing_value = CONFIGS.device.device_name.as_str(), num_args = 0..=1)] adapter: Option, + #[cfg(all(windows, not(debug_assertions)))] + /// Show the logs (stdout and stderr) of the most recent application run + #[arg(short, long, exclusive = true)] + logs: bool, /// Restore default settings - #[arg(short, long)] + #[arg(short, long, exclusive = true)] restore_default: bool, } -pub fn parse_cli_args() -> Task { - let mut boot_task_chain = window::get_latest().map(Message::WindowId); - +pub fn handle_cli_args() -> Task { let args = Args::parse(); + #[cfg(all(windows, not(debug_assertions)))] + if let Some(logs_file) = crate::utils::formatted_strings::get_logs_file_path() { + if args.logs { + std::process::Command::new("explorer") + .arg(logs_file) + .spawn() + .unwrap() + .wait() + .unwrap_or_default(); + std::process::exit(0); + } else { + // truncate logs file + let _ = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(logs_file); + } + } + if args.restore_default { Configs::default().store(); + std::process::exit(0); } + let mut boot_task_chain = window::get_latest().map(Message::WindowId); if let Some(adapter) = args.adapter { boot_task_chain = boot_task_chain .chain(Task::done(Message::AdapterSelection(adapter))) diff --git a/src/main.rs b/src/main.rs index 9811672d..4171dd12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use iced::{application, window, Font, Pixels, Settings}; use chart::types::chart_type::ChartType; use chart::types::traffic_chart::TrafficChart; -use cli::parse_cli_args; +use cli::handle_cli_args; use configs::types::config_device::ConfigDevice; use configs::types::config_settings::ConfigSettings; use gui::pages::types::running_page::RunningPage; @@ -55,9 +55,18 @@ pub const SNIFFNET_TITLECASE: &str = "Sniffnet"; /// /// It initializes shared variables and loads configuration parameters pub fn main() -> iced::Result { - let configs = CONFIGS.clone(); + #[cfg(all(windows, not(debug_assertions)))] + let _gag1: gag::Redirect; + #[cfg(all(windows, not(debug_assertions)))] + let _gag2: gag::Redirect; + #[cfg(all(windows, not(debug_assertions)))] + if let Some((gag1, gag2)) = utils::formatted_strings::redirect_stdout_stderr_to_file() { + _gag1 = gag1; + _gag2 = gag2; + } - let boot_task_chain = parse_cli_args(); + let configs = CONFIGS.clone(); + let boot_task_chain = handle_cli_args(); let configs1 = Arc::new(Mutex::new(configs)); let configs2 = configs1.clone(); diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index a022b766..3af31d96 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -165,6 +165,31 @@ pub fn get_formatted_num_seconds(num_seconds: u128) -> String { } } +#[allow(dead_code)] +#[cfg(windows)] +pub fn get_logs_file_path() -> Option { + let mut conf = confy::get_configuration_file_path(crate::SNIFFNET_LOWERCASE, "logs").ok()?; + conf.set_extension("txt"); + Some(conf.to_str()?.to_string()) +} + +#[cfg(all(windows, not(debug_assertions)))] +pub fn redirect_stdout_stderr_to_file( +) -> Option<(gag::Redirect, gag::Redirect)> { + if let Ok(logs_file) = std::fs::OpenOptions::new() + .write(true) + .create(true) + .append(true) + .open(get_logs_file_path()?) + { + return Some(( + gag::Redirect::stdout(logs_file.try_clone().ok()?).ok()?, + gag::Redirect::stderr(logs_file).ok()?, + )); + } + None +} + #[cfg(test)] mod tests { use super::*; @@ -200,4 +225,12 @@ mod tests { "94522879700260684295381835397713392:04:15" ); } + + #[cfg(windows)] + #[test] + fn test_logs_file_path() { + let file_path = std::path::PathBuf::from(get_logs_file_path().unwrap()); + assert!(file_path.is_absolute()); + assert_eq!(file_path.file_name().unwrap(), "logs.txt"); + } }