Skip to content

Commit

Permalink
fixup! WIP Reset daemon environment if needed in cleanup routine
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusPettersson98 committed Apr 9, 2024
1 parent 821efdf commit 22d7b71
Showing 1 changed file with 143 additions and 14 deletions.
157 changes: 143 additions & 14 deletions test/test-runner/src/sys.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;
#[cfg(target_os = "windows")]
use std::io;
use std::{collections::HashMap, str::FromStr};
use test_rpc::{meta::OsVersion, mullvad_daemon::Verbosity};

#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -154,6 +154,7 @@ pub fn reboot() -> Result<(), test_rpc::Error> {
#[cfg(target_os = "linux")]
pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test_rpc::Error> {
use tokio::io::AsyncWriteExt;
// TODO(markus): Only define once
const SYSTEMD_OVERRIDE_FILE: &str =
"/etc/systemd/system/mullvad-daemon.service.d/override.conf";

Expand Down Expand Up @@ -388,9 +389,63 @@ pub async fn set_daemon_log_level(_verbosity_level: Verbosity) -> Result<(), tes
Ok(())
}

pub async fn get_daemon_environment() -> Result<HashMap<String, String>, test_rpc::Error> {
let env = HashMap::new();
Ok(env)
// TODO(markus): Move somewhere sane!
#[derive(Debug)]
struct EnvVar {
var: String,
value: String,
}

impl<K: Into<String>, V: Into<String>> From<(K, V)> for EnvVar {
fn from(value: (K, V)) -> Self {
Self {
var: value.0.into(),
value: value.1.into(),
}
}
}

// TODO(markus): This is only valid on Linux
impl std::str::FromStr for EnvVar {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
// Here, we are only concerned with parsing a line that starts with "Environment".
let error = "Failed to parse systemd env-config";
let mut input = s.trim().split('=');
let pre = input.next().ok_or(error)?;
match pre {
"Environment" => {
// Pre-proccess the input just a bit more - remove the leading and trailing quote (").
let var = {
let var = input.next().ok_or(error)?;
var[1..].to_string()
};
let value = {
let value = input.next().ok_or(error)?;
value[..(value.len() - 1)].to_string()
};
Ok(EnvVar { var, value })
}
_ => Err(error),
}
}
}

impl EnvVar {
/// Convert `self` into a tuple of its parts.
pub fn tuple(self) -> (String, String) {
(self.var, self.value)
}

#[cfg(target_os = "linux")]
fn to_systemd_string(&self) -> String {
format!(
"Environment=\"{key}={value}\"",
key = self.var,
value = self.value
)
}
}

#[cfg(target_os = "linux")]
Expand All @@ -403,8 +458,9 @@ pub async fn set_daemon_environment(env: HashMap<String, String>) -> Result<(),
let mut override_content = String::new();
override_content.push_str("[Service]\n");

for (k, v) in env {
writeln!(&mut override_content, "Environment=\"{k}={v}\"").unwrap();
for var in env.iter().map(EnvVar::from) {
writeln!(&mut override_content, "{}", var.to_systemd_string())
.map_err(|err| test_rpc::Error::Service(err.to_string()))?;
}

let override_path = std::path::Path::new(SYSTEMD_OVERRIDE_FILE);
Expand All @@ -414,15 +470,10 @@ pub async fn set_daemon_environment(env: HashMap<String, String>) -> Result<(),
.map_err(|e| test_rpc::Error::Service(e.to_string()))?;
}

let mut file = tokio::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(override_path)
tokio::fs::File::create(override_path)
.await
.map_err(|e| test_rpc::Error::Service(e.to_string()))?;

file.write_all(override_content.as_bytes())
.map_err(|e| test_rpc::Error::Service(e.to_string()))?
.write_all(override_content.as_bytes())
.await
.map_err(|e| test_rpc::Error::Service(e.to_string()))?;

Expand Down Expand Up @@ -545,6 +596,57 @@ async fn set_launch_daemon_state(on: bool) -> Result<(), test_rpc::Error> {
Ok(())
}

#[cfg(target_os = "linux")]
pub async fn get_daemon_environment() -> Result<HashMap<String, String>, test_rpc::Error> {
use tokio::fs::File;
use tokio::io::AsyncReadExt;
const SYSTEMD_OVERRIDE_FILE: &str = "/etc/systemd/system/mullvad-daemon.service.d/env.conf";

let mut file = File::open(SYSTEMD_OVERRIDE_FILE)
.await
.map_err(|err| test_rpc::Error::FileSystem(err.to_string()))?;
let text = {
let mut buf = String::new();
file.read_to_string(&mut buf)
.await
.map_err(|err| test_rpc::Error::FileSystem(err.to_string()))?;
buf
};

let env: HashMap<String, String> = parse_systemd_env_file(&text)
.into_iter()
.map(EnvVar::tuple)
.collect();
Ok(env)
}

/// Parse a systemd env-file.
/// TODO(markus): Define what the `input` parameter is.
///
/// Example systemd-env file:
/// [Service]
/// Environment="VAR1=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN"
/// Environment="VAR2=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"
fn parse_systemd_env_file(input: &str) -> impl IntoIterator<Item = EnvVar> + '_ {
input
.lines()
// Skip the [Service] line
.skip(1)
.map(EnvVar::from_str)
.filter_map(|env_var| env_var.ok())
.inspect(|env_var| println!("Parsed {env_var:?}"))
}

#[cfg(target_os = "windows")]
pub async fn get_daemon_environment() -> Result<HashMap<String, String>, test_rpc::Error> {
todo!("Not implemented on Windows")
}

#[cfg(target_os = "macos")]
pub async fn get_daemon_environment() -> Result<HashMap<String, String>, test_rpc::Error> {
todo!("Not implemented on macOS")
}

#[cfg(target_os = "linux")]
enum ServiceState {
Running,
Expand Down Expand Up @@ -623,3 +725,30 @@ pub fn get_os_version() -> Result<OsVersion, test_rpc::Error> {
pub fn get_os_version() -> Result<OsVersion, test_rpc::Error> {
Ok(OsVersion::Linux)
}

#[cfg(test)]
mod test {

#[test]
fn parse_systemd_environment_variables() {
use super::parse_systemd_env_file;
// Define an example systemd environment file
let systemd_file = "
[Service]
Environment=\"var1=value1\"
Environment=\"var2=value2\"
";

// Parse the "file"
let env_vars: Vec<_> = parse_systemd_env_file(systemd_file).into_iter().collect();

// Assert that the environment variables it defines are parsed as expected.
assert_eq!(env_vars.len(), 2);
let first = env_vars.first().unwrap();
assert_eq!(first.var, "var1");
assert_eq!(first.value, "value1");
let second = env_vars.get(1).unwrap();
assert_eq!(second.var, "var2");
assert_eq!(second.value, "value2");
}
}

0 comments on commit 22d7b71

Please sign in to comment.