summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-07-17 11:29:29 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-07-17 17:49:44 +0200
commit81f6995c7421238026f3aeff39ee88cab6fbb001 (patch)
treedb980526b6fba21b9b8265d867cf08882d86c3c1
parent90588bced678c1ecbf7db0190c7c039d79e94d3e (diff)
downloadmullvadvpn-81f6995c7421238026f3aeff39ee88cab6fbb001.tar.xz
mullvadvpn-81f6995c7421238026f3aeff39ee88cab6fbb001.zip
Read and write settings to toml file
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/main.rs29
-rw-r--r--mullvad-daemon/src/settings.rs85
3 files changed, 109 insertions, 7 deletions
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index e70e03280c..34341f1801 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -8,6 +8,7 @@ description = "Mullvad VPN backend daemon. Runs and controls the VPN tunnels."
name = "mullvadd"
[dependencies]
+app_dirs = "1.1"
chrono = "0.4"
clap = "2.25"
error-chain = "0.10"
@@ -21,6 +22,7 @@ jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc" }
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc" }
uuid = { version = "0.5", features = ["v4"] }
lazy_static = "0.2"
+toml = "0.4"
mullvad-types = { path = "../mullvad-types" }
talpid-core = { path = "../talpid-core" }
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index b26ca315a8..505a9b41b0 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -27,6 +27,7 @@ extern crate talpid_ipc;
mod cli;
mod management_interface;
mod rpc_info;
+mod settings;
mod shutdown;
use error_chain::ChainedError;
@@ -73,7 +74,7 @@ lazy_static! {
];
}
-static CRATE_NAME: &str = "mullvadd";
+const CRATE_NAME: &str = "mullvadd";
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
@@ -140,12 +141,11 @@ struct Daemon {
rx: mpsc::Receiver<DaemonEvent>,
tx: mpsc::Sender<DaemonEvent>,
management_interface_broadcaster: management_interface::EventBroadcaster,
+ settings: settings::Settings,
// Just for testing. A cyclic iterator iterating over the hardcoded remotes,
// picking a new one for each retry.
remote_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, Endpoint>>>,
- // The current account token for now. Should be moved into the settings later.
- account_token: Option<String>,
}
impl Daemon {
@@ -167,8 +167,8 @@ impl Daemon {
rx,
tx,
management_interface_broadcaster,
+ settings: settings::Settings::load().chain_err(|| "Unable to read settings")?,
remote_iter: REMOTES.iter().cloned().cycle(),
- account_token: None,
},
)
}
@@ -271,9 +271,17 @@ impl Daemon {
warn!("Unable to send current state to management interface client",);
}
}
- SetAccount(account_token) => self.account_token = account_token,
+ SetAccount(account_token) => {
+ info!(
+ "Changing account token from {} to {}",
+ self.settings.account_token.as_ref().unwrap_or(&"[nothing]".to_owned()),
+ account_token.as_ref().unwrap_or(&"[nothing]".to_owned())
+ );
+ self.settings.account_token = account_token;
+ self.save_settings();
+ }
GetAccount(tx) => {
- if let Err(_) = tx.send(self.account_token.clone()) {
+ if let Err(_) = tx.send(self.settings.account_token.clone()) {
warn!("Unable to send current account to management interface client");
}
}
@@ -294,6 +302,12 @@ impl Daemon {
self.set_target_state(TargetState::Unsecured)
}
+ fn save_settings(&self) {
+ if let Err(e) = self.settings.save().chain_err(|| "Unable to save settings") {
+ error!("{}", e.display());
+ }
+ }
+
/// Update the state of the client. If it changed, notify the subscribers and trigger
/// appropriate actions.
fn set_state(&mut self, new_state: TunnelState) -> Result<()> {
@@ -372,7 +386,8 @@ impl Daemon {
ErrorKind::InvalidState
);
let remote = self.remote_iter.next().unwrap();
- let account_token = self.account_token
+ let account_token = self.settings
+ .account_token
.as_ref()
.ok_or(ErrorKind::InvalidSettings("No account token"))?
.clone();
diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs
new file mode 100644
index 0000000000..b22888e76e
--- /dev/null
+++ b/mullvad-daemon/src/settings.rs
@@ -0,0 +1,85 @@
+extern crate app_dirs;
+extern crate toml;
+
+use self::app_dirs::{AppDataType, AppInfo};
+
+use std::fs::File;
+use std::io::{self, Read, Write};
+use std::path::PathBuf;
+
+error_chain! {
+ errors {
+ DirectoryError {
+ description("Unable to create settings directory for program")
+ }
+ ReadError(path: PathBuf) {
+ description("Unable to read settings file")
+ display("Unable to read settings from {}", path.to_string_lossy())
+ }
+ WriteError(path: PathBuf) {
+ description("Unable to write settings file")
+ display("Unable to write settings to {}", path.to_string_lossy())
+ }
+ ParseError {
+ description("Malformed settings")
+ }
+ }
+}
+
+static APP_INFO: AppInfo = AppInfo {
+ name: ::CRATE_NAME,
+ author: "Mullvad",
+};
+
+static SETTINGS_FILE: &str = "settings.toml";
+
+#[derive(Debug, Clone, Deserialize, Serialize, Default)]
+pub struct Settings {
+ pub account_token: Option<String>,
+}
+
+impl Settings {
+ /// Loads user settings from file. If no file is present it returns the defaults.
+ pub fn load() -> Result<Settings> {
+ let settings_path = Self::get_settings_path()?;
+ match File::open(&settings_path) {
+ Ok(mut file) => {
+ info!("Loading settings from {}", settings_path.to_string_lossy());
+ Self::read_settings(&mut file, settings_path)
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
+ info!(
+ "No settings file at {}, using defaults",
+ settings_path.to_string_lossy()
+ );
+ Ok(Settings::default())
+ }
+ Err(e) => Err(e).chain_err(|| ErrorKind::ReadError(settings_path)),
+ }
+ }
+
+ /// Serializes the settings and saves them to the file it was loaded from.
+ pub fn save(&self) -> Result<()> {
+ let settings_path = Self::get_settings_path()?;
+ let data = toml::to_string(self).chain_err(|| ErrorKind::ParseError)?;
+
+ debug!("Writing settings to {}", settings_path.to_string_lossy());
+ let mut file = File::create(&settings_path)
+ .chain_err(|| ErrorKind::WriteError(settings_path.clone()))?;
+ file.write_all(data.as_bytes()).chain_err(|| ErrorKind::WriteError(settings_path))?;
+ Ok(())
+ }
+
+
+ fn read_settings(file: &mut File, path: PathBuf) -> Result<Settings> {
+ let mut data = Vec::new();
+ file.read_to_end(&mut data).chain_err(|| ErrorKind::ReadError(path))?;
+ toml::from_slice(&data).chain_err(|| ErrorKind::ParseError)
+ }
+
+ fn get_settings_path() -> Result<PathBuf> {
+ let dir = app_dirs::app_root(AppDataType::UserConfig, &APP_INFO)
+ .chain_err(|| ErrorKind::DirectoryError)?;
+ Ok(dir.join(SETTINGS_FILE))
+ }
+}