summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--mullvad-cli/src/cmds/mod.rs4
-rw-r--r--mullvad-cli/src/cmds/reset.rs44
-rw-r--r--mullvad-daemon/src/account_history.rs6
-rw-r--r--mullvad-daemon/src/lib.rs53
-rw-r--r--mullvad-daemon/src/management_interface.rs17
-rw-r--r--mullvad-ipc-client/src/lib.rs4
-rw-r--r--mullvad-types/src/settings.rs30
8 files changed, 156 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6de581ecd3..008daea001 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
- Add new settings page for generating and verifying wireguard keys.
+- Add `factory-reset` CLI command for removing settings, logs and clearing the cache.
### Changed
- Upgrade OpenVPN from 2.4.6 to 2.4.7.
diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs
index 83bfd47883..8caf26eac5 100644
--- a/mullvad-cli/src/cmds/mod.rs
+++ b/mullvad-cli/src/cmds/mod.rs
@@ -28,6 +28,9 @@ pub use self::relay::Relay;
mod lan;
pub use self::lan::Lan;
+mod reset;
+pub use self::reset::Reset;
+
mod tunnel;
pub use self::tunnel::Tunnel;
@@ -45,6 +48,7 @@ pub fn get_commands() -> HashMap<&'static str, Box<dyn Command>> {
Box::new(Disconnect),
Box::new(Lan),
Box::new(Relay),
+ Box::new(Reset),
Box::new(Status),
Box::new(Tunnel),
Box::new(Version),
diff --git a/mullvad-cli/src/cmds/reset.rs b/mullvad-cli/src/cmds/reset.rs
new file mode 100644
index 0000000000..d859d1e436
--- /dev/null
+++ b/mullvad-cli/src/cmds/reset.rs
@@ -0,0 +1,44 @@
+use crate::{new_rpc_client, Command, Result};
+use std::io::stdin;
+
+pub struct Reset;
+impl Command for Reset {
+ fn name(&self) -> &'static str {
+ "factory-reset"
+ }
+
+ fn clap_subcommand(&self) -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name(self.name()).about("Reset settings, caches and logs")
+ }
+
+ fn run(&self, _matches: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ if Self::receive_confirmation() {
+ if rpc.factory_reset().is_err() {
+ eprintln!("FAILED TO PERFORM FACTORY RESET");
+ } else {
+ #[cfg(target_os = "linux")]
+ println!("If you're running systemd, to remove all logs, you must use journalctl");
+ }
+ }
+ Ok(())
+ }
+}
+
+impl Reset {
+ fn receive_confirmation() -> bool {
+ println!("Are you sure you want to disconnect, log out, delete all settings, logs and cache files for the Mullvad VPN system service? [Yes/No (default)]");
+ loop {
+ let mut buf = String::new();
+ if let Err(e) = stdin().read_line(&mut buf) {
+ eprintln!("Couldn't read from STDIN - {}", e);
+ return false;
+ }
+ match buf.trim() {
+ "Yes" => return true,
+ "No" | "no" | "" => return false,
+ _ => println!("Unexpected response. Please enter \"Yes\" or \"No\""),
+ }
+ }
+ }
+}
diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs
index c8bb92c7a9..faecc5b5e7 100644
--- a/mullvad-daemon/src/account_history.rs
+++ b/mullvad-daemon/src/account_history.rs
@@ -147,6 +147,12 @@ impl AccountHistory {
self.save_to_disk()
}
+ /// Remove account history
+ pub fn clear(&mut self) -> Result<()> {
+ self.accounts = VecDeque::new();
+ self.save_to_disk()
+ }
+
fn save_to_disk(&mut self) -> Result<()> {
self.file.get_mut().set_len(0).map_err(Error::Write)?;
self.file
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 8309f42e81..5fb1f11a89 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -46,7 +46,7 @@ use mullvad_types::{
wireguard::KeygenEvent,
};
use settings::Settings;
-use std::{io, mem, path::PathBuf, sync::mpsc, thread, time::Duration};
+use std::{fs, io, mem, path::PathBuf, sync::mpsc, thread, time::Duration};
use talpid_core::{
mpsc::IntoSender,
tunnel::tun_provider::{PlatformTunProvider, TunProvider},
@@ -101,6 +101,15 @@ pub enum Error {
#[error(display = "Tunnel state machine error")]
TunnelError(#[error(cause)] tunnel_state_machine::Error),
+
+ #[error(display = "Failed to remove a directory")]
+ RemovalError(#[error(cause)] io::Error),
+
+ #[error(display = "Failed to create a directory")]
+ CreateDirError(#[error(cause)] io::Error),
+
+ #[error(display = "Failed to get path")]
+ PathError(#[error(cause)] mullvad_paths::Error),
}
type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>;
@@ -699,6 +708,7 @@ where
VerifyWireguardKey(tx) => self.on_verify_wireguard_key(tx),
GetVersionInfo(tx) => self.on_get_version_info(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
+ FactoryReset(tx) => self.on_factory_reset(tx),
Shutdown => self.handle_trigger_shutdown_event(),
}
}
@@ -932,6 +942,35 @@ where
Self::oneshot_send(tx, self.version.clone(), "get_current_version response");
}
+ fn on_factory_reset(&mut self, tx: oneshot::Sender<()>) {
+ self.set_target_state(TargetState::Unsecured);
+ let mut failed = false;
+
+ if let Err(e) = self.clear_cache_directory() {
+ log::error!("Failed to clear cache directory - {}", e);
+ failed = true;
+ }
+
+ if let Err(e) = self.clear_log_directory() {
+ log::error!("Failed to clear log directory - {}", e);
+ failed = true;
+ }
+
+ if let Err(e) = self.settings.reset() {
+ log::error!("Failed to reset settings - {}", e);
+ failed = true;
+ }
+
+ if let Err(e) = self.account_history.clear() {
+ log::error!("Failed to clear account history - {}", e);
+ failed = true;
+ }
+
+ if !failed {
+ Self::oneshot_send(tx, (), "factory_reset response");
+ }
+ }
+
fn on_update_relay_settings(&mut self, tx: oneshot::Sender<()>, update: RelaySettingsUpdate) {
let save_result = self.settings.update_relay_settings(update);
match save_result {
@@ -1293,6 +1332,18 @@ where
.expect("Tunnel state machine has stopped");
}
+ fn clear_log_directory(&self) -> Result<()> {
+ let log_dir = mullvad_paths::get_log_dir().map_err(Error::PathError)?;
+ fs::remove_dir_all(&log_dir).map_err(Error::RemovalError)?;
+ fs::create_dir_all(&log_dir).map_err(Error::CreateDirError)
+ }
+
+ fn clear_cache_directory(&self) -> Result<()> {
+ let cache_dir = mullvad_paths::cache_dir().map_err(Error::PathError)?;
+ fs::remove_dir_all(&cache_dir).map_err(Error::RemovalError)?;
+ fs::create_dir_all(&cache_dir).map_err(Error::CreateDirError)
+ }
+
pub fn shutdown_handle(&self) -> DaemonShutdownHandle {
DaemonShutdownHandle {
tx: self.tx.clone(),
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index a2eae7830d..04193ca9b5 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -151,6 +151,10 @@ build_rpc_trait! {
#[rpc(meta, name = "get_version_info")]
fn get_version_info(&self, Self::Metadata) -> BoxFuture<version::AppVersionInfo, Error>;
+ /// Remove all configuration and cache files
+ #[rpc(meta, name = "factory_reset")]
+ fn factory_reset(&self, Self::Metadata) -> BoxFuture<(), Error>;
+
#[pubsub(name = "daemon_event")] {
/// Subscribes to events from the daemon.
#[rpc(name = "daemon_event_subscribe")]
@@ -222,6 +226,8 @@ pub enum ManagementCommand {
GetVersionInfo(OneshotSender<BoxFuture<version::AppVersionInfo, mullvad_rpc::Error>>),
/// Get current version of the app
GetCurrentVersion(OneshotSender<version::AppVersion>),
+ /// Remove settings and clear the cache
+ FactoryReset(OneshotSender<()>),
/// Makes the daemon exit the main loop and quit.
Shutdown,
}
@@ -679,6 +685,17 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
+ fn factory_reset(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
+ log::debug!("factory_reset");
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::FactoryReset(tx))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+
+ Box::new(future)
+ }
+
+
fn daemon_event_subscribe(
&self,
_: Self::Metadata,
diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs
index 98b6206ba4..bf6ea760a7 100644
--- a/mullvad-ipc-client/src/lib.rs
+++ b/mullvad-ipc-client/src/lib.rs
@@ -204,6 +204,10 @@ impl DaemonRpcClient {
self.call("shutdown", &NO_ARGS)
}
+ pub fn factory_reset(&mut self) -> Result<()> {
+ self.call("factory_reset", &NO_ARGS)
+ }
+
pub fn update_relay_settings(&mut self, update: RelaySettingsUpdate) -> Result<()> {
self.call("update_relay_settings", &[update])
}
diff --git a/mullvad-types/src/settings.rs b/mullvad-types/src/settings.rs
index 41f280c973..dab81bf43a 100644
--- a/mullvad-types/src/settings.rs
+++ b/mullvad-types/src/settings.rs
@@ -5,8 +5,15 @@ use crate::relay_constraints::{
use log::{debug, info};
use serde::{Deserialize, Serialize};
use serde_json;
-use std::{fs::File, io, path::PathBuf};
-use talpid_types::net::{openvpn, wireguard, GenericTunnelOptions};
+use std::{
+ fs::{self, File},
+ io,
+ path::PathBuf,
+};
+use talpid_types::{
+ net::{openvpn, wireguard, GenericTunnelOptions},
+ ErrorExt,
+};
pub type Result<T> = std::result::Result<T, Error>;
@@ -19,6 +26,9 @@ pub enum Error {
#[error(display = "Unable to read settings from {}", _0)]
ReadError(String, #[error(cause)] io::Error),
+ #[error(display = "Unable to remove settings file {}", _0)]
+ DeleteError(String, #[error(cause)] io::Error),
+
#[error(display = "Malformed settings")]
ParseError(#[error(cause)] serde_json::Error),
@@ -101,6 +111,22 @@ impl Settings {
.map_err(|e| Error::WriteError(path.display().to_string(), e))
}
+ /// Resets default settings
+ pub fn reset(&mut self) -> Result<()> {
+ *self = Default::default();
+ self.save().or_else(|e| {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Unable to save default settings")
+ );
+ log::error!("Will attempt to remove settings file");
+ Self::get_settings_path().and_then(|path| {
+ fs::remove_file(&path)
+ .map_err(|e| Error::DeleteError(path.display().to_string(), e))
+ })
+ })
+ }
+
fn get_settings_path() -> Result<PathBuf> {
let dir = ::mullvad_paths::settings_dir().map_err(Error::DirectoryError)?;
Ok(dir.join(SETTINGS_FILE))