summaryrefslogtreecommitdiffhomepage
path: root/src/process
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-03-06 15:27:25 +0100
committerLinus Färnstrand <linus@mullvad.net>2017-03-06 15:27:25 +0100
commitc354d79ea9634f77eb1a58b3c22d9e8f8229454f (patch)
treee644230f3cb7e27d0a74624e21b3ca0537989be1 /src/process
parentc8f40a5088199ad18078418c503277dd24f6dfc8 (diff)
parent379172f8b87aebc9f2e7c4327d6d3ef744f9dc93 (diff)
downloadmullvadvpn-c354d79ea9634f77eb1a58b3c22d9e8f8229454f.tar.xz
mullvadvpn-c354d79ea9634f77eb1a58b3c22d9e8f8229454f.zip
Merge branch 'openvpn-command-plugin'
Diffstat (limited to 'src/process')
-rw-r--r--src/process/mod.rs181
-rw-r--r--src/process/openvpn.rs194
2 files changed, 200 insertions, 175 deletions
diff --git a/src/process/mod.rs b/src/process/mod.rs
index a82893d64a..3259f90524 100644
--- a/src/process/mod.rs
+++ b/src/process/mod.rs
@@ -1,123 +1,15 @@
-use net::{RemoteAddr, ToRemoteAddrs};
-
-use std::ffi::{OsString, OsStr};
-use std::fmt;
use std::io;
-use std::path::{Path, PathBuf};
-use std::process::{Command, Child, Stdio, ChildStdout, ChildStderr};
+
+use std::process::{ChildStderr, ChildStdout};
/// A module for monitoring child processes and get notified of events on them.
pub mod monitor;
-use self::monitor::{MonitoredChild, ChildSpawner};
+use self::monitor::MonitoredChild;
-use clonablechild::{ClonableChild, ChildExt};
-
-/// An OpenVPN process builder, providing control over the different arguments that the OpenVPN
-/// binary accepts.
-pub struct OpenVpnCommand {
- openvpn_bin: OsString,
- config: Option<PathBuf>,
- remotes: Vec<RemoteAddr>,
- pipe_output: bool,
-}
+/// A module for all OpenVPN related process management.
+pub mod openvpn;
-impl OpenVpnCommand {
- /// Constructs a new `OpenVpnCommand` for launching OpenVPN processes from the binary at
- /// `openvpn_bin`.
- pub fn new<P: AsRef<OsStr>>(openvpn_bin: P) -> Self {
- OpenVpnCommand {
- openvpn_bin: OsString::from(openvpn_bin.as_ref()),
- config: None,
- remotes: vec![],
- pipe_output: true,
- }
- }
-
- /// Sets what configuration file will be given to OpenVPN
- pub fn config<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
- self.config = Some(path.as_ref().to_path_buf());
- self
- }
-
- /// Sets the addresses that OpenVPN will connect to. See OpenVPN documentation for how multiple
- /// remotes are handled.
- pub fn remotes<A: ToRemoteAddrs>(&mut self, remotes: A) -> io::Result<&mut Self> {
- self.remotes = remotes.to_remote_addrs()?.collect();
- Ok(self)
- }
-
- /// If piping the standard streams, stdout and stderr will be available to the parent process.
- /// This is the default behavior. If you want the equivalence of attaching the child streams to
- /// /dev/null, invoke this method with false.
- pub fn pipe_output(&mut self, pipe_output: bool) -> &mut Self {
- self.pipe_output = pipe_output;
- self
- }
-
- /// Executes the OpenVPN process as a child process, returning a handle to it.
- pub fn spawn(&self) -> io::Result<Child> {
- let mut command = self.create_command();
- command.args(&self.get_arguments());
- command.spawn()
- }
-
- fn create_command(&self) -> Command {
- let mut command = Command::new(&self.openvpn_bin);
- command.env_clear()
- .stdin(Stdio::null())
- .stdout(self.get_output_pipe_policy())
- .stderr(self.get_output_pipe_policy());
- command
- }
-
- fn get_output_pipe_policy(&self) -> Stdio {
- if self.pipe_output {
- Stdio::piped()
- } else {
- Stdio::null()
- }
- }
-
- /// Returns all arguments that the subprocess would be spawned with.
- pub fn get_arguments(&self) -> Vec<OsString> {
- let mut args = vec![];
- if let Some(ref config) = self.config {
- args.push(OsString::from("--config"));
- args.push(OsString::from(config.as_os_str()));
- }
- for remote in &self.remotes {
- args.push(OsString::from("--remote"));
- args.push(OsString::from(remote.address()));
- args.push(OsString::from(remote.port().to_string()));
- }
- args
- }
-}
-
-impl fmt::Display for OpenVpnCommand {
- /// Format the program and arguments of an `OpenVpnCommand` for display. Any non-utf8 data is
- /// lossily converted using the utf8 replacement character.
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.write_str(&self.openvpn_bin.to_string_lossy())?;
- for arg in self.get_arguments().iter().map(|arg| arg.to_string_lossy()) {
- write_argument(fmt, &arg)?;
- }
- Ok(())
- }
-}
-
-fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result {
- fmt.write_str(" ")?;
- let quote = arg.contains(char::is_whitespace);
- if quote {
- fmt.write_str("\"")?;
- }
- fmt.write_str(arg)?;
- if quote {
- fmt.write_str("\"")?;
- }
- Ok(())
-}
+use clonablechild::ClonableChild;
impl MonitoredChild for ClonableChild {
@@ -137,64 +29,3 @@ impl MonitoredChild for ClonableChild {
self.stderr()
}
}
-
-impl ChildSpawner for OpenVpnCommand {
- type Child = ClonableChild;
-
- fn spawn(&mut self) -> io::Result<ClonableChild> {
- OpenVpnCommand::spawn(self).map(|child| child.into_clonable())
- }
-}
-
-
-#[cfg(test)]
-mod tests {
- use super::OpenVpnCommand;
- use net::RemoteAddr;
- use std::ffi::OsString;
-
- #[test]
- fn no_arguments() {
- let testee_args = OpenVpnCommand::new("").get_arguments();
- assert_eq!(0, testee_args.len());
- }
-
- #[test]
- fn passes_one_remote() {
- let remote = RemoteAddr::new("example.com", 3333);
-
- let testee_args = OpenVpnCommand::new("").remotes(remote).unwrap().get_arguments();
-
- assert!(testee_args.contains(&OsString::from("example.com")));
- assert!(testee_args.contains(&OsString::from("3333")));
- }
-
- #[test]
- fn passes_two_remotes() {
- let remotes = vec![RemoteAddr::new("127.0.0.1", 998), RemoteAddr::new("fe80::1", 1337)];
-
- let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments();
-
- assert!(testee_args.contains(&OsString::from("127.0.0.1")));
- assert!(testee_args.contains(&OsString::from("998")));
- assert!(testee_args.contains(&OsString::from("fe80::1")));
- assert!(testee_args.contains(&OsString::from("1337")));
- }
-
- #[test]
- fn accepts_str() {
- assert!(OpenVpnCommand::new("").remotes("10.0.0.1:1377").is_ok());
- }
-
- #[test]
- fn accepts_slice_of_str() {
- let remotes = ["10.0.0.1:1337", "127.0.0.1:99"];
-
- let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments();
-
- assert!(testee_args.contains(&OsString::from("10.0.0.1")));
- assert!(testee_args.contains(&OsString::from("1337")));
- assert!(testee_args.contains(&OsString::from("127.0.0.1")));
- assert!(testee_args.contains(&OsString::from("99")));
- }
-}
diff --git a/src/process/openvpn.rs b/src/process/openvpn.rs
new file mode 100644
index 0000000000..65f87064ff
--- /dev/null
+++ b/src/process/openvpn.rs
@@ -0,0 +1,194 @@
+use super::monitor::ChildSpawner;
+
+use clonablechild::{ClonableChild, ChildExt};
+
+use net::{RemoteAddr, ToRemoteAddrs};
+
+use std::ffi::{OsString, OsStr};
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Child, Stdio};
+
+/// An OpenVPN process builder, providing control over the different arguments that the OpenVPN
+/// binary accepts.
+#[derive(Clone)]
+pub struct OpenVpnCommand {
+ openvpn_bin: OsString,
+ config: Option<PathBuf>,
+ remotes: Vec<RemoteAddr>,
+ plugin: Option<(PathBuf, Vec<String>)>,
+ pipe_output: bool,
+}
+
+impl OpenVpnCommand {
+ /// Constructs a new `OpenVpnCommand` for launching OpenVPN processes from the binary at
+ /// `openvpn_bin`.
+ pub fn new<P: AsRef<OsStr>>(openvpn_bin: P) -> Self {
+ OpenVpnCommand {
+ openvpn_bin: OsString::from(openvpn_bin.as_ref()),
+ config: None,
+ remotes: vec![],
+ plugin: None,
+ pipe_output: true,
+ }
+ }
+
+ /// Sets what configuration file will be given to OpenVPN
+ pub fn config<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+ self.config = Some(path.as_ref().to_path_buf());
+ self
+ }
+
+ /// Sets the addresses that OpenVPN will connect to. See OpenVPN documentation for how multiple
+ /// remotes are handled.
+ pub fn remotes<A: ToRemoteAddrs>(&mut self, remotes: A) -> io::Result<&mut Self> {
+ self.remotes = remotes.to_remote_addrs()?.collect();
+ Ok(self)
+ }
+
+ /// Sets a plugin and its arguments that OpenVPN will be started with.
+ pub fn plugin<P: AsRef<Path>>(&mut self, path: P, args: Vec<String>) -> &mut Self {
+ self.plugin = Some((path.as_ref().to_path_buf(), args));
+ self
+ }
+
+ /// If piping the standard streams, stdout and stderr will be available to the parent process.
+ /// This is the default behavior. If you want the equivalence of attaching the child streams to
+ /// /dev/null, invoke this method with false.
+ pub fn pipe_output(&mut self, pipe_output: bool) -> &mut Self {
+ self.pipe_output = pipe_output;
+ self
+ }
+
+ /// Executes the OpenVPN process as a child process, returning a handle to it.
+ pub fn spawn(&self) -> io::Result<Child> {
+ let mut command = self.create_command();
+ let args = self.get_arguments();
+ command.args(&args);
+ command.spawn()
+ }
+
+ fn create_command(&self) -> Command {
+ let mut command = Command::new(&self.openvpn_bin);
+ command.stdin(Stdio::null())
+ .stdout(self.get_output_pipe_policy())
+ .stderr(self.get_output_pipe_policy());
+ command
+ }
+
+ fn get_output_pipe_policy(&self) -> Stdio {
+ if self.pipe_output {
+ Stdio::piped()
+ } else {
+ Stdio::null()
+ }
+ }
+
+ /// Returns all arguments that the subprocess would be spawned with.
+ pub fn get_arguments(&self) -> Vec<OsString> {
+ let mut args = vec![];
+ if let Some(ref config) = self.config {
+ args.push(OsString::from("--config"));
+ args.push(OsString::from(config.as_os_str()));
+ }
+ for remote in &self.remotes {
+ args.push(OsString::from("--remote"));
+ args.push(OsString::from(remote.address()));
+ args.push(OsString::from(remote.port().to_string()));
+ }
+ if let Some((ref path, ref plugin_args)) = self.plugin {
+ args.push(OsString::from("--plugin"));
+ args.push(OsString::from(path));
+ args.extend(plugin_args.iter().map(|arg| OsString::from(arg)));
+ }
+ args
+ }
+}
+
+impl fmt::Display for OpenVpnCommand {
+ /// Format the program and arguments of an `OpenVpnCommand` for display. Any non-utf8 data is
+ /// lossily converted using the utf8 replacement character.
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.write_str(&self.openvpn_bin.to_string_lossy())?;
+ for arg in self.get_arguments().iter().map(|arg| arg.to_string_lossy()) {
+ write_argument(fmt, &arg)?;
+ }
+ Ok(())
+ }
+}
+
+fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result {
+ fmt.write_str(" ")?;
+ let quote = arg.contains(char::is_whitespace);
+ if quote {
+ fmt.write_str("\"")?;
+ }
+ fmt.write_str(arg)?;
+ if quote {
+ fmt.write_str("\"")?;
+ }
+ Ok(())
+}
+
+
+impl ChildSpawner for OpenVpnCommand {
+ type Child = ClonableChild;
+
+ fn spawn(&mut self) -> io::Result<ClonableChild> {
+ OpenVpnCommand::spawn(self).map(|child| child.into_clonable())
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::OpenVpnCommand;
+ use net::RemoteAddr;
+ use std::ffi::OsString;
+
+ #[test]
+ fn no_arguments() {
+ let testee_args = OpenVpnCommand::new("").get_arguments();
+ assert_eq!(0, testee_args.len());
+ }
+
+ #[test]
+ fn passes_one_remote() {
+ let remote = RemoteAddr::new("example.com", 3333);
+
+ let testee_args = OpenVpnCommand::new("").remotes(remote).unwrap().get_arguments();
+
+ assert!(testee_args.contains(&OsString::from("example.com")));
+ assert!(testee_args.contains(&OsString::from("3333")));
+ }
+
+ #[test]
+ fn passes_two_remotes() {
+ let remotes = vec![RemoteAddr::new("127.0.0.1", 998), RemoteAddr::new("fe80::1", 1337)];
+
+ let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments();
+
+ assert!(testee_args.contains(&OsString::from("127.0.0.1")));
+ assert!(testee_args.contains(&OsString::from("998")));
+ assert!(testee_args.contains(&OsString::from("fe80::1")));
+ assert!(testee_args.contains(&OsString::from("1337")));
+ }
+
+ #[test]
+ fn accepts_str() {
+ assert!(OpenVpnCommand::new("").remotes("10.0.0.1:1377").is_ok());
+ }
+
+ #[test]
+ fn accepts_slice_of_str() {
+ let remotes = ["10.0.0.1:1337", "127.0.0.1:99"];
+
+ let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments();
+
+ assert!(testee_args.contains(&OsString::from("10.0.0.1")));
+ assert!(testee_args.contains(&OsString::from("1337")));
+ assert!(testee_args.contains(&OsString::from("127.0.0.1")));
+ assert!(testee_args.contains(&OsString::from("99")));
+ }
+}