summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-01-09 11:29:53 +0100
committerDavid Lönnhager <david.l@mullvad.net>2024-01-10 10:42:50 +0100
commiteb5496bc68ee4f2d1532930d87261a75e47e4241 (patch)
tree6f480bc70ecb2afa315356b9c1b1a702c6f447a8
parentd3b2a52c765ac8588430834de4a9be2a36598a20 (diff)
downloadmullvadvpn-eb5496bc68ee4f2d1532930d87261a75e47e4241.tar.xz
mullvadvpn-eb5496bc68ee4f2d1532930d87261a75e47e4241.zip
Add CLI export-settings command
-rw-r--r--mullvad-cli/src/cmds/patch.rs53
-rw-r--r--mullvad-cli/src/main.rs9
2 files changed, 57 insertions, 5 deletions
diff --git a/mullvad-cli/src/cmds/patch.rs b/mullvad-cli/src/cmds/patch.rs
index d5a79aaef9..c935c08aac 100644
--- a/mullvad-cli/src/cmds/patch.rs
+++ b/mullvad-cli/src/cmds/patch.rs
@@ -2,15 +2,15 @@ use anyhow::{anyhow, Context, Result};
use mullvad_management_interface::MullvadProxyClient;
use std::{
fs::File,
- io::{stdin, BufRead, BufReader},
+ io::{stdin, stdout, BufRead, BufReader, BufWriter, Write},
path::Path,
};
/// Maximum size of a settings patch. Bigger files/streams cause the read to fail.
const MAX_PATCH_BYTES: usize = 10 * 1024;
-/// If source is specified, read from the provided file and send it as a settings patch to the daemon.
-/// Otherwise, read the patch from standard input.
+/// If source is specified, read from the provided file and send it as a settings patch to the
+/// daemon. Otherwise, read the patch from standard input.
pub async fn import(source: String) -> Result<()> {
let json_blob = tokio::task::spawn_blocking(|| get_blob(source))
.await
@@ -26,10 +26,28 @@ pub async fn import(source: String) -> Result<()> {
Ok(())
}
+/// If source is specified, write a patch to the file. Otherwise, write the patch to standard
+/// output.
+pub async fn export(dest: String) -> Result<()> {
+ let mut rpc = MullvadProxyClient::new().await?;
+ let json_blob = rpc
+ .export_json_settings()
+ .await
+ .context("Error exporting patch")?;
+
+ tokio::task::spawn_blocking(|| put_blob(dest, json_blob))
+ .await
+ .unwrap()?;
+
+ Ok(())
+}
+
fn get_blob(source: String) -> Result<String> {
match source.as_str() {
"-" => read_settings_from_stdin().context("Failed to read from stdin"),
- _ => read_settings_from_file(source).context("Failed to read from path: {source}"),
+ _ => {
+ read_settings_from_file(&source).context(format!("Failed to read from path: {source}"))
+ }
}
}
@@ -99,3 +117,30 @@ fn read_settings_from_reader(mut reader: impl BufRead) -> Result<String> {
.context("settings must be utf8 encoded")?
.to_owned())
}
+
+fn put_blob(dest: String, blob: String) -> Result<()> {
+ match dest.as_str() {
+ "-" => write_settings_to_stdout(blob).context("Failed to write to stdout"),
+ _ => write_settings_to_file(&dest, blob).context(format!("Failed to write to path {dest}")),
+ }
+}
+
+/// Write patch to standard output
+fn write_settings_to_stdout(blob: String) -> Result<()> {
+ write_settings_using_writer(BufWriter::new(stdout()), blob)
+}
+
+/// Write patch to path
+fn write_settings_to_file(path: impl AsRef<Path>, blob: String) -> Result<()> {
+ write_settings_using_writer(
+ BufWriter::new(File::options().create(true).write(true).open(path)?),
+ blob,
+ )
+}
+
+fn write_settings_using_writer(mut writer: impl Write, blob: String) -> Result<()> {
+ writer
+ .write_all(blob.as_bytes())
+ .context("Failed to write blob to destination")?;
+ writer.write_all(b"\n").context("Failed to write newline")
+}
diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs
index a5887b39d5..c051f0246f 100644
--- a/mullvad-cli/src/main.rs
+++ b/mullvad-cli/src/main.rs
@@ -138,11 +138,17 @@ enum Cli {
#[clap(subcommand)]
CustomList(custom_list::CustomList),
- /// Apply a JSON patch
+ /// Apply a JSON patch generated by 'export-settings'
ImportSettings {
/// File to read from. If this is "-", read from standard input
file: String,
},
+
+ /// Export a JSON patch based on the current settings
+ ExportSettings {
+ /// File to write to. If this is "-", write to standard output
+ file: String,
+ },
}
#[tokio::main]
@@ -170,6 +176,7 @@ async fn main() -> Result<()> {
Cli::Status { cmd, args } => status::handle(cmd, args).await,
Cli::CustomList(cmd) => cmd.handle().await,
Cli::ImportSettings { file } => patch::import(file).await,
+ Cli::ExportSettings { file } => patch::export(file).await,
#[cfg(all(unix, not(target_os = "android")))]
Cli::ShellCompletions { shell, dir } => {