From 546928b5290c836f67096ee7a8b51f38d9836abc Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 5 Aug 2024 17:58:21 +0200 Subject: [PATCH 1/3] Add --json flag to `mullvad status` --- Cargo.lock | 17 ++++++----- mullvad-cli/Cargo.toml | 2 ++ mullvad-cli/src/cmds/status.rs | 56 ++++++++++++++++++++++------------ mullvad-types/src/device.rs | 6 ++-- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d90d38adc08..6b387409b427 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1934,7 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -2175,6 +2175,8 @@ dependencies = [ "mullvad-types", "mullvad-version", "natord", + "serde", + "serde_json", "talpid-types", "thiserror", "tokio", @@ -3503,18 +3505,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -3523,11 +3525,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index 516296eef0c6..2a95f743af8f 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -29,6 +29,8 @@ talpid-types = { path = "../talpid-types" } mullvad-management-interface = { path = "../mullvad-management-interface" } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "fs"] } +serde_json = "1.0.122" +serde = "1.0.204" [target.'cfg(all(unix, not(target_os = "android")))'.dependencies] clap_complete = { version = "4.4.8" } diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs index 96631d02d0be..d7e646f42369 100644 --- a/mullvad-cli/src/cmds/status.rs +++ b/mullvad-cli/src/cmds/status.rs @@ -1,8 +1,10 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use clap::{Args, Subcommand}; use futures::StreamExt; use mullvad_management_interface::{client::DaemonEvent, MullvadProxyClient}; use mullvad_types::{device::DeviceState, states::TunnelState}; +use serde::Serialize; +use std::fmt::Debug; use crate::format; @@ -19,8 +21,12 @@ pub struct StatusArgs { verbose: bool, /// Enable debug output - #[arg(long, short = 'd')] + #[arg(long, short = 'd', conflicts_with_all = ["verbose", "json"])] debug: bool, + + /// Format output as JSON + #[arg(long, short = 'j', conflicts_with_all = ["verbose", "debug"])] + json: bool, } impl Status { @@ -33,6 +39,10 @@ impl Status { DaemonEvent::TunnelState(new_state) => { if args.debug { println!("New tunnel state: {new_state:#?}"); + } else if args.json { + let json = serde_json::to_string(&new_state) + .context("Failed to format output as JSON")?; + println!("{json}"); } else { // When we enter the connected or disconnected state, am.i.mullvad.net will // be polled to get exit location. When it arrives, we will get another @@ -70,34 +80,22 @@ impl Status { } } DaemonEvent::Settings(settings) => { - if args.debug { - println!("New settings: {settings:#?}"); - } + print_debug_or_json(&args, "New settings", &settings)?; } DaemonEvent::RelayList(relay_list) => { - if args.debug { - println!("New relay list: {relay_list:#?}"); - } + print_debug_or_json(&args, "New relay list", &relay_list)?; } DaemonEvent::AppVersionInfo(app_version_info) => { - if args.debug { - println!("New app version info: {app_version_info:#?}"); - } + print_debug_or_json(&args, "New app version info", &app_version_info)?; } DaemonEvent::Device(device) => { - if args.debug { - println!("Device event: {device:#?}"); - } + print_debug_or_json(&args, "Device event", &device)?; } DaemonEvent::RemoveDevice(device) => { - if args.debug { - println!("Remove device event: {device:#?}"); - } + print_debug_or_json(&args, "Remove device event", &device)?; } DaemonEvent::NewAccessMethod(access_method) => { - if args.debug { - println!("New access method: {access_method:#?}"); - } + print_debug_or_json(&args, "New access method", &access_method)?; } } } @@ -114,6 +112,9 @@ pub async fn handle(cmd: Option, args: StatusArgs) -> Result<()> { if args.debug { println!("Tunnel state: {state:#?}"); + } else if args.json { + let json = serde_json::to_string(&state).context("Failed to format output as JSON")?; + println!("{json}"); } else { format::print_state(&state, args.verbose); format::print_location(&state); @@ -139,3 +140,18 @@ fn print_account_logged_out(state: &TunnelState, device: &DeviceState) { TunnelState::Disconnected { .. } | TunnelState::Disconnecting(_) => (), } } + +fn print_debug_or_json( + args: &StatusArgs, + debug_message: &str, + t: &T, +) -> Result<()> { + if args.debug { + println!("{debug_message}: {t:#?}"); + } else if args.json { + let json = serde_json::to_string(&t).context("Failed to format output as JSON")?; + println!("{json}"); + } + + Ok(()) +} diff --git a/mullvad-types/src/device.rs b/mullvad-types/src/device.rs index b4e1f796046c..140815414df3 100644 --- a/mullvad-types/src/device.rs +++ b/mullvad-types/src/device.rs @@ -86,7 +86,7 @@ impl AccountAndDevice { } /// Reason why a [DeviceEvent] was emitted. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub enum DeviceEventCause { /// Logged in on a new device. LoggedIn, @@ -101,7 +101,7 @@ pub enum DeviceEventCause { } /// Emitted when logging in or out of an account, or when the device changes. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct DeviceEvent { pub cause: DeviceEventCause, pub new_state: DeviceState, @@ -109,7 +109,7 @@ pub struct DeviceEvent { /// Emitted when a device is removed using the `RemoveDevice` RPC. /// This is not sent by a normal logout or when it is revoked remotely. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct RemoveDeviceEvent { pub account_token: AccountToken, pub new_devices: Vec, From 9f13cbd94e7550efd57b3cf0115a258ef52297f3 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 6 Aug 2024 10:41:56 +0200 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e5be200b9c..9fdc22eebdc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Line wrap the file at 100 chars. Th ## [Unreleased] ### Added - Add DAITA (Defence against AI-guided Traffic Analysis) setting for Linux and macOS. +- Add `--json` flag to `mullvad status` CLI. ### Changed - Ignore obfuscation protocol constraints when the obfuscation mode is set to auto. From 9df36ce0435771b60a4b95466ae1b01a6d1f71c4 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 6 Aug 2024 16:08:09 +0200 Subject: [PATCH 3/3] Promote serde(_json) to workspace dependencies --- Cargo.toml | 2 ++ android/translations-converter/Cargo.toml | 2 +- mullvad-api/Cargo.toml | 4 ++-- mullvad-cli/Cargo.toml | 4 ++-- mullvad-daemon/Cargo.toml | 4 ++-- mullvad-relay-selector/Cargo.toml | 2 +- mullvad-types/Cargo.toml | 2 +- talpid-core/Cargo.toml | 4 ++-- talpid-types/Cargo.toml | 2 +- test/Cargo.lock | 13 +++++++------ test/connection-checker/Cargo.toml | 2 +- 11 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac63e1a4ca2e..c3b1185cbbda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,8 @@ windows-sys = "0.52.0" chrono = { version = "0.4.26", default-features = false } clap = { version = "4.4.18", features = ["cargo", "derive"] } once_cell = "1.13" +serde = "1.0.204" +serde_json = "1.0.122" ipnetwork = "0.20" diff --git a/android/translations-converter/Cargo.toml b/android/translations-converter/Cargo.toml index 57deb26a57a2..9bf902b17229 100644 --- a/android/translations-converter/Cargo.toml +++ b/android/translations-converter/Cargo.toml @@ -15,5 +15,5 @@ thiserror = { workspace = true } htmlize = { version = "1.0.2", features = ["unescape"] } once_cell = { workspace = true } regex = "1" -serde = { version = "1", features = ["derive"] } +serde = { workspace = true, features = ["derive"] } quick-xml = { version = "0.27.1", features = ["serialize"] } diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index 6958abb5bb6c..10b1cddce1ce 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -23,8 +23,8 @@ http = "0.2" hyper = { version = "0.14", features = ["client", "stream", "http1", "tcp" ] } ipnetwork = { workspace = true } log = { workspace = true } -serde = "1" -serde_json = "1.0" +serde = { workspace = true } +serde_json = { workspace = true } tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread", "net", "io-std", "io-util", "fs"] } tokio-rustls = "0.24.1" tokio-socks = "0.5.1" diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index 2a95f743af8f..5be2adfd5757 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -29,8 +29,8 @@ talpid-types = { path = "../talpid-types" } mullvad-management-interface = { path = "../mullvad-management-interface" } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "fs"] } -serde_json = "1.0.122" -serde = "1.0.204" +serde = { workspace = true } +serde_json = { workspace = true } [target.'cfg(all(unix, not(target_os = "android")))'.dependencies] clap_complete = { version = "4.4.8" } diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index ca7c46d8ca31..41172ad48674 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -23,8 +23,8 @@ once_cell = { workspace = true } libc = "0.2" log = { workspace = true } regex = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } tokio = { workspace = true, features = ["fs", "io-util", "rt-multi-thread", "sync", "time"] } tokio-stream = "0.1" diff --git a/mullvad-relay-selector/Cargo.toml b/mullvad-relay-selector/Cargo.toml index 0fb700d1f5c2..7f8436bacb24 100644 --- a/mullvad-relay-selector/Cargo.toml +++ b/mullvad-relay-selector/Cargo.toml @@ -18,7 +18,7 @@ itertools = "0.12" log = { workspace = true } once_cell = { workspace = true } rand = "0.8.5" -serde_json = "1.0" +serde_json = { workspace = true } talpid-types = { path = "../talpid-types" } mullvad-types = { path = "../mullvad-types" } diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml index 864a7465e6e5..033743506407 100644 --- a/mullvad-types/Cargo.toml +++ b/mullvad-types/Cargo.toml @@ -17,7 +17,7 @@ ipnetwork = { workspace = true } once_cell = { workspace = true } log = { workspace = true } regex = "1" -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true, features = ["derive"] } uuid = { version = "1.4.1", features = ["v4", "serde" ] } talpid-types = { path = "../talpid-types" } diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index f7c67b9be72d..22515b3cc212 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -58,8 +58,8 @@ pcap = { version = "2.0", features = ["capture-stream"] } pnet_packet = "0.34" tun = { version = "0.5.5", features = ["async"] } nix = { version = "0.28", features = ["socket"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } [target.'cfg(windows)'.dependencies] bitflags = "1.2" diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml index e9e989f36bc4..d6e9ddc69f7c 100644 --- a/talpid-types/Cargo.toml +++ b/talpid-types/Cargo.toml @@ -11,7 +11,7 @@ rust-version.workspace = true workspace = true [dependencies] -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true, features = ["derive"] } ipnetwork = { workspace = true } base64 = "0.22.0" x25519-dalek = { version = "2.0.1", features = ["static_secrets", "zeroize", "getrandom"] } diff --git a/test/Cargo.lock b/test/Cargo.lock index 7632b6d903b3..b2c01ff0696b 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -2904,18 +2904,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -2924,11 +2924,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] diff --git a/test/connection-checker/Cargo.toml b/test/connection-checker/Cargo.toml index d579510bd1e8..a09292e42650 100644 --- a/test/connection-checker/Cargo.toml +++ b/test/connection-checker/Cargo.toml @@ -16,5 +16,5 @@ color-eyre = "0.6.2" eyre = "0.6.12" ping = "0.5.2" reqwest = { version = "0.11.24", default-features = false, features = ["blocking", "rustls-tls", "json"] } -serde = { version = "1.0.197", features = ["derive"] } +serde = { workspace = true, features = ["derive"] } socket2 = { version = "0.5.4", features = ["all"] }