diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-07-17 11:29:29 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-07-17 17:49:44 +0200 |
| commit | 81f6995c7421238026f3aeff39ee88cab6fbb001 (patch) | |
| tree | db980526b6fba21b9b8265d867cf08882d86c3c1 | |
| parent | 90588bced678c1ecbf7db0190c7c039d79e94d3e (diff) | |
| download | mullvadvpn-81f6995c7421238026f3aeff39ee88cab6fbb001.tar.xz mullvadvpn-81f6995c7421238026f3aeff39ee88cab6fbb001.zip | |
Read and write settings to toml file
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 29 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 85 |
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)) + } +} |
